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