001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.util.jsse;
018
019import java.io.IOException;
020import java.net.InetAddress;
021import java.net.ServerSocket;
022import java.net.Socket;
023import java.net.UnknownHostException;
024import java.security.GeneralSecurityException;
025import java.security.KeyManagementException;
026import java.security.SecureRandom;
027import java.util.ArrayList;
028import java.util.Arrays;
029import java.util.Collection;
030import java.util.Collections;
031import java.util.LinkedList;
032import java.util.List;
033import java.util.regex.Matcher;
034import java.util.regex.Pattern;
035
036import javax.net.ssl.KeyManager;
037import javax.net.ssl.SNIServerName;
038import javax.net.ssl.SSLContext;
039import javax.net.ssl.SSLContextSpi;
040import javax.net.ssl.SSLEngine;
041import javax.net.ssl.SSLParameters;
042import javax.net.ssl.SSLServerSocket;
043import javax.net.ssl.SSLServerSocketFactory;
044import javax.net.ssl.SSLSessionContext;
045import javax.net.ssl.SSLSocket;
046import javax.net.ssl.SSLSocketFactory;
047import javax.net.ssl.TrustManager;
048
049import org.apache.camel.util.jsse.FilterParameters.Patterns;
050
051import org.slf4j.Logger;
052import org.slf4j.LoggerFactory;
053
054import static org.apache.camel.util.CollectionHelper.collectionAsCommaDelimitedString;
055
056/**
057 * Represents configuration options that can be applied in the client-side
058 * or server-side context depending on what they are applied to.
059 */
060public abstract class BaseSSLContextParameters extends JsseParameters {
061
062    protected static final List<String> DEFAULT_CIPHER_SUITES_FILTER_INCLUDE =
063        Collections.unmodifiableList(Arrays.asList(".*"));
064    
065    protected static final List<String> DEFAULT_CIPHER_SUITES_FILTER_EXCLUDE =
066        Collections.unmodifiableList(Arrays.asList(".*_NULL_.*", ".*_anon_.*", ".*_EXPORT_.*", ".*_DES_.*"));
067    
068    protected static final List<String> DEFAULT_SECURE_SOCKET_PROTOCOLS_FILTER_INCLUDE =
069        Collections.unmodifiableList(Arrays.asList(".*"));
070    
071    protected static final List<String> DEFAULT_SECURE_SOCKET_PROTOCOLS_FILTER_EXCLUDE =
072        Collections.unmodifiableList(Arrays.asList("SSL.*"));
073    
074    private static final Logger LOG = LoggerFactory.getLogger(BaseSSLContextParameters.class);
075    
076    private static final String LS = System.lineSeparator();
077    
078    private static final String SSL_ENGINE_CIPHER_SUITE_LOG_MSG = createCipherSuiteLogMessage("SSLEngine");
079    
080    private static final String SSL_SOCKET_CIPHER_SUITE_LOG_MSG = createCipherSuiteLogMessage("SSLSocket");
081    
082    private static final String SSL_SERVER_SOCKET_CIPHER_SUITE_LOG_MSG = createCipherSuiteLogMessage("SSLServerSocket");
083    
084    private static final String SSL_ENGINE_PROTOCOL_LOG_MSG = createProtocolLogMessage("SSLEngine");
085    
086    private static final String SSL_SOCKET_PROTOCOL_LOG_MSG = createProtocolLogMessage("SSLSocket");
087    
088    private static final String SSL_SERVER_SOCKET_PROTOCOL_LOG_MSG = createProtocolLogMessage("SSLServerSocket");
089    
090    /**
091     * The optional explicitly configured cipher suites for this configuration.
092     */
093    private CipherSuitesParameters cipherSuites;
094    
095    /**
096     * The optional cipher suite filter configuration for this configuration.
097     */
098    private FilterParameters cipherSuitesFilter;
099    
100    /**
101     * The optional explicitly configured secure socket protocol names for this configuration.
102     */
103    private SecureSocketProtocolsParameters secureSocketProtocols;
104    
105    /**
106     * The option secure socket protocol name filter configuration for this configuration.
107     */
108    private FilterParameters secureSocketProtocolsFilter;
109    
110    /**
111     * The optional {@link SSLSessionContext} timeout time for {@link javax.net.ssl.SSLSession}s in seconds.
112     */
113    private String sessionTimeout;
114
115    protected List<SNIServerName> getSNIHostNames() {
116        return Collections.emptyList();
117    }
118
119    /**
120     * Returns the optional explicitly configured cipher suites for this configuration.
121     * These options are used in the configuration of {@link SSLEngine},
122     * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
123     * on the context in which they are applied.
124     * <p/>
125     * These values override any filters supplied in {@link #setCipherSuitesFilter(FilterParameters)}
126     */
127    public CipherSuitesParameters getCipherSuites() {
128        return cipherSuites;
129    }
130
131    /**
132     * Sets the optional explicitly configured cipher suites for this configuration.
133     * These options are used in the configuration of {@link SSLEngine},
134     * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
135     * on the context in which they are applied.
136     * <p/>
137     * These values override any filters supplied in {@link #setCipherSuitesFilter(FilterParameters)}
138     * 
139     * @param cipherSuites the suite configuration
140     */
141    public void setCipherSuites(CipherSuitesParameters cipherSuites) {
142        this.cipherSuites = cipherSuites;
143    }
144
145    /**
146     * Returns the optional cipher suite filter for this configuration.
147     * These options are used in the configuration of {@link SSLEngine},
148     * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
149     * on the context in which they are applied.
150     * <p/>
151     * These values are ignored if {@link #setCipherSuites(CipherSuitesParameters)} is
152     * called with a non {@code null} argument.
153     */
154    public FilterParameters getCipherSuitesFilter() {
155        return cipherSuitesFilter;
156    }
157
158    /**
159     * Sets the optional cipher suite filter for this JSSE configuration.
160     * These options are used in the configuration of {@link SSLEngine},
161     * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
162     * on the context in which they are applied.
163     * <p/>
164     * These values are ignored if {@link #setCipherSuites(CipherSuitesParameters)} is
165     * called with a non {@code null} argument.
166     * 
167     * @param cipherSuitesFilter the filter configuration
168     */
169    public void setCipherSuitesFilter(FilterParameters cipherSuitesFilter) {
170        this.cipherSuitesFilter = cipherSuitesFilter;
171    }
172    
173    /**
174     * Returns the explicitly configured secure socket protocol names for this configuration.
175     * These options are used in the configuration of {@link SSLEngine},
176     * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
177     * on the context in which they are applied.
178     * <p/>
179     * These values override any filters supplied in {@link #setSecureSocketProtocolsFilter(FilterParameters)}
180     */
181    public SecureSocketProtocolsParameters getSecureSocketProtocols() {
182        return secureSocketProtocols;
183    }
184
185    /**
186     * Sets the explicitly configured secure socket protocol names for this configuration.
187     * These options are used in the configuration of {@link SSLEngine},
188     * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
189     * on the context in which they are applied.
190     * <p/>
191     * These values override any filters supplied in {@link #setSecureSocketProtocolsFilter(FilterParameters)}
192     */
193    public void setSecureSocketProtocols(SecureSocketProtocolsParameters secureSocketProtocols) {
194        this.secureSocketProtocols = secureSocketProtocols;
195    }
196    
197    /**
198     * Returns the optional secure socket protocol filter for this configuration.
199     * These options are used in the configuration of {@link SSLEngine},
200     * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
201     * on the context in which they are applied.
202     * <p/>
203     * These values are ignored if {@link #setSecureSocketProtocols(SecureSocketProtocolsParameters)} is
204     * called with a non-{@code null} argument.
205     */
206    public FilterParameters getSecureSocketProtocolsFilter() {
207        return secureSocketProtocolsFilter;
208    }
209
210    /**
211     * Sets the optional secure socket protocol filter for this JSSE configuration.
212     * These options are used in the configuration of {@link SSLEngine},
213     * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending
214     * on the context in which they are applied.
215     * <p/>
216     * These values are ignored if {@link #setSecureSocketProtocols(SecureSocketProtocolsParameters)} is
217     * called with a non-{@code null} argument.
218     * 
219     * @param secureSocketProtocolsFilter the filter configuration
220     */
221    public void setSecureSocketProtocolsFilter(FilterParameters secureSocketProtocolsFilter) {
222        this.secureSocketProtocolsFilter = secureSocketProtocolsFilter;
223    }
224
225    /**
226     * Returns the optional {@link SSLSessionContext} timeout time for {@link javax.net.ssl.SSLSession}s 
227     * in seconds.
228     */
229    public String getSessionTimeout() {
230        return sessionTimeout;
231    }
232
233    /**
234     * Sets the optional {@link SSLSessionContext} timeout time for {@link javax.net.ssl.SSLSession}s
235     * in seconds.
236     *
237     * @param sessionTimeout the timeout value or {@code null} to use the default
238     */
239    public void setSessionTimeout(String sessionTimeout) {
240        this.sessionTimeout = sessionTimeout;
241    }
242    
243    /**
244     * Returns a flag indicating if default values should be applied in the event that no other property
245     * of the instance configures a particular aspect of the entity produced by the instance.
246     * This flag is used to allow instances of this class to produce a configurer that simply
247     * passes through the current configuration of a configured entity when the instance of this
248     * class would otherwise only apply some default configuration.
249     *
250     * @see SSLContextClientParameters
251     * @see SSLContextServerParameters
252     */
253    protected boolean getAllowPassthrough() {
254        return false;
255    }
256    
257    /**
258     * Configures the actual {@link SSLContext} itself with direct setter calls.  This method differs from
259     * configuration options that are handled by a configurer instance in that the options are part of the
260     * context itself and are not part of some factory or instance object returned by the context.
261     * 
262     * @param context the context to configure
263     *
264     * @throws GeneralSecurityException if there is an error configuring the context
265     */
266    protected void configureSSLContext(SSLContext context) throws GeneralSecurityException {
267        LOG.trace("Configuring client and server side SSLContext parameters on SSLContext [{}]...", context);
268
269        if (this.getSessionTimeout() != null) {
270            LOG.debug("Configuring client and server side SSLContext session timeout on SSLContext [{}] to [{}]",
271                      context, this.getSessionTimeout());
272            this.configureSessionContext(context.getClientSessionContext(), this.getSessionTimeout());
273            this.configureSessionContext(context.getServerSessionContext(), this.getSessionTimeout());
274        }
275        
276        LOG.trace("Configured client and server side SSLContext parameters on SSLContext [{}].", context);
277    }
278    
279    protected FilterParameters getDefaultCipherSuitesFilter() {
280        FilterParameters filter = new FilterParameters();
281        
282        filter.getInclude().addAll(DEFAULT_CIPHER_SUITES_FILTER_INCLUDE);
283        filter.getExclude().addAll(DEFAULT_CIPHER_SUITES_FILTER_EXCLUDE);
284        
285        return filter;
286    }
287    
288    protected FilterParameters getDefaultSecureSocketProcotolFilter() {
289        FilterParameters filter = new FilterParameters();
290        
291        filter.getInclude().addAll(DEFAULT_SECURE_SOCKET_PROTOCOLS_FILTER_INCLUDE);
292        filter.getExclude().addAll(DEFAULT_SECURE_SOCKET_PROTOCOLS_FILTER_EXCLUDE);
293        
294        return filter; 
295    }
296        
297    /**
298     * Returns the list of configurers to apply to an {@link SSLEngine} in order
299     * to fully configure it in compliance with the provided configuration options.
300     * The configurers are to be applied in the order in which they appear in the list.
301     *
302     * @param context the context that serves as the factory for {@code SSLEngine} instances
303     * 
304     * @return the needed configurers
305     */
306    protected List<Configurer<SSLEngine>> getSSLEngineConfigurers(SSLContext context) {
307        
308        final List<String> enabledCipherSuites = this.getCipherSuites() == null
309                ? null : this.parsePropertyValues(this.getCipherSuites().getCipherSuite());
310        
311        final Patterns enabledCipherSuitePatterns;
312        final Patterns defaultEnabledCipherSuitePatterns = this.getDefaultCipherSuitesFilter().getPatterns();
313                
314        if (this.getCipherSuitesFilter() != null) {
315            enabledCipherSuitePatterns = this.getCipherSuitesFilter().getPatterns();
316        } else {
317            enabledCipherSuitePatterns = null;
318        }
319        
320        ///
321        
322        final List<String> enabledSecureSocketProtocols = this.getSecureSocketProtocols() == null
323                ? null : this.parsePropertyValues(this.getSecureSocketProtocols().getSecureSocketProtocol());
324        
325        final Patterns enabledSecureSocketProtocolsPatterns;
326        final Patterns defaultEnabledSecureSocketProtocolsPatterns = 
327            this.getDefaultSecureSocketProcotolFilter().getPatterns();
328        
329        if (this.getSecureSocketProtocolsFilter() != null) {
330            enabledSecureSocketProtocolsPatterns = this.getSecureSocketProtocolsFilter().getPatterns();
331        } else {
332            enabledSecureSocketProtocolsPatterns = null;
333        }
334        
335        //
336        
337        final boolean allowPassthrough = getAllowPassthrough();
338        
339        //////
340        
341        Configurer<SSLEngine> sslEngineConfigurer = new Configurer<SSLEngine>() {
342            
343            @Override
344            public SSLEngine configure(SSLEngine engine) {
345                
346                Collection<String> filteredCipherSuites = BaseSSLContextParameters.this
347                    .filter(enabledCipherSuites, Arrays.asList(engine.getSSLParameters().getCipherSuites()),
348                            Arrays.asList(engine.getEnabledCipherSuites()),
349                            enabledCipherSuitePatterns, defaultEnabledCipherSuitePatterns,
350                            !allowPassthrough);
351
352                if (LOG.isDebugEnabled()) {
353                    LOG.debug(SSL_ENGINE_CIPHER_SUITE_LOG_MSG,
354                            new Object[] {engine,
355                                          enabledCipherSuites,
356                                          enabledCipherSuitePatterns, 
357                                          engine.getSSLParameters().getCipherSuites(),
358                                          engine.getEnabledCipherSuites(),
359                                          defaultEnabledCipherSuitePatterns,
360                                          filteredCipherSuites});
361                }
362                
363                engine.setEnabledCipherSuites(filteredCipherSuites.toArray(new String[filteredCipherSuites.size()]));
364
365                Collection<String> filteredSecureSocketProtocols = BaseSSLContextParameters.this
366                    .filter(enabledSecureSocketProtocols, Arrays.asList(engine.getSSLParameters().getProtocols()),
367                            Arrays.asList(engine.getEnabledProtocols()),
368                            enabledSecureSocketProtocolsPatterns, defaultEnabledSecureSocketProtocolsPatterns,
369                            !allowPassthrough);
370                
371                if (LOG.isDebugEnabled()) {
372                    LOG.debug(SSL_ENGINE_PROTOCOL_LOG_MSG,
373                            new Object[] {engine,
374                                          enabledSecureSocketProtocols,
375                                          enabledSecureSocketProtocolsPatterns, 
376                                          engine.getSSLParameters().getProtocols(),
377                                          engine.getEnabledProtocols(),
378                                          defaultEnabledSecureSocketProtocolsPatterns,
379                                          filteredSecureSocketProtocols});
380                }
381                
382                engine.setEnabledProtocols(filteredSecureSocketProtocols.toArray(new String[filteredSecureSocketProtocols.size()]));
383                
384                return engine;
385            }
386        };
387        
388        List<Configurer<SSLEngine>> sslEngineConfigurers = new LinkedList<>();
389        sslEngineConfigurers.add(sslEngineConfigurer);
390        
391        return sslEngineConfigurers;
392    }
393    
394    /**
395     * Returns the list of configurers to apply to an {@link SSLSocketFactory} in order
396     * to fully configure it in compliance with the provided configuration options.
397     * The configurers are to be applied in the order in which they appear in the list.
398     * <p/>
399     * It is preferred to use {@link #getSSLSocketFactorySSLSocketConfigurers(SSLContext)} instead
400     * of this method as {@code SSLSocketFactory} does not contain any configuration options that
401     * are non-proprietary.
402     *
403     * @param context the context that serves as the factory for {@code SSLSocketFactory} instances
404     * 
405     * @return the needed configurers
406     * 
407     * @see #getSSLSocketFactorySSLSocketConfigurers(SSLContext)
408     */
409    protected List<Configurer<SSLSocketFactory>> getSSLSocketFactoryConfigurers(SSLContext context) {
410        
411        final List<Configurer<SSLSocket>> sslSocketConfigurers = 
412            this.getSSLSocketFactorySSLSocketConfigurers(context);
413        
414        Configurer<SSLSocketFactory> sslSocketFactoryConfigurer = new Configurer<SSLSocketFactory>() {
415            
416            @Override
417            public SSLSocketFactory configure(SSLSocketFactory factory) {
418                return new SSLSocketFactoryDecorator(
419                                              factory, 
420                                              sslSocketConfigurers);
421            }
422        };
423        
424
425        List<Configurer<SSLSocketFactory>> sslSocketFactoryConfigurers = 
426            new LinkedList<>();
427        sslSocketFactoryConfigurers.add(sslSocketFactoryConfigurer);
428        
429        return sslSocketFactoryConfigurers;
430    }
431    
432    /**
433     * Returns the list of configurers to apply to an {@link SSLServerSocketFactory} in order
434     * to fully configure it in compliance with the provided configuration options.
435     * The configurers are to be applied in the order in which they appear in the list.
436     * <p/>
437     * It is preferred to use {@link #getSSLServerSocketFactorySSLServerSocketConfigurers(SSLContext)} instead
438     * of this method as {@code SSLServerSocketFactory} does not contain any configuration options that
439     * are non-proprietary.
440     *
441     * @param context the context that serves as the factory for {@code SSLServerSocketFactory} instances
442     * 
443     * @return the needed configurers
444     * 
445     * @see #getSSLServerSocketFactorySSLServerSocketConfigurers(SSLContext)
446     */
447    protected List<Configurer<SSLServerSocketFactory>> getSSLServerSocketFactoryConfigurers(SSLContext context) {
448        
449        final List<Configurer<SSLServerSocket>> sslServerSocketConfigurers = 
450            this.getSSLServerSocketFactorySSLServerSocketConfigurers(context);
451        
452        Configurer<SSLServerSocketFactory> sslServerSocketFactoryConfigurer = new Configurer<SSLServerSocketFactory>() {
453            
454            @Override
455            public SSLServerSocketFactory configure(SSLServerSocketFactory factory) {
456                return new SSLServerSocketFactoryDecorator(
457                                              factory, 
458                                              sslServerSocketConfigurers);
459            }
460        };
461        
462
463        List<Configurer<SSLServerSocketFactory>> sslServerSocketFactoryConfigurers = 
464            new LinkedList<>();
465        sslServerSocketFactoryConfigurers.add(sslServerSocketFactoryConfigurer);
466        
467        return sslServerSocketFactoryConfigurers;
468    }
469    
470    /**
471     * Returns the list of configurers to apply to an {@link SSLSocket} in order
472     * to fully configure it in compliance with the provided configuration
473     * options. These configurers are intended for sockets produced by a
474     * {@link SSLSocketFactory}, see
475     * {@link #getSSLServerSocketFactorySSLServerSocketConfigurers(SSLContext)} for
476     * configurers related to sockets produced by a
477     * {@link SSLServerSocketFactory}. The configurers are to be applied in
478     * the order in which they appear in the list.
479     * 
480     * @param context the context that serves as the factory for
481     *            {@code SSLSocketFactory} instances
482     *
483     * @return the needed configurers
484     */
485    protected List<Configurer<SSLSocket>> getSSLSocketFactorySSLSocketConfigurers(SSLContext context) {
486        final List<String> enabledCipherSuites = this.getCipherSuites() == null
487                ? null : this.parsePropertyValues(this.getCipherSuites().getCipherSuite());
488
489        final Patterns enabledCipherSuitePatterns;
490        final Patterns defaultEnabledCipherSuitePatterns = this.getDefaultCipherSuitesFilter().getPatterns();
491                
492        if (this.getCipherSuitesFilter() != null) {
493            enabledCipherSuitePatterns = this.getCipherSuitesFilter().getPatterns();
494        } else {
495            enabledCipherSuitePatterns = null;
496        }
497        
498        ///
499        
500        final List<String> enabledSecureSocketProtocols = this.getSecureSocketProtocols() == null
501                ? null : this.parsePropertyValues(this.getSecureSocketProtocols().getSecureSocketProtocol());
502        
503        final Patterns enabledSecureSocketProtocolsPatterns;
504        final Patterns defaultEnabledSecureSocketProtocolsPatterns = 
505            this.getDefaultSecureSocketProcotolFilter().getPatterns();
506        
507        if (this.getSecureSocketProtocolsFilter() != null) {
508            enabledSecureSocketProtocolsPatterns = this.getSecureSocketProtocolsFilter().getPatterns();
509        } else {
510            enabledSecureSocketProtocolsPatterns = null;
511        }
512        
513        //
514        
515        final boolean allowPassthrough = getAllowPassthrough();
516        
517        //////
518        
519        Configurer<SSLSocket> sslSocketConfigurer = new Configurer<SSLSocket>() {
520            
521            @Override
522            public SSLSocket configure(SSLSocket socket) {
523
524                if (!getSNIHostNames().isEmpty()) {
525                    SSLParameters sslParameters = socket.getSSLParameters();
526                    sslParameters.setServerNames(getSNIHostNames());
527                    socket.setSSLParameters(sslParameters);
528                }
529
530                Collection<String> filteredCipherSuites = BaseSSLContextParameters.this
531                    .filter(enabledCipherSuites, Arrays.asList(socket.getSSLParameters().getCipherSuites()),
532                            Arrays.asList(socket.getEnabledCipherSuites()),
533                            enabledCipherSuitePatterns, defaultEnabledCipherSuitePatterns,
534                            !allowPassthrough);
535                if (LOG.isDebugEnabled()) {
536                    LOG.debug(SSL_SOCKET_CIPHER_SUITE_LOG_MSG,
537                            new Object[] {socket,
538                                          enabledCipherSuites,
539                                          enabledCipherSuitePatterns, 
540                                          socket.getSSLParameters().getCipherSuites(),
541                                          socket.getEnabledCipherSuites(),
542                                          defaultEnabledCipherSuitePatterns,
543                                          filteredCipherSuites});
544                }
545                 
546                socket.setEnabledCipherSuites(filteredCipherSuites.toArray(new String[filteredCipherSuites.size()]));
547        
548                Collection<String> filteredSecureSocketProtocols = BaseSSLContextParameters.this
549                    .filter(enabledSecureSocketProtocols, Arrays.asList(socket.getSSLParameters().getProtocols()),
550                            Arrays.asList(socket.getEnabledProtocols()),
551                            enabledSecureSocketProtocolsPatterns, defaultEnabledSecureSocketProtocolsPatterns,
552                            !allowPassthrough);
553
554                if (LOG.isDebugEnabled()) {
555                    LOG.debug(SSL_SOCKET_PROTOCOL_LOG_MSG,
556                            new Object[] {socket,
557                                          enabledSecureSocketProtocols,
558                                          enabledSecureSocketProtocolsPatterns, 
559                                          socket.getSSLParameters().getProtocols(),
560                                          socket.getEnabledProtocols(),
561                                          defaultEnabledSecureSocketProtocolsPatterns,
562                                          filteredSecureSocketProtocols});
563                }
564                
565                socket.setEnabledProtocols(filteredSecureSocketProtocols.toArray(new String[filteredSecureSocketProtocols.size()]));
566                return socket;
567            }
568        };
569        
570        List<Configurer<SSLSocket>> sslSocketConfigurers = new LinkedList<>();
571        sslSocketConfigurers.add(sslSocketConfigurer);
572        
573        return sslSocketConfigurers;
574    }
575    
576    /**
577     * Returns the list of configurers to apply to an {@link SSLServerSocket} in order
578     * to fully configure it in compliance with the provided configuration
579     * options. These configurers are intended for sockets produced by a
580     * {@link SSLServerSocketFactory}, see
581     * {@link #getSSLSocketFactorySSLSocketConfigurers(SSLContext)} for
582     * configurers related to sockets produced by a
583     * {@link SSLSocketFactory}. The configurers are to be applied in
584     * the order in which they appear in the list.
585     * 
586     * @param context the context that serves as the factory for
587     *            {@code SSLServerSocketFactory} instances
588     * @return the needed configurers
589     */
590    protected List<Configurer<SSLServerSocket>> getSSLServerSocketFactorySSLServerSocketConfigurers(SSLContext context) {
591        final List<String> enabledCipherSuites = this.getCipherSuites() == null
592                ? null : this.parsePropertyValues(this.getCipherSuites().getCipherSuite());
593        
594        final Patterns enabledCipherSuitePatterns;
595        final Patterns defaultEnabledCipherSuitePatterns = this.getDefaultCipherSuitesFilter().getPatterns();
596                
597        if (this.getCipherSuitesFilter() != null) {
598            enabledCipherSuitePatterns = this.getCipherSuitesFilter().getPatterns();
599        } else {
600            enabledCipherSuitePatterns = null;
601        }
602        
603        ///
604        
605        final List<String> enabledSecureSocketProtocols = this.getSecureSocketProtocols() == null
606                ? null : this.parsePropertyValues(this.getSecureSocketProtocols().getSecureSocketProtocol());
607        
608        final Patterns enabledSecureSocketProtocolsPatterns;
609        final Patterns defaultEnabledSecureSocketProtocolsPatterns = 
610            this.getDefaultSecureSocketProcotolFilter().getPatterns();
611        
612        if (this.getSecureSocketProtocolsFilter() != null) {
613            enabledSecureSocketProtocolsPatterns = this.getSecureSocketProtocolsFilter().getPatterns();
614        } else {
615            enabledSecureSocketProtocolsPatterns = null;
616        }
617        
618        //
619        
620        final boolean allowPassthrough = getAllowPassthrough();
621        
622        //////
623        
624        Configurer<SSLServerSocket> sslServerSocketConfigurer = new Configurer<SSLServerSocket>() {
625            
626            @Override
627            public SSLServerSocket configure(SSLServerSocket socket) {
628                
629                Collection<String> filteredCipherSuites = BaseSSLContextParameters.this
630                    .filter(enabledCipherSuites, Arrays.asList(socket.getSupportedCipherSuites()),
631                            Arrays.asList(socket.getEnabledCipherSuites()),
632                            enabledCipherSuitePatterns, defaultEnabledCipherSuitePatterns,
633                            !allowPassthrough);
634                 
635                if (LOG.isDebugEnabled()) {
636                    LOG.debug(SSL_SERVER_SOCKET_CIPHER_SUITE_LOG_MSG,
637                            new Object[] {socket,
638                                          enabledCipherSuites,
639                                          enabledCipherSuitePatterns, 
640                                          socket.getSupportedCipherSuites(),
641                                          socket.getEnabledCipherSuites(),
642                                          defaultEnabledCipherSuitePatterns,
643                                          filteredCipherSuites});
644                }
645                
646                socket.setEnabledCipherSuites(filteredCipherSuites.toArray(new String[filteredCipherSuites.size()]));
647        
648                Collection<String> filteredSecureSocketProtocols = BaseSSLContextParameters.this
649                    .filter(enabledSecureSocketProtocols, Arrays.asList(socket.getSupportedProtocols()),
650                            Arrays.asList(socket.getEnabledProtocols()),
651                            enabledSecureSocketProtocolsPatterns, defaultEnabledSecureSocketProtocolsPatterns,
652                            !allowPassthrough);
653
654                if (LOG.isDebugEnabled()) {
655                    LOG.debug(SSL_SERVER_SOCKET_PROTOCOL_LOG_MSG,
656                            new Object[] {socket,
657                                          enabledSecureSocketProtocols,
658                                          enabledSecureSocketProtocolsPatterns, 
659                                          socket.getSupportedProtocols(),
660                                          socket.getEnabledProtocols(),
661                                          defaultEnabledSecureSocketProtocolsPatterns,
662                                          filteredSecureSocketProtocols});
663                }
664                
665                socket.setEnabledProtocols(filteredSecureSocketProtocols.toArray(new String[filteredSecureSocketProtocols.size()]));
666                return socket;
667            }
668        };
669        
670        List<Configurer<SSLServerSocket>> sslServerSocketConfigurers = new LinkedList<>();
671        sslServerSocketConfigurers.add(sslServerSocketConfigurer);
672        
673        return sslServerSocketConfigurers;
674    }
675    
676    /**
677     * Configures a {@link SSLSessionContext}, client or server, with the supplied session timeout.
678     *
679     * @param sessionContext the context to configure
680     * @param sessionTimeout the timeout time period
681     * @throws GeneralSecurityException if {@code sessionContext} is {@code null}
682     */
683    protected void configureSessionContext(
684        SSLSessionContext sessionContext, String sessionTimeout) throws GeneralSecurityException {
685        
686        int sessionTimeoutInt = Integer.parseInt(this.parsePropertyValue(sessionTimeout));
687        
688        if (sessionContext != null) {
689            sessionContext.setSessionTimeout(sessionTimeoutInt);
690        } else {
691            throw new GeneralSecurityException(
692                    "The SSLContext does not support SSLSessionContext, "
693                            + "but a session timeout is configured. Set sessionTimeout to null "
694                            + "to avoid this error.");
695        }
696    }
697    
698    /**
699     * Filters the values in {@code availableValues} returning only the values that
700     * are explicitly listed in {@code explicitValues} (returns them regardless
701     * of if they appear in {@code availableValues} or not) if {@code explicitValues} is not
702     * {@code null} or according to the following rules:
703     * <ol>
704     * <li>Match the include patterns in {@code patterns} and don't match the exclude patterns in {@code patterns}
705     * if patterns is not {@code null}.</li>
706     * <li>Match the include patterns in {@code defaultPatterns} and don't match the exclude patterns in {@code defaultPatterns}
707     * if patterns is {@code null} and {@code applyDefaults} is true.</li>
708     * <li>Are provided in currentValues if if patterns is {@code null} and {@code applyDefaults} is false.</li>
709     * </ol>
710     * 
711     * @param explicitValues the optional explicit values to use
712     * @param availableValues the available values to filter from
713     * @param patterns the optional patterns to use when {@code explicitValues} is not used
714     * @param defaultPatterns the required patterns to use when {@code explicitValues} and {@code patterns} are not used
715     * @param applyDefaults flag indicating whether or not to apply defaults in the event that no explicit values and no
716     *              patterns apply
717     * 
718     * @return the filtered values
719     *
720     * @see #filter(Collection, Collection, List, List)
721     */
722    protected Collection<String> filter(
723            Collection<String> explicitValues, Collection<String> availableValues, 
724            Collection<String> currentValues, Patterns patterns, Patterns defaultPatterns,
725            boolean applyDefaults) {
726
727        final List<Pattern> enabledIncludePatterns;
728        final List<Pattern> enabledExcludePatterns;
729
730        if (explicitValues == null && patterns == null && !applyDefaults) {
731            return currentValues;
732        }
733        
734        if (patterns != null) {
735            enabledIncludePatterns = patterns.getIncludes();
736            enabledExcludePatterns = patterns.getExcludes();
737        } else {
738            enabledIncludePatterns = defaultPatterns.getIncludes();
739            enabledExcludePatterns = defaultPatterns.getExcludes();
740        }
741
742        return this.filter(
743                explicitValues,
744                availableValues,
745                enabledIncludePatterns, enabledExcludePatterns);
746    }
747    
748    /**
749     * Filters the values in {@code availableValues} returning only the values that
750     * are explicitly listed in {@code explicitValues} (returns them regardless
751     * of if they appear in {@code availableValues} or not) if {@code explicitValues} is not
752     * {@code null} or as match the patterns in {@code includePatterns} and do
753     * not match the patterns in {@code excludePatterns} if {@code explicitValues} is {@code null}.
754     * 
755     * @param explicitValues the optional explicit values to use
756     * @param availableValues the available values to filter from if {@code explicitValues} is {@code null}
757     * @param includePatterns the patterns to use for inclusion filtering, required if {@code explicitValues} is {@code null}
758     * @param excludePatterns the patterns to use for exclusion filtering, required if {@code explicitValues} is {@code null}
759     *
760     * @return the filtered values
761     */
762    protected Collection<String> filter(Collection<String> explicitValues, Collection<String> availableValues, 
763                                        List<Pattern> includePatterns, List<Pattern> excludePatterns) {
764        Collection<String> returnValues;
765
766        // Explicit list has precedence over filters, even when the list is
767        // empty.
768        if (explicitValues != null) {
769            returnValues = new ArrayList<>(explicitValues);
770        } else {
771            returnValues = new LinkedList<>();
772            
773            for (String value : availableValues) {
774                if (this.matchesOneOf(value, includePatterns)
775                    && !this.matchesOneOf(value, excludePatterns)) {
776                    returnValues.add(value);
777                }
778            }
779        }
780
781        return returnValues;
782    }
783    
784    /**
785     * Returns true if and only if the value is matched by one or more of the supplied patterns.
786     *
787     * @param value the value to match
788     * @param patterns the patterns to try to match against
789     */
790    protected boolean matchesOneOf(String value, List<Pattern> patterns) {
791        boolean matches = false;
792        
793        for (Pattern pattern : patterns) {
794            Matcher matcher = pattern.matcher(value);
795            if (matcher.matches()) {
796                matches = true;
797                break;
798            }
799        }
800        
801        return matches;
802    }
803    
804    /**
805     * Configures a {@code T} based on the related configuration options.
806     */
807    interface Configurer<T> {
808
809        /**
810         * Configures a {@code T} based on the related configuration options.
811         * The return value from this method may be {@code object} or it
812         * may be a decorated instance there of. Consequently, any subsequent
813         * actions on {@code object} must be performed using the returned value.
814         *
815         * @param object the object to configure
816         * @return {@code object} or a decorated instance there of
817         */
818        T configure(T object);
819    }
820    
821    /**
822     * Makes a decorated {@link SSLContext} appear as a normal {@code SSLContext}.
823     */
824    protected static final class SSLContextDecorator extends SSLContext {
825
826        public SSLContextDecorator(SSLContextSpiDecorator decorator) {
827            super(decorator, decorator.getDelegate().getProvider(), decorator.getDelegate().getProtocol());
828            LOG.debug("SSLContextDecorator [{}] decorating SSLContext [{}].", this, decorator.getDelegate());
829        }
830
831        @Override
832        public String toString() {
833            return String.format("SSLContext[hash=%h, provider=%s, protocol=%s, needClientAuth=%s, " 
834                + "wantClientAuth=%s\n\tdefaultProtocols=%s\n\tdefaultChiperSuites=%s\n\tsupportedProtocols=%s\n\tsupportedChiperSuites=%s\n]",
835                hashCode(), getProvider(), getProtocol(), getDefaultSSLParameters().getNeedClientAuth(), getDefaultSSLParameters().getWantClientAuth(),
836                collectionAsCommaDelimitedString(getDefaultSSLParameters().getProtocols()),
837                collectionAsCommaDelimitedString(getDefaultSSLParameters().getCipherSuites()),
838                collectionAsCommaDelimitedString(getSupportedSSLParameters().getProtocols()),
839                collectionAsCommaDelimitedString(getSupportedSSLParameters().getCipherSuites()));
840        }
841    }
842    
843    /**
844     * Class needed to provide decoration of an existing {@link SSLContext}.
845     * Since {@code SSLContext} is an abstract class and requires an instance of
846     * {@link SSLContextSpi}, this class effectively wraps an
847     * {@code SSLContext} as if it were an {@code SSLContextSpi}, allowing us to
848     * achieve decoration.
849     */
850    protected static final class SSLContextSpiDecorator extends SSLContextSpi {
851        
852        private final SSLContext context;
853        
854        private final List<Configurer<SSLEngine>> sslEngineConfigurers;
855        
856        private final List<Configurer<SSLSocketFactory>> sslSocketFactoryConfigurers;
857        
858        private final List<Configurer<SSLServerSocketFactory>> sslServerSocketFactoryConfigurers;
859        
860        public SSLContextSpiDecorator(SSLContext context,
861                List<Configurer<SSLEngine>> sslEngineConfigurers,
862                List<Configurer<SSLSocketFactory>> sslSocketFactoryConfigurers,
863                List<Configurer<SSLServerSocketFactory>> sslServerSocketFactoryConfigurers) {
864            this.context = context;
865            this.sslEngineConfigurers = sslEngineConfigurers;
866            this.sslSocketFactoryConfigurers = sslSocketFactoryConfigurers;
867            this.sslServerSocketFactoryConfigurers = sslServerSocketFactoryConfigurers;
868        }
869
870        @Override
871        protected SSLEngine engineCreateSSLEngine() {
872            SSLEngine engine = this.context.createSSLEngine();
873            LOG.debug("SSLEngine [{}] created from SSLContext [{}].", engine, context);
874            this.configureSSLEngine(engine);
875            return engine;
876        }
877
878        @Override
879        protected SSLEngine engineCreateSSLEngine(String peerHost, int peerPort) {
880            SSLEngine engine = this.context.createSSLEngine(peerHost, peerPort);
881            LOG.debug("SSLEngine [{}] created from SSLContext [{}].", engine, context);
882            return this.configureSSLEngine(engine);
883        }
884
885        @Override
886        protected SSLSessionContext engineGetClientSessionContext() {
887            return this.context.getClientSessionContext();
888        }
889
890        @Override
891        protected SSLSessionContext engineGetServerSessionContext() {
892            return this.context.getServerSessionContext();
893        }
894
895        @Override
896        protected SSLServerSocketFactory engineGetServerSocketFactory() {
897            SSLServerSocketFactory factory = this.context.getServerSocketFactory();
898            LOG.debug("SSLServerSocketFactoryEngine [{}] created from SSLContext [{}].", factory, context);
899            return this.configureSSLServerSocketFactory(factory);
900        }
901
902        @Override
903        protected SSLSocketFactory engineGetSocketFactory() {
904            SSLSocketFactory factory = this.context.getSocketFactory();
905            LOG.debug("SSLSocketFactory [{}] created from SSLContext [{}].", factory, context);
906            return this.configureSSLSocketFactory(factory);
907        }
908
909        @Override
910        protected void engineInit(KeyManager[] km,
911                                  TrustManager[] tm, 
912                                  SecureRandom random) throws KeyManagementException {
913            this.context.init(km, tm, random);
914        }
915        
916        protected SSLContext getDelegate() {
917            return this.context;
918        }
919
920        /**
921         * Configures an {@link SSLEngine} based on the configurers in instance.
922         * The return value from this method may be {@code engine} or it may be
923         * a decorated instance there of. Consequently, any subsequent actions
924         * on {@code engine} must be performed using the returned value.
925         * 
926         * @param engine the engine to configure
927         * @return {@code engine} or a decorated instance there of
928         */
929        protected SSLEngine configureSSLEngine(SSLEngine engine) {
930            SSLEngine workingEngine = engine;
931            
932            for (Configurer<SSLEngine> configurer : this.sslEngineConfigurers) {
933                workingEngine = configurer.configure(workingEngine);
934            }
935            
936            return workingEngine;
937        }
938        
939        /**
940         * Configures an {@link SSLSocketFactory} based on the configurers in
941         * this instance. The return value from this method may be
942         * {@code factory} or it may be a decorated instance there of.
943         * Consequently, any subsequent actions on {@code factory} must be
944         * performed using the returned value.
945         * 
946         * @param factory the factory to configure
947         * @return {@code factory} or a decorated instance there of
948         */
949        protected SSLSocketFactory configureSSLSocketFactory(SSLSocketFactory factory) {
950            SSLSocketFactory workingFactory = factory;
951            
952            for (Configurer<SSLSocketFactory> configurer : this.sslSocketFactoryConfigurers) {
953                workingFactory = configurer.configure(workingFactory);
954            }
955            
956            return workingFactory;
957        }
958
959        /**
960         * Configures an {@link SSLServerSocketFactory} based on the
961         * configurers in this instance. The return value from this method may be
962         * {@code factory} or it may be a decorated instance there of.
963         * Consequently, any subsequent actions on {@code factory} must be
964         * performed using the returned value.
965         * 
966         * @param factory the factory to configure
967         * @return {@code factory} or a decorated instance there of
968         */
969        protected SSLServerSocketFactory configureSSLServerSocketFactory(
970                SSLServerSocketFactory factory) {
971            SSLServerSocketFactory workingFactory = factory;
972            
973            for (Configurer<SSLServerSocketFactory> configurer : this.sslServerSocketFactoryConfigurers) {
974                workingFactory = configurer.configure(workingFactory);
975            }
976            
977            return workingFactory;
978        }
979    }
980    
981    /**
982     * A decorator that enables the application of configuration options to be
983     * applied to created sockets even after this factory has been created and
984     * turned over to client code.
985     */
986    protected static final class SSLServerSocketFactoryDecorator extends SSLServerSocketFactory {
987        
988        private final SSLServerSocketFactory sslServerSocketFactory;
989        private final List<Configurer<SSLServerSocket>> sslServerSocketConfigurers;
990        
991        public SSLServerSocketFactoryDecorator(SSLServerSocketFactory sslServerSocketFactory,
992                                               List<Configurer<SSLServerSocket>> sslServerSocketConfigurers) {
993            this.sslServerSocketFactory = sslServerSocketFactory;
994            this.sslServerSocketConfigurers = sslServerSocketConfigurers;
995        }
996
997        @Override
998        public String[] getDefaultCipherSuites() {
999            return this.sslServerSocketFactory.getDefaultCipherSuites();
1000        }
1001
1002        @Override
1003        public String[] getSupportedCipherSuites() {
1004            return this.sslServerSocketFactory.getSupportedCipherSuites();
1005        }
1006
1007        @Override
1008        public ServerSocket createServerSocket() throws IOException {
1009            return this.configureSocket(this.sslServerSocketFactory.createServerSocket());
1010        }
1011
1012        @Override
1013        public ServerSocket createServerSocket(int port, int backlog, InetAddress ifAddress) throws IOException {
1014            return this.configureSocket(this.sslServerSocketFactory.createServerSocket(port, backlog, ifAddress));
1015        }
1016
1017        @Override
1018        public ServerSocket createServerSocket(int port, int backlog) throws IOException {
1019            return this.configureSocket(this.sslServerSocketFactory.createServerSocket(port, backlog));
1020        }
1021
1022        @Override
1023        public ServerSocket createServerSocket(int port) throws IOException {
1024            return this.configureSocket(this.sslServerSocketFactory.createServerSocket(port));
1025        }
1026        
1027        public SSLServerSocketFactory getDelegate() {
1028            return this.sslServerSocketFactory;
1029        }
1030        
1031        private ServerSocket configureSocket(ServerSocket s) {
1032            SSLServerSocket workingSocket = (SSLServerSocket) s;
1033            
1034            LOG.debug("Created ServerSocket [{}] from SslServerSocketFactory [{}].", s, sslServerSocketFactory);
1035
1036            for (Configurer<SSLServerSocket> configurer : this.sslServerSocketConfigurers) {
1037                workingSocket = configurer.configure(workingSocket);
1038            }
1039
1040            return workingSocket;
1041        }
1042    }
1043    
1044    /**
1045     * A decorator that enables the application of configuration options to be
1046     * applied to created sockets even after this factory has been created and
1047     * turned over to client code.
1048     */
1049    protected static final class SSLSocketFactoryDecorator extends SSLSocketFactory {
1050
1051        private final SSLSocketFactory sslSocketFactory;
1052        private final List<Configurer<SSLSocket>> sslSocketConfigurers;
1053
1054        public SSLSocketFactoryDecorator(SSLSocketFactory sslSocketFactory,
1055                                         List<Configurer<SSLSocket>> sslSocketConfigurers) {
1056            this.sslSocketFactory = sslSocketFactory;
1057            this.sslSocketConfigurers = sslSocketConfigurers;
1058        }
1059
1060        @Override
1061        public String[] getDefaultCipherSuites() {
1062            return sslSocketFactory.getDefaultCipherSuites();
1063        }
1064
1065        @Override
1066        public String[] getSupportedCipherSuites() {
1067            return sslSocketFactory.getSupportedCipherSuites();
1068        }
1069
1070        @Override
1071        public Socket createSocket() throws IOException {
1072            return configureSocket(sslSocketFactory.createSocket());
1073        }
1074
1075        @Override
1076        public Socket createSocket(Socket s, String host, 
1077                                   int port, boolean autoClose) throws IOException, UnknownHostException {
1078            return configureSocket(sslSocketFactory.createSocket(s, host, port, autoClose));
1079        }
1080
1081        @Override
1082        public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
1083            return configureSocket(sslSocketFactory.createSocket(host, port));
1084        }
1085
1086        @Override
1087        public Socket createSocket(String host, int port, 
1088                                   InetAddress localHost, int localPort) throws IOException, UnknownHostException {
1089            return configureSocket(sslSocketFactory.createSocket(host, port, localHost, localPort));
1090        }
1091
1092        @Override
1093        public Socket createSocket(InetAddress host, int port) throws IOException {
1094            return configureSocket(sslSocketFactory.createSocket(host, port));
1095        }
1096
1097        @Override
1098        public Socket createSocket(InetAddress address, int port, 
1099                                   InetAddress localAddress, int localPort) throws IOException {
1100            return configureSocket(sslSocketFactory.createSocket(address, port, localAddress, localPort));
1101        }
1102        
1103        public SSLSocketFactory getDelegate() {
1104            return this.sslSocketFactory;
1105        }
1106
1107        private Socket configureSocket(Socket s) {
1108            SSLSocket workingSocket = (SSLSocket) s;
1109            
1110            LOG.debug("Created Socket [{}] from SocketFactory [{}].", s, sslSocketFactory);
1111
1112            for (Configurer<SSLSocket> configurer : this.sslSocketConfigurers) {
1113                workingSocket = configurer.configure(workingSocket);
1114            }
1115
1116            return workingSocket;
1117        }
1118    }
1119
1120    private static String createCipherSuiteLogMessage(String entityName) {
1121        return "Configuring " + entityName + " [{}] with " + LS
1122                + "\t explicitly set cipher suites [{}]," + LS
1123                + "\t cipher suite patterns [{}]," + LS
1124                + "\t available cipher suites [{}]," + LS
1125                + "\t currently enabled cipher suites [{}]," + LS
1126                + "\t and default cipher suite patterns [{}]." + LS
1127                + "\t Resulting enabled cipher suites are [{}].";
1128    }
1129    
1130    private static String createProtocolLogMessage(String entityName) {
1131        return "Configuring " + entityName + " [{}] with " + LS
1132                + "\t explicitly set protocols [{}]," + LS
1133                + "\t protocol patterns [{}]," + LS
1134                + "\t available protocols [{}]," + LS
1135                + "\t currently enabled protocols [{}]," + LS
1136                + "\t and default protocol patterns [{}]." + LS
1137                + "\t Resulting enabled protocols are [{}].";
1138    }
1139}