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