| /** |
| * 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 [{}]."; |
| } |
| } |