001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.util.jsse;
018    
019    import java.io.IOException;
020    import java.security.GeneralSecurityException;
021    import java.security.SecureRandom;
022    import java.security.Security;
023    import java.util.List;
024    
025    import javax.net.ssl.KeyManager;
026    import javax.net.ssl.SSLContext;
027    import javax.net.ssl.SSLEngine;
028    import javax.net.ssl.SSLServerSocketFactory;
029    import javax.net.ssl.SSLSocketFactory;
030    import javax.net.ssl.TrustManager;
031    import javax.net.ssl.X509KeyManager;
032    
033    import org.slf4j.Logger;
034    import org.slf4j.LoggerFactory;
035    
036    /**
037     * Represents {@link SSLContext} configuration options used in instantiating an
038     * {@code SSLContext} instance.
039     */
040    public 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    }