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