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.security.GeneralSecurityException;
021import java.security.SecureRandom;
022import java.security.Security;
023import java.util.List;
024
025import javax.net.ssl.KeyManager;
026import javax.net.ssl.SSLContext;
027import javax.net.ssl.SSLEngine;
028import javax.net.ssl.SSLServerSocketFactory;
029import javax.net.ssl.SSLSocketFactory;
030import javax.net.ssl.TrustManager;
031import javax.net.ssl.X509KeyManager;
032
033import org.apache.camel.CamelContext;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037/**
038 * Represents {@link SSLContext} configuration options used in instantiating an
039 * {@code SSLContext} instance.
040 */
041public class SSLContextParameters extends BaseSSLContextParameters {
042
043    // TODO : switch to TLSv1.3 when we fully upgrade to JDK11
044    protected static final String DEFAULT_SECURE_SOCKET_PROTOCOL = "TLSv1.2";
045    
046    private static final Logger LOG = LoggerFactory.getLogger(SSLContextParameters.class);
047
048    /**
049     * The optional key manager configuration for creating the
050     * {@link KeyManager}s used in constructing an {@link SSLContext}.
051     */
052    private KeyManagersParameters keyManagers;
053    
054    /**
055     * The optional trust manager configuration for creating the
056     * {@link TrustManager}s used in constructing an {@link SSLContext}.
057     */
058    private TrustManagersParameters trustManagers;
059        
060    /**
061     * The optional secure random configuration options to use for constructing
062     * the {@link SecureRandom} used in the creation of an {@link SSLContext].
063     */
064    private SecureRandomParameters secureRandom;
065    
066    /**
067     * The optional configuration options to be applied purely to the client side settings
068     * of the {@link SSLContext}.  Settings specified here override any duplicate settings
069     * provided at the overall level by this class.  These parameters apply to 
070     * {@link SSLSocketFactory}s and {@link SSLEngine}s produced by the {@code SSLContext}
071     * produced from this class as well as to the {@code SSLContext} itself.
072     */
073    private SSLContextClientParameters clientParameters;
074    
075    /**
076     * The optional configuration options to be applied purely to the server side settings
077     * of the {@link SSLContext}.  Settings specified here override any duplicate settings
078     * provided at the overall level by this class.  These parameters apply to 
079     * {@link SSLServerSocketFactory}s and {@link SSLEngine}s produced by the {@code SSLContext}
080     * produced from this class as well as to the {@code SSLContext} itself.
081     */
082    private SSLContextServerParameters serverParameters;
083
084    /**
085     * The optional provider identifier for the JSSE implementation to use when
086     * constructing an {@link SSLContext}.
087     */
088    private String provider;
089
090    /**
091     * The optional protocol for the secure sockets created by the {@link SSLContext}
092     * represented by this instance's configuration. See Appendix A in the <a
093     * href="http://download.oracle.com/javase/6/docs/technotes/guides//security/jsse/JSSERefGuide.html#AppA"
094     * >Java Secure Socket Extension Reference Guide</a> for information about
095     * standard protocol names.
096     */
097    private String secureSocketProtocol;    
098    
099    /**
100     * An optional certificate alias to use. This is useful when the keystore has multiple 
101     * certificates.
102     */
103    private String certAlias;
104
105    public KeyManagersParameters getKeyManagers() {
106        return keyManagers;
107    }
108
109    /**
110     * Sets the optional key manager configuration for creating the
111     * {@link KeyManager}s used in constructing an {@link SSLContext}.
112     * 
113     * @param keyManagers the options or {@code null} to provide no
114     *            {@code KeyManager}s
115     */
116    public void setKeyManagers(KeyManagersParameters keyManagers) {
117        this.keyManagers = keyManagers;
118    }
119
120    public TrustManagersParameters getTrustManagers() {
121        return trustManagers;
122    }
123
124    /**
125     * Sets the optional trust manager configuration for creating the
126     * {@link TrustManager}s used in constructing an {@link SSLContext}.
127     * 
128     * @param trustManagers the options or {@code null} to provide no
129     *            {@code TrustManager}s
130     */
131    public void setTrustManagers(TrustManagersParameters trustManagers) {
132        this.trustManagers = trustManagers;
133    }
134
135    public SecureRandomParameters getSecureRandom() {
136        return secureRandom;
137    }
138
139    /**
140     * Sets the optional secure random configuration options to use for 
141     * constructing the {@link SecureRandom} used in the creation of an {@link SSLContext}.
142     *
143     * @param secureRandom the options or {@code null} to use the default
144     */
145    public void setSecureRandom(SecureRandomParameters secureRandom) {
146        this.secureRandom = secureRandom;
147    }
148    
149    public SSLContextClientParameters getClientParameters() {
150        return clientParameters;
151    }
152
153    /**
154     * The optional configuration options to be applied purely to the client side settings
155     * of the {@link SSLContext}.  Settings specified here override any duplicate settings
156     * provided at the overall level by this class.  These parameters apply to 
157     * {@link SSLSocketFactory}s and {@link SSLEngine}s produced by the {@code SSLContext}
158     * produced from this class as well as to the {@code SSLContext} itself.
159     *
160     * @param clientParameters the optional additional client-side parameters
161     */
162    public void setClientParameters(SSLContextClientParameters clientParameters) {
163        this.clientParameters = clientParameters;
164    }
165
166    public SSLContextServerParameters getServerParameters() {
167        return serverParameters;
168    }
169
170    /**
171     * The optional configuration options to be applied purely to the server side settings
172     * of the {@link SSLContext}.  Settings specified here override any duplicate settings
173     * provided at the overall level by this class.  These parameters apply to 
174     * {@link SSLServerSocketFactory}s and {@link SSLEngine}s produced by the {@code SSLContext}
175     * produced from this class as well as to the {@code SSLContext} itself.
176     *
177     * @param serverParameters the optional additional client-side parameters
178     */
179    public void setServerParameters(SSLContextServerParameters serverParameters) {
180        this.serverParameters = serverParameters;
181    }
182
183    public String getProvider() {
184        return provider;
185    }
186
187    /**
188     * Sets the optional provider identifier to use when constructing an
189     * {@link SSLContext}.
190     * 
191     * @param provider the identifier (from the list of available providers
192     *            returned by {@link Security#getProviders()}) or {@code null}
193     *            to use the highest priority provider implementing the secure
194     *            socket protocol
195     *
196     * @see Security#getProviders(java.util.Map)
197     * @see #setSecureSocketProtocol(String)            
198     */
199    public void setProvider(String provider) {
200        this.provider = provider;
201    }
202
203    public String getSecureSocketProtocol() {
204        if (this.secureSocketProtocol == null) {
205            return DEFAULT_SECURE_SOCKET_PROTOCOL;
206        }
207        return this.secureSocketProtocol;
208    }
209
210    /**
211     * Sets the optional protocol for the secure sockets created by the
212     * {@link SSLContext} represented by this instance's configuration. Defaults
213     * to TLS. See Appendix A in the <a href=
214     * "http://download.oracle.com/javase/6/docs/technotes/guides//security/jsse/JSSERefGuide.html#AppA"
215     * >Java Secure Socket Extension Reference Guide</a> for information about
216     * standard protocol names.
217     * 
218     * @param secureSocketProtocol the name of the protocol or {@code null} to
219     *            use the default (TLS)
220     */
221    public void setSecureSocketProtocol(String secureSocketProtocol) {
222        this.secureSocketProtocol = secureSocketProtocol;
223    }
224    
225    public String getCertAlias() {
226        return certAlias;
227    }
228
229    /**
230     * An optional certificate alias to use. This is useful when the keystore has multiple 
231     * certificates.
232     * @param certAlias an optional certificate alias to use
233     */
234    public void setCertAlias(String certAlias) {
235        this.certAlias = certAlias;
236    }
237    
238    ////////////////////////////////////////////
239
240    /**
241     * Creates an {@link SSLContext} based on the related configuration options
242     * of this instance. Namely, {@link #keyManagers}, {@link #trustManagers}, and
243     * {@link #secureRandom}, but also respecting the chosen provider and secure
244     * socket protocol as well.
245     *
246     * @param camelContext  The camel context
247     *
248     * @return a newly configured instance
249     *
250     * @throws GeneralSecurityException if there is a problem in this instances
251     *             configuration or that of its nested configuration options
252     * @throws IOException if there is an error reading a key/trust store
253     */
254    public SSLContext createSSLContext(CamelContext camelContext) throws GeneralSecurityException, IOException {
255        if (camelContext != null) {
256            // setup CamelContext before creating SSLContext
257            setCamelContext(camelContext);
258            if (keyManagers != null) {
259                keyManagers.setCamelContext(camelContext);
260            }
261            if (trustManagers != null) {
262                trustManagers.setCamelContext(camelContext);
263            }
264            if (secureRandom != null) {
265                secureRandom.setCamelContext(camelContext);
266            }
267            if (clientParameters != null) {
268                clientParameters.setCamelContext(camelContext);
269            }
270            if (serverParameters != null) {
271                serverParameters.setCamelContext(camelContext);
272            }
273        }
274
275        LOG.trace("Creating SSLContext from SSLContextParameters [{}].", this);
276        
277        LOG.info("Available providers: {}.", Security.getProviders());
278
279        KeyManager[] keyManagers = this.keyManagers == null ? null : this.keyManagers.createKeyManagers();
280        TrustManager[] trustManagers = this.trustManagers == null ? null : this.trustManagers.createTrustManagers();
281        SecureRandom secureRandom = this.secureRandom == null ? null : this.secureRandom.createSecureRandom();
282
283        SSLContext context;
284        if (this.getProvider() == null) {
285            context = SSLContext.getInstance(this.parsePropertyValue(this.getSecureSocketProtocol()));
286        } else {
287            context = SSLContext.getInstance(this.parsePropertyValue(this.getSecureSocketProtocol()),
288                                             this.parsePropertyValue(this.getProvider()));
289        }
290        
291        if (this.getCertAlias() != null && keyManagers != null) {
292            for (int idx = 0; idx < keyManagers.length; idx++) {
293                if (keyManagers[idx] instanceof X509KeyManager) {
294                    try {
295                        keyManagers[idx] = new AliasedX509ExtendedKeyManager(this.getCertAlias(),
296                                                                             (X509KeyManager)keyManagers[idx]);
297                    } catch (Exception e) {
298                        throw new GeneralSecurityException(e);
299                    }
300                }
301            }
302        }
303        
304        LOG.debug("SSLContext [{}], initialized from [{}], is using provider [{}], protocol [{}], key managers {}, trust managers {}, and secure random [{}].",
305                 new Object[] {context, this, context.getProvider(), context.getProtocol(), keyManagers, trustManagers, secureRandom});
306        
307        context.init(keyManagers, trustManagers, secureRandom);
308        
309        this.configureSSLContext(context);
310        
311        // Decorate the context.
312        context = new SSLContextDecorator(
313                new SSLContextSpiDecorator(
314                        context,
315                        this.getSSLEngineConfigurers(context),
316                        this.getSSLSocketFactoryConfigurers(context),
317                        this.getSSLServerSocketFactoryConfigurers(context)));
318
319        return context;
320    }
321    
322    @Override
323    protected void configureSSLContext(SSLContext context) throws GeneralSecurityException {
324        LOG.trace("Configuring client and server side SSLContext parameters on SSLContext [{}]...", context);
325        super.configureSSLContext(context);
326        
327        if (this.getClientParameters() != null) {
328            LOG.trace("Overriding client-side SSLContext parameters on SSLContext [{}] with configured client parameters.",
329                      context);
330            this.getClientParameters().configureSSLContext(context);
331        }
332
333        if (this.getServerParameters() != null) {
334            LOG.trace("Overriding server-side SSLContext parameters on SSLContext [{}] with configured server parameters.",
335                      context);
336            this.getServerParameters().configureSSLContext(context);
337        }        
338        
339        LOG.trace("Configured client and server side SSLContext parameters on SSLContext [{}].", context);
340    }
341    
342    @Override
343    protected List<Configurer<SSLEngine>> getSSLEngineConfigurers(SSLContext context) {
344        LOG.trace("Collecting client and server side SSLEngine configurers on SSLContext [{}]...", context);
345        List<Configurer<SSLEngine>> configurers = super.getSSLEngineConfigurers(context);
346        
347        if (this.getClientParameters() != null) {
348            LOG.trace("Augmenting SSLEngine configurers with configurers from client parameters on SSLContext [{}].",
349                      context);
350            configurers.addAll(this.getClientParameters().getSSLEngineConfigurers(context));
351        }
352        
353        if (this.getServerParameters() != null) {
354            LOG.trace("Augmenting SSLEngine configurers with configurers from server parameters on SSLContext [{}].",
355                      context);
356            configurers.addAll(this.getServerParameters().getSSLEngineConfigurers(context));
357        }
358        
359        LOG.trace("Collected client and server side SSLEngine configurers on SSLContext [{}].", context);
360        
361        return configurers;
362    }
363    
364    @Override
365    protected List<Configurer<SSLSocketFactory>> getSSLSocketFactoryConfigurers(SSLContext context) {
366        LOG.trace("Collecting SSLSocketFactory configurers on SSLContext [{}]...", context);
367        List<Configurer<SSLSocketFactory>> configurers = super.getSSLSocketFactoryConfigurers(context);
368        
369        if (this.getClientParameters() != null) {
370            LOG.trace("Augmenting SSLSocketFactory configurers with configurers from client parameters on SSLContext [{}].",
371                      context);
372            configurers.addAll(this.getClientParameters().getSSLSocketFactoryConfigurers(context));
373        }
374        
375        LOG.trace("Collected SSLSocketFactory configurers on SSLContext [{}].", context);
376        
377        return configurers;
378    }
379
380    @Override
381    protected List<Configurer<SSLServerSocketFactory>> getSSLServerSocketFactoryConfigurers(SSLContext context) {
382        LOG.trace("Collecting SSLServerSocketFactory configurers for SSLContext [{}]...", context);
383        List<Configurer<SSLServerSocketFactory>> configurers = super.getSSLServerSocketFactoryConfigurers(context);
384        
385        if (this.getServerParameters() != null) {
386            LOG.trace("Augmenting SSLServerSocketFactory configurers with configurers from server parameters for SSLContext [{}].",
387                      context);
388            configurers.addAll(this.getServerParameters().getSSLServerSocketFactoryConfigurers(context));
389        }
390        
391        LOG.trace("Collected client and server side SSLServerSocketFactory configurers for SSLContext [{}].", context);
392        
393        return configurers;
394    }
395
396    @Override
397    public String toString() {
398        StringBuilder builder = new StringBuilder();
399        builder.append("SSLContextParameters[keyManagers=");
400        builder.append(keyManagers);
401        builder.append(", trustManagers=");
402        builder.append(trustManagers);
403        builder.append(", secureRandom=");
404        builder.append(secureRandom);
405        builder.append(", clientParameters=");
406        builder.append(clientParameters);
407        builder.append(", serverParameters=");
408        builder.append(serverParameters);
409        builder.append(", provider=");
410        builder.append(provider);
411        builder.append(", secureSocketProtocol=");
412        builder.append(secureSocketProtocol);
413        builder.append(", certAlias=");
414        builder.append(certAlias);
415        builder.append(", getCipherSuites()=");
416        builder.append(getCipherSuites());
417        builder.append(", getCipherSuitesFilter()=");
418        builder.append(getCipherSuitesFilter());
419        builder.append(", getSecureSocketProtocols()=");
420        builder.append(getSecureSocketProtocols());
421        builder.append(", getSecureSocketProtocolsFilter()=");
422        builder.append(getSecureSocketProtocolsFilter());
423        builder.append(", getSessionTimeout()=");
424        builder.append(getSessionTimeout());
425        builder.append("]");
426        return builder.toString();
427    }
428
429}