com.tangosol.internal.net.ssl.SSLContextDependencies Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of coherence Show documentation
Show all versions of coherence Show documentation
Oracle Coherence Community Edition
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* https://oss.oracle.com/licenses/upl.
*/
package com.tangosol.internal.net.ssl;
import com.oracle.coherence.common.base.Exceptions;
import com.oracle.coherence.common.base.Logger;
import com.oracle.coherence.common.net.SSLSocketProvider;
import com.tangosol.net.ssl.RefreshPolicy;
import com.tangosol.util.Base;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.Provider;
import java.security.SecureRandom;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* The dependencies required for building SSL contexts.
*
* @author Jonathan Knight 2022.08.25
* @since 22.06.2
*/
public class SSLContextDependencies
{
// ----- constructors ---------------------------------------------------
/**
* Create a {@link SSLContextDependencies}.
*
* @param listener a consumer that will be called whenever the ssl context
* dependencies are updated
*/
public SSLContextDependencies(Listener listener)
{
m_listener = listener;
}
/**
* Copy constructor.
*
* @param deps the dependencies to copy
* @param listener the listener for the new dependencies
*/
public SSLContextDependencies(SSLContextDependencies deps, Listener listener)
{
this(listener);
if (deps != null)
{
m_secureRandom = deps.m_secureRandom;
m_aKeyManager = deps.m_aKeyManager;
m_aTrustManagers = deps.m_aTrustManagers;
m_keyManagersBuilder = deps.m_keyManagersBuilder;
m_trustManagersBuilder = deps.m_trustManagersBuilder;
m_deps = deps.m_deps;
m_depsIdMgr = deps.m_depsIdMgr;
m_depsTrustMgr = deps.m_depsTrustMgr;
m_sProtocol = deps.m_sProtocol;
m_provider = deps.m_provider;
m_sProviderName = deps.m_sProviderName;
m_fPeerAuthentication = deps.m_fPeerAuthentication;
m_nRefreshPeriodMillis = deps.m_nRefreshPeriodMillis;
}
}
// ----- SSLContextDependencies methods ---------------------------------
/**
* Initialise the context.
*/
public void init()
{
init(m_aKeyManager, m_aTrustManagers, m_secureRandom);
}
/**
* Initialise the context.
*
* @param keyManagers the array of {@link KeyManager key managers} to use
* @param trustManagers the array of {@link TrustManager trust managers} to use
* @param secureRandom the {@link SecureRandom} to use
*/
protected void init(KeyManager[] keyManagers, TrustManager[] trustManagers, SecureRandom secureRandom)
{
m_aKeyManager = keyManagers;
m_aTrustManagers = trustManagers;
m_secureRandom = secureRandom;
try
{
update();
if (m_nRefreshPeriodMillis > 0)
{
ScheduledExecutorService refreshExecutor = Executors.newSingleThreadScheduledExecutor(SSLContextDependencies::makeRefreshThread);
refreshExecutor.scheduleAtFixedRate(this::onScheduledUpdate, m_nRefreshPeriodMillis, m_nRefreshPeriodMillis, TimeUnit.MILLISECONDS);
}
}
catch (Exception e)
{
throw Exceptions.ensureRuntimeException(e);
}
}
// ----- accessor methods -----------------------------------------------
/**
* Set the dependencies to use for the SSL context.
*
* @param deps the {@link SSLSocketProvider.Dependencies socket provider} dependencies
* @param depsIdMgr the {@link ManagerDependencies key manager dependencies}
* @param depsTrustMgr the {@link ManagerDependencies trust manager dependencies}
*/
public void setDependencies(SSLSocketProvider.Dependencies deps, ManagerDependencies depsIdMgr, ManagerDependencies depsTrustMgr)
{
m_deps = deps;
m_depsIdMgr = depsIdMgr;
m_depsTrustMgr = depsTrustMgr;
}
/**
* Return the {@link SecureRandom} to use.
*
* @return the {@link SecureRandom} to use
*/
public SecureRandom getSecureRandom()
{
return m_secureRandom;
}
/**
* Set the {@link SecureRandom} to use.
*
* @param secureRandom the {@link SecureRandom} to use
*/
public void setSecureRandom(SecureRandom secureRandom)
{
m_secureRandom = secureRandom;
}
/**
* Return the array of {@link KeyManager key managers} to use.
*
* @return the array of {@link KeyManager key managers} to use
*/
public KeyManager[] getKeyManagers()
{
return m_aKeyManager;
}
/**
* Return the array of {@link TrustManager trust managers} to use.
*
* @return the array of {@link TrustManager trust managers} to use
*/
public TrustManager[] getTrustManagers()
{
return m_aTrustManagers;
}
/**
* Return the {@link HostnameVerifier} to use.
*
* @return the {@link HostnameVerifier} to use
*/
public HostnameVerifier getHostnameVerifier()
{
return m_deps.getHostnameVerifier();
}
/**
* Set whether peer authentication is required.
*
* @param fPeerAuthentication {@code true} if peer authentication is required
*/
public void setPeerAuthentication(boolean fPeerAuthentication)
{
m_fPeerAuthentication = fPeerAuthentication;
}
/**
* Return whether peer authentication is required.
*
* @return {@code true} if peer authentication is required
*/
public boolean isPeerAuthentication()
{
return m_fPeerAuthentication;
}
/**
* Set the Java security provider and name
*
* @param provider the {@link Provider} implementation
* @param sProviderName the provider name
*/
public void setProvider(Provider provider, String sProviderName)
{
m_provider = provider;
m_sProviderName = sProviderName;
}
/**
* Return the {@link Provider} implementation.
*
* @return the {@link Provider} implementation
*/
public Provider getProvider()
{
return m_provider;
}
/**
* Return the provider name.
*
* @return the provider name
*/
public String getProviderName()
{
return m_sProviderName;
}
/**
* Set the context refresh period, in milliseconds.
*
* @param nRefreshPeriodInMillis the context refresh period, in milliseconds
*/
public void setRefreshPeriodInMillis(long nRefreshPeriodInMillis)
{
m_nRefreshPeriodMillis = nRefreshPeriodInMillis;
}
/**
* Returns the context refresh period, in milliseconds.
*
* @return the context refresh period, in milliseconds
*/
public long getRefreshPeriodMillis()
{
return m_nRefreshPeriodMillis;
}
/**
* Return the set of enabled SSL cipher suites.
*
* @return the enabled SSL cipher suites, or {@code null} for default
*/
public String[] getEnabledCipherSuites()
{
return m_deps.getEnabledCipherSuites();
}
/**
* Set the security protocol to use, the default is TLS.
*
* @param sProtocol the security protocol to use
*/
public void setProtocol(String sProtocol)
{
m_sProtocol = sProtocol;
}
/**
* Return the security protocol to use, the default is TLS.
*
* @return the security protocol to use
*/
public String getProtocol()
{
if (m_sProtocol == null || m_sProtocol.isEmpty())
{
return SSLSocketProviderDefaultDependencies.DEFAULT_SSL_PROTOCOL;
}
return m_sProtocol;
}
// ----- helper methods -------------------------------------------------
/**
* Ensure the {@link KeyManagersBuilder} field is set.
*
* @return the {@link KeyManagersBuilder}
*/
protected KeyManagersBuilder ensureKeyManagersBuilder()
{
if (m_keyManagersBuilder == null)
{
synchronized (this)
{
if (m_keyManagersBuilder == null)
{
KeyManagersBuilder builder;
if (m_provider instanceof KeyManagersBuilder)
{
builder = (KeyManagersBuilder) m_provider;
}
else
{
builder = new DefaultKeyManagerBuilder();
}
m_keyManagersBuilder = builder;
}
}
}
return m_keyManagersBuilder;
}
/**
* Set the {@link KeyManagersBuilder}.
*
* @param keyManagersBuilder the {@link KeyManagersBuilder}
*/
protected void setKeyManagersBuilder(KeyManagersBuilder keyManagersBuilder)
{
m_keyManagersBuilder = keyManagersBuilder;
}
/**
* Ensure the {@link TrustManagersBuilder} field is set.
*
* @return the {@link TrustManagersBuilder}
*/
protected TrustManagersBuilder ensureTrustManagersBuilder()
{
if (m_trustManagersBuilder == null)
{
synchronized (this)
{
if (m_trustManagersBuilder == null)
{
TrustManagersBuilder builder;
if (m_provider instanceof TrustManagersBuilder)
{
builder = (TrustManagersBuilder) m_provider;
}
else
{
builder = new DefaultTrustManagerBuilder();
}
m_trustManagersBuilder = builder;
}
}
}
return m_trustManagersBuilder;
}
/**
* Set the {@link TrustManagersBuilder}.
*
* @param trustManagersBuilder the {@link TrustManagersBuilder}
*/
protected void setTrustManagersBuilder(TrustManagersBuilder trustManagersBuilder)
{
m_trustManagersBuilder = trustManagersBuilder;
}
/**
* Create a {@link Thread} used to run the refresh.
*
* @param runnable the refresh {@link Runnable}
*
* @return a {@link Thread} used to run the refresh
*/
protected static Thread makeRefreshThread(Runnable runnable)
{
String sName = "SSLContextRefreshThread:" + INSTANCE_COUNT.incrementAndGet();
return Base.makeThread(null, runnable, sName);
}
/**
* Perform a scheduled refresh.
*/
protected void onScheduledUpdate()
{
try
{
RefreshPolicy policy = m_deps.getRefreshPolicy();
if (policy == null || policy.shouldRefresh(m_deps, m_depsIdMgr, m_depsTrustMgr))
{
update();
}
}
catch (Throwable t)
{
Logger.err("Failed to update keystores", t);
}
}
/**
* Update the SSL context.
*
* @throws KeyManagementException if an error occurs
*/
protected synchronized void update() throws KeyManagementException
{
try
{
StringBuilder sbDesc = new StringBuilder();
KeyManagersBuilder keyManagersBuilder = ensureKeyManagersBuilder();
TrustManagersBuilder trustManagersBuilder = ensureTrustManagersBuilder();
if (!keyManagersBuilder.isRefreshable(m_depsIdMgr) && !trustManagersBuilder.isRefreshable(m_depsTrustMgr))
{
// nothing to refresh
return;
}
m_aKeyManager = keyManagersBuilder.buildKeyManagers(m_depsIdMgr, sbDesc);
m_aTrustManagers = trustManagersBuilder.buildTrustManagers(m_depsTrustMgr, sbDesc.append(", "));
m_fPeerAuthentication = m_aTrustManagers != null;
m_deps.setClientAuthenticationRequired(m_fPeerAuthentication);
if (m_listener != null)
{
m_listener.onUpdate(this);
}
logDescription(sbDesc);
}
catch (Throwable e)
{
if (m_listener != null)
{
m_listener.onError(this, e);
}
else
{
throw new KeyManagementException("Could not create SSLContext dependencies", e);
}
}
}
/**
* Log a description of the SSL context.
*
* @param sbDesc the {@link StringBuilder} containing the description
*/
protected void logDescription(StringBuilder sbDesc)
{
if (m_deps.getHostnameVerifier() != null)
{
sbDesc.append(", hostname-verifier=custom");
}
String sAuth = m_aKeyManager == null && m_aTrustManagers == null
? "none"
: m_aKeyManager == null
? "one-way client"
: m_aTrustManagers == null ? "one-way server" : "two-way";
m_deps.setDescription(sbDesc.insert(0, "SSLSocketProvider(auth=" + sAuth + ", ").append(')').toString());
Logger.fine("instantiated SSLSocketProviderDependencies: " + sbDesc);
}
// ----- inner class: Listener ------------------------------------------
/**
* A listener that will be notified of SSL context updates.
*/
public interface Listener
{
/**
* Called when the SSL context has been updated.
*
* @param dependencies the updated dependencies
*
* @throws GeneralSecurityException if an error occurs
*/
void onUpdate(SSLContextDependencies dependencies) throws GeneralSecurityException;
/**
* Called when an error occurred updating the SSL context.
*
* @param dependencies the updated dependencies
*
* @throws KeyManagementException if an error occurs
*/
void onError(SSLContextDependencies dependencies, Throwable t) throws KeyManagementException;
}
// ----- data members ---------------------------------------------------
private volatile SecureRandom m_secureRandom;
private volatile KeyManager[] m_aKeyManager;
private volatile TrustManager[] m_aTrustManagers;
private volatile KeyManagersBuilder m_keyManagersBuilder;
private volatile TrustManagersBuilder m_trustManagersBuilder;
private SSLSocketProvider.Dependencies m_deps;
private ManagerDependencies m_depsIdMgr;
private ManagerDependencies m_depsTrustMgr;
private String m_sProtocol;
private Provider m_provider;
private String m_sProviderName;
private boolean m_fPeerAuthentication;
private long m_nRefreshPeriodMillis;
private static final AtomicLong INSTANCE_COUNT = new AtomicLong(0L);
private final Listener m_listener;
}