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