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