blob: f92d815047094016d55eebf215d4970d3fb86b8c [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.util.jsse;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLContextSpi;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import org.apache.camel.util.jsse.FilterParameters.Patterns;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.camel.util.CollectionHelper.collectionAsCommaDelimitedString;
/**
* Represents configuration options that can be applied in the client-side
* or server-side context depending on what they are applied to.
*/
public abstract class BaseSSLContextParameters extends JsseParameters {
protected static final List<String> DEFAULT_CIPHER_SUITES_FILTER_INCLUDE =
Collections.unmodifiableList(Arrays.asList(".*"));
protected static final List<String> DEFAULT_CIPHER_SUITES_FILTER_EXCLUDE =
Collections.unmodifiableList(Arrays.asList(".*_NULL_.*", ".*_anon_.*"));
protected static final List<String> DEFAULT_SECURE_SOCKET_PROTOCOLS_FILTER_INCLUDE =
Collections.unmodifiableList(Arrays.asList(".*"));
private static final Logger LOG = LoggerFactory.getLogger(BaseSSLContextParameters.class);
private static final String LS = System.getProperty("line.separator");
private static final String SSL_ENGINE_CIPHER_SUITE_LOG_MSG = createCipherSuiteLogMessage("SSLEngine");
private static final String SSL_SOCKET_CIPHER_SUITE_LOG_MSG = createCipherSuiteLogMessage("SSLSocket");
private static final String SSL_SERVER_SOCKET_CIPHER_SUITE_LOG_MSG = createCipherSuiteLogMessage("SSLServerSocket");
private static final String SSL_ENGINE_PROTOCOL_LOG_MSG = createProtocolLogMessage("SSLEngine");
private static final String SSL_SOCKET_PROTOCOL_LOG_MSG = createProtocolLogMessage("SSLSocket");
private static final String SSL_SERVER_SOCKET_PROTOCOL_LOG_MSG = createProtocolLogMessage("SSLServerSocket");
/**
* The optional explicitly configured cipher suites for this configuration.
*/
private CipherSuitesParameters cipherSuites;
/**
* The optional cipher suite filter configuration for this configuration.
*/
private FilterParameters cipherSuitesFilter;
/**
* The optional explicitly configured secure socket protocol names for this configuration.
*/
private SecureSocketProtocolsParameters secureSocketProtocols;
/**
* The option secure socket protocol name filter configuration for this configuration.
*/
private FilterParameters secureSocketProtocolsFilter;
/**
* The optional {@link SSLSessionContext} timeout time for {@link javax.net.ssl.SSLSession}s in seconds.
*/
private String sessionTimeout;
/**
* Returns the optional explicitly configured cipher suites for this configuration.
* These options are used in the configuration of {@link SSLEngine},
* {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
* on the context in which they are applied.
* <p/>
* These values override any filters supplied in {@link #setCipherSuitesFilter(FilterParameters)}
*/
public CipherSuitesParameters getCipherSuites() {
return cipherSuites;
}
/**
* Sets the optional explicitly configured cipher suites for this configuration.
* These options are used in the configuration of {@link SSLEngine},
* {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
* on the context in which they are applied.
* <p/>
* These values override any filters supplied in {@link #setCipherSuitesFilter(FilterParameters)}
*
* @param cipherSuites the suite configuration
*/
public void setCipherSuites(CipherSuitesParameters cipherSuites) {
this.cipherSuites = cipherSuites;
}
/**
* Returns the optional cipher suite filter for this configuration.
* These options are used in the configuration of {@link SSLEngine},
* {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
* on the context in which they are applied.
* <p/>
* These values are ignored if {@link #setCipherSuites(CipherSuitesParameters)} is
* called with a non {@code null} argument.
*/
public FilterParameters getCipherSuitesFilter() {
return cipherSuitesFilter;
}
/**
* Sets the optional cipher suite filter for this JSSE configuration.
* These options are used in the configuration of {@link SSLEngine},
* {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
* on the context in which they are applied.
* <p/>
* These values are ignored if {@link #setCipherSuites(CipherSuitesParameters)} is
* called with a non {@code null} argument.
*
* @param cipherSuitesFilter the filter configuration
*/
public void setCipherSuitesFilter(FilterParameters cipherSuitesFilter) {
this.cipherSuitesFilter = cipherSuitesFilter;
}
/**
* Returns the explicitly configured secure socket protocol names for this configuration.
* These options are used in the configuration of {@link SSLEngine},
* {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
* on the context in which they are applied.
* <p/>
* These values override any filters supplied in {@link #setSecureSocketProtocolsFilter(FilterParameters)}
*/
public SecureSocketProtocolsParameters getSecureSocketProtocols() {
return secureSocketProtocols;
}
/**
* Sets the explicitly configured secure socket protocol names for this configuration.
* These options are used in the configuration of {@link SSLEngine},
* {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
* on the context in which they are applied.
* <p/>
* These values override any filters supplied in {@link #setSecureSocketProtocolsFilter(FilterParameters)}
*/
public void setSecureSocketProtocols(SecureSocketProtocolsParameters secureSocketProtocols) {
this.secureSocketProtocols = secureSocketProtocols;
}
/**
* Returns the optional secure socket protocol filter for this configuration.
* These options are used in the configuration of {@link SSLEngine},
* {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
* on the context in which they are applied.
* <p/>
* These values are ignored if {@link #setSecureSocketProtocols(SecureSocketProtocolsParameters)} is
* called with a non-{@code null} argument.
*/
public FilterParameters getSecureSocketProtocolsFilter() {
return secureSocketProtocolsFilter;
}
/**
* Sets the optional secure socket protocol filter for this JSSE configuration.
* These options are used in the configuration of {@link SSLEngine},
* {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
* on the context in which they are applied.
* <p/>
* These values are ignored if {@link #setSecureSocketProtocols(SecureSocketProtocolsParameters)} is
* called with a non-{@code null} argument.
*
* @param secureSocketProtocolsFilter the filter configuration
*/
public void setSecureSocketProtocolsFilter(FilterParameters secureSocketProtocolsFilter) {
this.secureSocketProtocolsFilter = secureSocketProtocolsFilter;
}
/**
* Returns the optional {@link SSLSessionContext} timeout time for {@link javax.net.ssl.SSLSession}s
* in seconds.
*/
public String getSessionTimeout() {
return sessionTimeout;
}
/**
* Sets the optional {@link SSLSessionContext} timeout time for {@link javax.net.ssl.SSLSession}s
* in seconds.
*
* @param sessionTimeout the timeout value or {@code null} to use the default
*/
public void setSessionTimeout(String sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
/**
* Returns a flag indicating if default values should be applied in the event that no other property
* of the instance configures a particular aspect of the entity produced by the instance.
* This flag is used to allow instances of this class to produce a configurer that simply
* passes through the current configuration of a configured entity when the instance of this
* class would otherwise only apply some default configuration.
*
* @see SSLContextClientParameters
* @see SSLContextServerParameters
*/
protected boolean getAllowPassthrough() {
return false;
}
/**
* Configures the actual {@link SSLContext} itself with direct setter calls. This method differs from
* configuration options that are handled by a configurer instance in that the options are part of the
* context itself and are not part of some factory or instance object returned by the context.
*
* @param context the context to configure
*
* @throws GeneralSecurityException if there is an error configuring the context
*/
protected void configureSSLContext(SSLContext context) throws GeneralSecurityException {
LOG.trace("Configuring client and server side SSLContext parameters on SSLContext [{}]...", context);
if (this.getSessionTimeout() != null) {
LOG.debug("Configuring client and server side SSLContext session timeout on SSLContext [{}] to [{}]",
context, this.getSessionTimeout());
this.configureSessionContext(context.getClientSessionContext(), this.getSessionTimeout());
this.configureSessionContext(context.getServerSessionContext(), this.getSessionTimeout());
}
LOG.trace("Configured client and server side SSLContext parameters on SSLContext [{}].", context);
}
protected FilterParameters getDefaultCipherSuitesFilter() {
FilterParameters filter = new FilterParameters();
filter.getInclude().addAll(DEFAULT_CIPHER_SUITES_FILTER_INCLUDE);
filter.getExclude().addAll(DEFAULT_CIPHER_SUITES_FILTER_EXCLUDE);
return filter;
}
protected FilterParameters getDefaultSecureSocketProcotolFilter() {
FilterParameters filter = new FilterParameters();
filter.getInclude().addAll(DEFAULT_SECURE_SOCKET_PROTOCOLS_FILTER_INCLUDE);
return filter;
}
/**
* Returns the list of configurers to apply to an {@link SSLEngine} in order
* to fully configure it in compliance with the provided configuration options.
* The configurers are to be applied in the order in which they appear in the list.
*
* @param context the context that serves as the factory for {@code SSLEngine} instances
*
* @return the needed configurers
*/
protected List<Configurer<SSLEngine>> getSSLEngineConfigurers(SSLContext context) {
final List<String> enabledCipherSuites = this.getCipherSuites() == null
? null : this.parsePropertyValues(this.getCipherSuites().getCipherSuite());
final Patterns enabledCipherSuitePatterns;
final Patterns defaultEnabledCipherSuitePatterns = this.getDefaultCipherSuitesFilter().getPatterns();
if (this.getCipherSuitesFilter() != null) {
enabledCipherSuitePatterns = this.getCipherSuitesFilter().getPatterns();
} else {
enabledCipherSuitePatterns = null;
}
///
final List<String> enabledSecureSocketProtocols = this.getSecureSocketProtocols() == null
? null : this.parsePropertyValues(this.getSecureSocketProtocols().getSecureSocketProtocol());
final Patterns enabledSecureSocketProtocolsPatterns;
final Patterns defaultEnabledSecureSocketProtocolsPatterns =
this.getDefaultSecureSocketProcotolFilter().getPatterns();
if (this.getSecureSocketProtocolsFilter() != null) {
enabledSecureSocketProtocolsPatterns = this.getSecureSocketProtocolsFilter().getPatterns();
} else {
enabledSecureSocketProtocolsPatterns = null;
}
//
final boolean allowPassthrough = getAllowPassthrough();
//////
Configurer<SSLEngine> sslEngineConfigurer = new Configurer<SSLEngine>() {
@Override
public SSLEngine configure(SSLEngine engine) {
Collection<String> filteredCipherSuites = BaseSSLContextParameters.this
.filter(enabledCipherSuites, Arrays.asList(engine.getSSLParameters().getCipherSuites()),
Arrays.asList(engine.getEnabledCipherSuites()),
enabledCipherSuitePatterns, defaultEnabledCipherSuitePatterns,
!allowPassthrough);
if (LOG.isDebugEnabled()) {
LOG.debug(SSL_ENGINE_CIPHER_SUITE_LOG_MSG,
new Object[] {engine,
enabledCipherSuites,
enabledCipherSuitePatterns,
engine.getSSLParameters().getCipherSuites(),
engine.getEnabledCipherSuites(),
defaultEnabledCipherSuitePatterns,
filteredCipherSuites});
}
engine.setEnabledCipherSuites(filteredCipherSuites.toArray(new String[filteredCipherSuites.size()]));
Collection<String> filteredSecureSocketProtocols = BaseSSLContextParameters.this
.filter(enabledSecureSocketProtocols, Arrays.asList(engine.getSSLParameters().getProtocols()),
Arrays.asList(engine.getEnabledProtocols()),
enabledSecureSocketProtocolsPatterns, defaultEnabledSecureSocketProtocolsPatterns,
!allowPassthrough);
if (LOG.isDebugEnabled()) {
LOG.debug(SSL_ENGINE_PROTOCOL_LOG_MSG,
new Object[] {engine,
enabledSecureSocketProtocols,
enabledSecureSocketProtocolsPatterns,
engine.getSSLParameters().getProtocols(),
engine.getEnabledProtocols(),
defaultEnabledSecureSocketProtocolsPatterns,
filteredSecureSocketProtocols});
}
engine.setEnabledProtocols(filteredSecureSocketProtocols.toArray(new String[filteredSecureSocketProtocols.size()]));
return engine;
}
};
List<Configurer<SSLEngine>> sslEngineConfigurers = new LinkedList<Configurer<SSLEngine>>();
sslEngineConfigurers.add(sslEngineConfigurer);
return sslEngineConfigurers;
}
/**
* Returns the list of configurers to apply to an {@link SSLSocketFactory} in order
* to fully configure it in compliance with the provided configuration options.
* The configurers are to be applied in the order in which they appear in the list.
* <p/>
* It is preferred to use {@link #getSSLSocketFactorySSLSocketConfigurers(SSLContext)} instead
* of this method as {@code SSLSocketFactory} does not contain any configuration options that
* are non-proprietary.
*
* @param context the context that serves as the factory for {@code SSLSocketFactory} instances
*
* @return the needed configurers
*
* @see #getSSLSocketFactorySSLSocketConfigurers(SSLContext)
*/
protected List<Configurer<SSLSocketFactory>> getSSLSocketFactoryConfigurers(SSLContext context) {
final List<Configurer<SSLSocket>> sslSocketConfigurers =
this.getSSLSocketFactorySSLSocketConfigurers(context);
Configurer<SSLSocketFactory> sslSocketFactoryConfigurer = new Configurer<SSLSocketFactory>() {
@Override
public SSLSocketFactory configure(SSLSocketFactory factory) {
return new SSLSocketFactoryDecorator(
factory,
sslSocketConfigurers);
}
};
List<Configurer<SSLSocketFactory>> sslSocketFactoryConfigurers =
new LinkedList<Configurer<SSLSocketFactory>>();
sslSocketFactoryConfigurers.add(sslSocketFactoryConfigurer);
return sslSocketFactoryConfigurers;
}
/**
* Returns the list of configurers to apply to an {@link SSLServerSocketFactory} in order
* to fully configure it in compliance with the provided configuration options.
* The configurers are to be applied in the order in which they appear in the list.
* <p/>
* It is preferred to use {@link #getSSLServerSocketFactorySSLServerSocketConfigurers(SSLContext)} instead
* of this method as {@code SSLServerSocketFactory} does not contain any configuration options that
* are non-proprietary.
*
* @param context the context that serves as the factory for {@code SSLServerSocketFactory} instances
*
* @return the needed configurers
*
* @see #getSSLServerSocketFactorySSLServerSocketConfigurers(SSLContext)
*/
protected List<Configurer<SSLServerSocketFactory>> getSSLServerSocketFactoryConfigurers(SSLContext context) {
final List<Configurer<SSLServerSocket>> sslServerSocketConfigurers =
this.getSSLServerSocketFactorySSLServerSocketConfigurers(context);
Configurer<SSLServerSocketFactory> sslServerSocketFactoryConfigurer = new Configurer<SSLServerSocketFactory>() {
@Override
public SSLServerSocketFactory configure(SSLServerSocketFactory factory) {
return new SSLServerSocketFactoryDecorator(
factory,
sslServerSocketConfigurers);
}
};
List<Configurer<SSLServerSocketFactory>> sslServerSocketFactoryConfigurers =
new LinkedList<Configurer<SSLServerSocketFactory>>();
sslServerSocketFactoryConfigurers.add(sslServerSocketFactoryConfigurer);
return sslServerSocketFactoryConfigurers;
}
/**
* Returns the list of configurers to apply to an {@link SSLSocket} in order
* to fully configure it in compliance with the provided configuration
* options. These configurers are intended for sockets produced by a
* {@link SSLSocketFactory}, see
* {@link #getSSLServerSocketFactorySSLServerSocketConfigurers(SSLContext)} for
* configurers related to sockets produced by a
* {@link SSLServerSocketFactory}. The configurers are to be applied in
* the order in which they appear in the list.
*
* @param context the context that serves as the factory for
* {@code SSLSocketFactory} instances
*
* @return the needed configurers
*/
protected List<Configurer<SSLSocket>> getSSLSocketFactorySSLSocketConfigurers(SSLContext context) {
final List<String> enabledCipherSuites = this.getCipherSuites() == null
? null : this.parsePropertyValues(this.getCipherSuites().getCipherSuite());
final Patterns enabledCipherSuitePatterns;
final Patterns defaultEnabledCipherSuitePatterns = this.getDefaultCipherSuitesFilter().getPatterns();
if (this.getCipherSuitesFilter() != null) {
enabledCipherSuitePatterns = this.getCipherSuitesFilter().getPatterns();
} else {
enabledCipherSuitePatterns = null;
}
///
final List<String> enabledSecureSocketProtocols = this.getSecureSocketProtocols() == null
? null : this.parsePropertyValues(this.getSecureSocketProtocols().getSecureSocketProtocol());
final Patterns enabledSecureSocketProtocolsPatterns;
final Patterns defaultEnabledSecureSocketProtocolsPatterns =
this.getDefaultSecureSocketProcotolFilter().getPatterns();
if (this.getSecureSocketProtocolsFilter() != null) {
enabledSecureSocketProtocolsPatterns = this.getSecureSocketProtocolsFilter().getPatterns();
} else {
enabledSecureSocketProtocolsPatterns = null;
}
//
final boolean allowPassthrough = getAllowPassthrough();
//////
Configurer<SSLSocket> sslSocketConfigurer = new Configurer<SSLSocket>() {
@Override
public SSLSocket configure(SSLSocket socket) {
Collection<String> filteredCipherSuites = BaseSSLContextParameters.this
.filter(enabledCipherSuites, Arrays.asList(socket.getSSLParameters().getCipherSuites()),
Arrays.asList(socket.getEnabledCipherSuites()),
enabledCipherSuitePatterns, defaultEnabledCipherSuitePatterns,
!allowPassthrough);
if (LOG.isDebugEnabled()) {
LOG.debug(SSL_SOCKET_CIPHER_SUITE_LOG_MSG,
new Object[] {socket,
enabledCipherSuites,
enabledCipherSuitePatterns,
socket.getSSLParameters().getCipherSuites(),
socket.getEnabledCipherSuites(),
defaultEnabledCipherSuitePatterns,
filteredCipherSuites});
}
socket.setEnabledCipherSuites(filteredCipherSuites.toArray(new String[filteredCipherSuites.size()]));
Collection<String> filteredSecureSocketProtocols = BaseSSLContextParameters.this
.filter(enabledSecureSocketProtocols, Arrays.asList(socket.getSSLParameters().getProtocols()),
Arrays.asList(socket.getEnabledProtocols()),
enabledSecureSocketProtocolsPatterns, defaultEnabledSecureSocketProtocolsPatterns,
!allowPassthrough);
if (LOG.isDebugEnabled()) {
LOG.debug(SSL_SOCKET_PROTOCOL_LOG_MSG,
new Object[] {socket,
enabledSecureSocketProtocols,
enabledSecureSocketProtocolsPatterns,
socket.getSSLParameters().getProtocols(),
socket.getEnabledProtocols(),
defaultEnabledSecureSocketProtocolsPatterns,
filteredSecureSocketProtocols});
}
socket.setEnabledProtocols(filteredSecureSocketProtocols.toArray(new String[filteredSecureSocketProtocols.size()]));
return socket;
}
};
List<Configurer<SSLSocket>> sslSocketConfigurers = new LinkedList<Configurer<SSLSocket>>();
sslSocketConfigurers.add(sslSocketConfigurer);
return sslSocketConfigurers;
}
/**
* Returns the list of configurers to apply to an {@link SSLServerSocket} in order
* to fully configure it in compliance with the provided configuration
* options. These configurers are intended for sockets produced by a
* {@link SSLServerSocketFactory}, see
* {@link #getSSLSocketFactorySSLSocketConfigurers(SSLContext)} for
* configurers related to sockets produced by a
* {@link SSLSocketFactory}. The configurers are to be applied in
* the order in which they appear in the list.
*
* @param context the context that serves as the factory for
* {@code SSLServerSocketFactory} instances
* @return the needed configurers
*/
protected List<Configurer<SSLServerSocket>> getSSLServerSocketFactorySSLServerSocketConfigurers(SSLContext context) {
final List<String> enabledCipherSuites = this.getCipherSuites() == null
? null : this.parsePropertyValues(this.getCipherSuites().getCipherSuite());
final Patterns enabledCipherSuitePatterns;
final Patterns defaultEnabledCipherSuitePatterns = this.getDefaultCipherSuitesFilter().getPatterns();
if (this.getCipherSuitesFilter() != null) {
enabledCipherSuitePatterns = this.getCipherSuitesFilter().getPatterns();
} else {
enabledCipherSuitePatterns = null;
}
///
final List<String> enabledSecureSocketProtocols = this.getSecureSocketProtocols() == null
? null : this.parsePropertyValues(this.getSecureSocketProtocols().getSecureSocketProtocol());
final Patterns enabledSecureSocketProtocolsPatterns;
final Patterns defaultEnabledSecureSocketProtocolsPatterns =
this.getDefaultSecureSocketProcotolFilter().getPatterns();
if (this.getSecureSocketProtocolsFilter() != null) {
enabledSecureSocketProtocolsPatterns = this.getSecureSocketProtocolsFilter().getPatterns();
} else {
enabledSecureSocketProtocolsPatterns = null;
}
//
final boolean allowPassthrough = getAllowPassthrough();
//////
Configurer<SSLServerSocket> sslServerSocketConfigurer = new Configurer<SSLServerSocket>() {
@Override
public SSLServerSocket configure(SSLServerSocket socket) {
Collection<String> filteredCipherSuites = BaseSSLContextParameters.this
.filter(enabledCipherSuites, Arrays.asList(socket.getSupportedCipherSuites()),
Arrays.asList(socket.getEnabledCipherSuites()),
enabledCipherSuitePatterns, defaultEnabledCipherSuitePatterns,
!allowPassthrough);
if (LOG.isDebugEnabled()) {
LOG.debug(SSL_SERVER_SOCKET_CIPHER_SUITE_LOG_MSG,
new Object[] {socket,
enabledCipherSuites,
enabledCipherSuitePatterns,
socket.getSupportedCipherSuites(),
socket.getEnabledCipherSuites(),
defaultEnabledCipherSuitePatterns,
filteredCipherSuites});
}
socket.setEnabledCipherSuites(filteredCipherSuites.toArray(new String[filteredCipherSuites.size()]));
Collection<String> filteredSecureSocketProtocols = BaseSSLContextParameters.this
.filter(enabledSecureSocketProtocols, Arrays.asList(socket.getSupportedProtocols()),
Arrays.asList(socket.getEnabledProtocols()),
enabledSecureSocketProtocolsPatterns, defaultEnabledSecureSocketProtocolsPatterns,
!allowPassthrough);
if (LOG.isDebugEnabled()) {
LOG.debug(SSL_SERVER_SOCKET_PROTOCOL_LOG_MSG,
new Object[] {socket,
enabledSecureSocketProtocols,
enabledSecureSocketProtocolsPatterns,
socket.getSupportedProtocols(),
socket.getEnabledProtocols(),
defaultEnabledSecureSocketProtocolsPatterns,
filteredSecureSocketProtocols});
}
socket.setEnabledProtocols(filteredSecureSocketProtocols.toArray(new String[filteredSecureSocketProtocols.size()]));
return socket;
}
};
List<Configurer<SSLServerSocket>> sslServerSocketConfigurers = new LinkedList<Configurer<SSLServerSocket>>();
sslServerSocketConfigurers.add(sslServerSocketConfigurer);
return sslServerSocketConfigurers;
}
/**
* Configures a {@link SSLSessionContext}, client or server, with the supplied session timeout.
*
* @param sessionContext the context to configure
* @param sessionTimeout the timeout time period
* @throws GeneralSecurityException if {@code sessionContext} is {@code null}
*/
protected void configureSessionContext(
SSLSessionContext sessionContext, String sessionTimeout) throws GeneralSecurityException {
int sessionTimeoutInt = Integer.parseInt(this.parsePropertyValue(sessionTimeout));
if (sessionContext != null) {
sessionContext.setSessionTimeout(sessionTimeoutInt);
} else {
throw new GeneralSecurityException(
"The SSLContext does not support SSLSessionContext, "
+ "but a session timeout is configured. Set sessionTimeout to null "
+ "to avoid this error.");
}
}
/**
* Filters the values in {@code availableValues} returning only the values that
* are explicitly listed in {@code explicitValues} (returns them regardless
* of if they appear in {@code availableValues} or not) if {@code explicitValues} is not
* {@code null} or according to the following rules:
* <ol>
* <li>Match the include patterns in {@code patterns} and don't match the exclude patterns in {@code patterns}
* if patterns is not {@code null}.</li>
* <li>Match the include patterns in {@code defaultPatterns} and don't match the exclude patterns in {@code defaultPatterns}
* if patterns is {@code null} and {@code applyDefaults} is true.</li>
* <li>Are provided in currentValues if if patterns is {@code null} and {@code applyDefaults} is false.</li>
* </ol>
*
* @param explicitValues the optional explicit values to use
* @param availableValues the available values to filter from
* @param patterns the optional patterns to use when {@code explicitValues} is not used
* @param defaultPatterns the required patterns to use when {@code explicitValues} and {@code patterns} are not used
* @param applyDefaults flag indicating whether or not to apply defaults in the event that no explicit values and no
* patterns apply
*
* @return the filtered values
*
* @see #filter(Collection, Collection, List, List)
*/
protected Collection<String> filter(
Collection<String> explicitValues, Collection<String> availableValues,
Collection<String> currentValues, Patterns patterns, Patterns defaultPatterns,
boolean applyDefaults) {
final List<Pattern> enabledIncludePatterns;
final List<Pattern> enabledExcludePatterns;
if (explicitValues == null && patterns == null && !applyDefaults) {
return currentValues;
}
if (patterns != null) {
enabledIncludePatterns = patterns.getIncludes();
enabledExcludePatterns = patterns.getExcludes();
} else {
enabledIncludePatterns = defaultPatterns.getIncludes();
enabledExcludePatterns = defaultPatterns.getExcludes();
}
return this.filter(
explicitValues,
availableValues,
enabledIncludePatterns, enabledExcludePatterns);
}
/**
* Filters the values in {@code availableValues} returning only the values that
* are explicitly listed in {@code explicitValues} (returns them regardless
* of if they appear in {@code availableValues} or not) if {@code explicitValues} is not
* {@code null} or as match the patterns in {@code includePatterns} and do
* not match the patterns in {@code excludePatterns} if {@code explicitValues} is {@code null}.
*
* @param explicitValues the optional explicit values to use
* @param availableValues the available values to filter from if {@code explicitValues} is {@code null}
* @param includePatterns the patterns to use for inclusion filtering, required if {@code explicitValues} is {@code null}
* @param excludePatterns the patterns to use for exclusion filtering, required if {@code explicitValues} is {@code null}
*
* @return the filtered values
*/
protected Collection<String> filter(Collection<String> explicitValues, Collection<String> availableValues,
List<Pattern> includePatterns, List<Pattern> excludePatterns) {
Collection<String> returnValues;
// Explicit list has precedence over filters, even when the list is
// empty.
if (explicitValues != null) {
returnValues = new ArrayList<String>(explicitValues);
} else {
returnValues = new LinkedList<String>();
for (String value : availableValues) {
if (this.matchesOneOf(value, includePatterns)
&& !this.matchesOneOf(value, excludePatterns)) {
returnValues.add(value);
}
}
}
return returnValues;
}
/**
* Returns true if and only if the value is matched by one or more of the supplied patterns.
*
* @param value the value to match
* @param patterns the patterns to try to match against
*/
protected boolean matchesOneOf(String value, List<Pattern> patterns) {
boolean matches = false;
for (Pattern pattern : patterns) {
Matcher matcher = pattern.matcher(value);
if (matcher.matches()) {
matches = true;
break;
}
}
return matches;
}
/**
* Configures a {@code T} based on the related configuration options.
*/
interface Configurer<T> {
/**
* Configures a {@code T} based on the related configuration options.
* The return value from this method may be {@code object} or it
* may be a decorated instance there of. Consequently, any subsequent
* actions on {@code object} must be performed using the returned value.
*
* @param object the object to configure
* @return {@code object} or a decorated instance there of
*/
T configure(T object);
}
/**
* Makes a decorated {@link SSLContext} appear as a normal {@code SSLContext}.
*/
protected static final class SSLContextDecorator extends SSLContext {
public SSLContextDecorator(SSLContextSpiDecorator decorator) {
super(decorator, decorator.getDelegate().getProvider(), decorator.getDelegate().getProtocol());
LOG.debug("SSLContextDecorator [{}] decorating SSLContext [{}].", this, decorator.getDelegate());
}
@Override
public String toString() {
return String.format("SSLContext[hash=%h, provider=%s, protocol=%s, needClientAuth=%s, "
+ "wantClientAuth=%s\n\tdefaultProtocols=%s\n\tdefaultChiperSuites=%s\n\tsupportedProtocols=%s\n\tsupportedChiperSuites=%s\n]",
hashCode(), getProvider(), getProtocol(), getDefaultSSLParameters().getNeedClientAuth(), getDefaultSSLParameters().getWantClientAuth(),
collectionAsCommaDelimitedString(getDefaultSSLParameters().getProtocols()),
collectionAsCommaDelimitedString(getDefaultSSLParameters().getCipherSuites()),
collectionAsCommaDelimitedString(getSupportedSSLParameters().getProtocols()),
collectionAsCommaDelimitedString(getSupportedSSLParameters().getCipherSuites()));
}
}
/**
* Class needed to provide decoration of an existing {@link SSLContext}.
* Since {@code SSLContext} is an abstract class and requires an instance of
* {@link SSLContextSpi}, this class effectively wraps an
* {@code SSLContext} as if it were an {@code SSLContextSpi}, allowing us to
* achieve decoration.
*/
protected static final class SSLContextSpiDecorator extends SSLContextSpi {
private final SSLContext context;
private final List<Configurer<SSLEngine>> sslEngineConfigurers;
private final List<Configurer<SSLSocketFactory>> sslSocketFactoryConfigurers;
private final List<Configurer<SSLServerSocketFactory>> sslServerSocketFactoryConfigurers;
public SSLContextSpiDecorator(SSLContext context,
List<Configurer<SSLEngine>> sslEngineConfigurers,
List<Configurer<SSLSocketFactory>> sslSocketFactoryConfigurers,
List<Configurer<SSLServerSocketFactory>> sslServerSocketFactoryConfigurers) {
this.context = context;
this.sslEngineConfigurers = sslEngineConfigurers;
this.sslSocketFactoryConfigurers = sslSocketFactoryConfigurers;
this.sslServerSocketFactoryConfigurers = sslServerSocketFactoryConfigurers;
}
@Override
protected SSLEngine engineCreateSSLEngine() {
SSLEngine engine = this.context.createSSLEngine();
LOG.debug("SSLEngine [{}] created from SSLContext [{}].", engine, context);
this.configureSSLEngine(engine);
return engine;
}
@Override
protected SSLEngine engineCreateSSLEngine(String peerHost, int peerPort) {
SSLEngine engine = this.context.createSSLEngine(peerHost, peerPort);
LOG.debug("SSLEngine [{}] created from SSLContext [{}].", engine, context);
return this.configureSSLEngine(engine);
}
@Override
protected SSLSessionContext engineGetClientSessionContext() {
return this.context.getClientSessionContext();
}
@Override
protected SSLSessionContext engineGetServerSessionContext() {
return this.context.getServerSessionContext();
}
@Override
protected SSLServerSocketFactory engineGetServerSocketFactory() {
SSLServerSocketFactory factory = this.context.getServerSocketFactory();
LOG.debug("SSLServerSocketFactoryEngine [{}] created from SSLContext [{}].", factory, context);
return this.configureSSLServerSocketFactory(factory);
}
@Override
protected SSLSocketFactory engineGetSocketFactory() {
SSLSocketFactory factory = this.context.getSocketFactory();
LOG.debug("SSLSocketFactory [{}] created from SSLContext [{}].", factory, context);
return this.configureSSLSocketFactory(factory);
}
@Override
protected void engineInit(KeyManager[] km,
TrustManager[] tm,
SecureRandom random) throws KeyManagementException {
this.context.init(km, tm, random);
}
protected SSLContext getDelegate() {
return this.context;
}
/**
* Configures an {@link SSLEngine} based on the configurers in instance.
* The return value from this method may be {@code engine} or it may be
* a decorated instance there of. Consequently, any subsequent actions
* on {@code engine} must be performed using the returned value.
*
* @param engine the engine to configure
* @return {@code engine} or a decorated instance there of
*/
protected SSLEngine configureSSLEngine(SSLEngine engine) {
SSLEngine workingEngine = engine;
for (Configurer<SSLEngine> configurer : this.sslEngineConfigurers) {
workingEngine = configurer.configure(workingEngine);
}
return workingEngine;
}
/**
* Configures an {@link SSLSocketFactory} based on the configurers in
* this instance. The return value from this method may be
* {@code factory} or it may be a decorated instance there of.
* Consequently, any subsequent actions on {@code factory} must be
* performed using the returned value.
*
* @param factory the factory to configure
* @return {@code factory} or a decorated instance there of
*/
protected SSLSocketFactory configureSSLSocketFactory(SSLSocketFactory factory) {
SSLSocketFactory workingFactory = factory;
for (Configurer<SSLSocketFactory> configurer : this.sslSocketFactoryConfigurers) {
workingFactory = configurer.configure(workingFactory);
}
return workingFactory;
}
/**
* Configures an {@link SSLServerSocketFactory} based on the
* configurers in this instance. The return value from this method may be
* {@code factory} or it may be a decorated instance there of.
* Consequently, any subsequent actions on {@code factory} must be
* performed using the returned value.
*
* @param factory the factory to configure
* @return {@code factory} or a decorated instance there of
*/
protected SSLServerSocketFactory configureSSLServerSocketFactory(
SSLServerSocketFactory factory) {
SSLServerSocketFactory workingFactory = factory;
for (Configurer<SSLServerSocketFactory> configurer : this.sslServerSocketFactoryConfigurers) {
workingFactory = configurer.configure(workingFactory);
}
return workingFactory;
}
}
/**
* A decorator that enables the application of configuration options to be
* applied to created sockets even after this factory has been created and
* turned over to client code.
*/
protected static final class SSLServerSocketFactoryDecorator extends SSLServerSocketFactory {
private final SSLServerSocketFactory sslServerSocketFactory;
private final List<Configurer<SSLServerSocket>> sslServerSocketConfigurers;
public SSLServerSocketFactoryDecorator(SSLServerSocketFactory sslServerSocketFactory,
List<Configurer<SSLServerSocket>> sslServerSocketConfigurers) {
this.sslServerSocketFactory = sslServerSocketFactory;
this.sslServerSocketConfigurers = sslServerSocketConfigurers;
}
@Override
public String[] getDefaultCipherSuites() {
return this.sslServerSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return this.sslServerSocketFactory.getSupportedCipherSuites();
}
@Override
public ServerSocket createServerSocket() throws IOException {
return this.configureSocket(this.sslServerSocketFactory.createServerSocket());
}
@Override
public ServerSocket createServerSocket(int port, int backlog, InetAddress ifAddress) throws IOException {
return this.configureSocket(this.sslServerSocketFactory.createServerSocket(port, backlog, ifAddress));
}
@Override
public ServerSocket createServerSocket(int port, int backlog) throws IOException {
return this.configureSocket(this.sslServerSocketFactory.createServerSocket(port, backlog));
}
@Override
public ServerSocket createServerSocket(int port) throws IOException {
return this.configureSocket(this.sslServerSocketFactory.createServerSocket(port));
}
public SSLServerSocketFactory getDelegate() {
return this.sslServerSocketFactory;
}
private ServerSocket configureSocket(ServerSocket s) {
SSLServerSocket workingSocket = (SSLServerSocket) s;
LOG.debug("Created ServerSocket [{}] from SslServerSocketFactory [{}].", s, sslServerSocketFactory);
for (Configurer<SSLServerSocket> configurer : this.sslServerSocketConfigurers) {
workingSocket = configurer.configure(workingSocket);
}
return workingSocket;
}
}
/**
* A decorator that enables the application of configuration options to be
* applied to created sockets even after this factory has been created and
* turned over to client code.
*/
protected static final class SSLSocketFactoryDecorator extends SSLSocketFactory {
private final SSLSocketFactory sslSocketFactory;
private final List<Configurer<SSLSocket>> sslSocketConfigurers;
public SSLSocketFactoryDecorator(SSLSocketFactory sslSocketFactory,
List<Configurer<SSLSocket>> sslSocketConfigurers) {
this.sslSocketFactory = sslSocketFactory;
this.sslSocketConfigurers = sslSocketConfigurers;
}
@Override
public String[] getDefaultCipherSuites() {
return sslSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return sslSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
return configureSocket(sslSocketFactory.createSocket());
}
@Override
public Socket createSocket(Socket s, String host,
int port, boolean autoClose) throws IOException, UnknownHostException {
return configureSocket(sslSocketFactory.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return configureSocket(sslSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port,
InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return configureSocket(sslSocketFactory.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return configureSocket(sslSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port,
InetAddress localAddress, int localPort) throws IOException {
return configureSocket(sslSocketFactory.createSocket(address, port, localAddress, localPort));
}
public SSLSocketFactory getDelegate() {
return this.sslSocketFactory;
}
private Socket configureSocket(Socket s) {
SSLSocket workingSocket = (SSLSocket) s;
LOG.debug("Created Socket [{}] from SocketFactory [{}].", s, sslSocketFactory);
for (Configurer<SSLSocket> configurer : this.sslSocketConfigurers) {
workingSocket = configurer.configure(workingSocket);
}
return workingSocket;
}
}
private static String createCipherSuiteLogMessage(String entityName) {
return "Configuring " + entityName + " [{}] with " + LS
+ "\t explicitly set cipher suites [{}]," + LS
+ "\t cipher suite patterns [{}]," + LS
+ "\t available cipher suites [{}]," + LS
+ "\t currently enabled cipher suites [{}]," + LS
+ "\t and default cipher suite patterns [{}]." + LS
+ "\t Resulting enabled cipher suites are [{}].";
}
private static String createProtocolLogMessage(String entityName) {
return "Configuring " + entityName + " [{}] with " + LS
+ "\t explicitly set protocols [{}]," + LS
+ "\t protocol patterns [{}]," + LS
+ "\t available protocols [{}]," + LS
+ "\t currently enabled protocols [{}]," + LS
+ "\t and default protocol patterns [{}]." + LS
+ "\t Resulting enabled protocols are [{}].";
}
}