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