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