View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.net.ssl;
15  
16  import java.security.KeyManagementException;
17  import java.security.KeyStore;
18  import java.security.KeyStoreException;
19  import java.security.NoSuchAlgorithmException;
20  import java.security.NoSuchProviderException;
21  import java.security.SecureRandom;
22  import java.security.UnrecoverableKeyException;
23  import java.security.cert.CertificateException;
24  
25  import javax.net.ssl.KeyManager;
26  import javax.net.ssl.KeyManagerFactory;
27  import javax.net.ssl.SSLContext;
28  import javax.net.ssl.TrustManager;
29  import javax.net.ssl.TrustManagerFactory;
30  
31  import ch.qos.logback.core.spi.ContextAware;
32  
33  /**
34   * A factory bean for a JSSE {@link SSLContext}.
35   * <p>
36   * This object holds the configurable properties for an SSL context and uses
37   * them to create an {@link SSLContext} instance.
38   *
39   * @author Carl Harris
40   */
41  public class SSLContextFactoryBean {
42  
43      private static final String JSSE_KEY_STORE_PROPERTY = "javax.net.ssl.keyStore";
44      private static final String JSSE_TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore";
45  
46      private KeyStoreFactoryBean keyStore;
47      private KeyStoreFactoryBean trustStore;
48      private SecureRandomFactoryBean secureRandom;
49      private KeyManagerFactoryFactoryBean keyManagerFactory;
50      private TrustManagerFactoryFactoryBean trustManagerFactory;
51      private String protocol;
52      private String provider;
53  
54      /**
55       * Creates a new {@link SSLContext} using the receiver's configuration.
56       * 
57       * @param context context for status messages
58       * @return {@link SSLContext} object
59       * @throws NoSuchProviderException   if a provider specified for one of the JCA
60       *                                   or JSSE components utilized in creating the
61       *                                   context is not known to the platform
62       * @throws NoSuchAlgorithmException  if a JCA or JSSE algorithm, protocol, or
63       *                                   type name specified for one of the
64       *                                   context's components is not known to a
65       *                                   given provider (or platform default
66       *                                   provider for the component)
67       * @throws KeyManagementException    if an error occurs in creating a
68       *                                   {@link KeyManager} for the context
69       * @throws UnrecoverableKeyException if a private key needed by a
70       *                                   {@link KeyManager} cannot be obtained from
71       *                                   a key store
72       * @throws KeyStoreException         if an error occurs in reading the contents
73       *                                   of a key store
74       * @throws CertificateException      if an error occurs in reading the contents
75       *                                   of a certificate
76       */
77      public SSLContext createContext(ContextAware context) throws NoSuchProviderException, NoSuchAlgorithmException,
78              KeyManagementException, UnrecoverableKeyException, KeyStoreException, CertificateException {
79  
80          SSLContext sslContext = getProvider() != null ? SSLContext.getInstance(getProtocol(), getProvider())
81                  : SSLContext.getInstance(getProtocol());
82  
83          context.addInfo("SSL protocol '" + sslContext.getProtocol() + "' provider '" + sslContext.getProvider() + "'");
84  
85          KeyManager[] keyManagers = createKeyManagers(context);
86          TrustManager[] trustManagers = createTrustManagers(context);
87          SecureRandom secureRandom = createSecureRandom(context);
88          sslContext.init(keyManagers, trustManagers, secureRandom);
89          return sslContext;
90      }
91  
92      /**
93       * Creates key managers using the receiver's key store configuration.
94       * 
95       * @param context context for status messages
96       * @return an array of key managers or {@code null} if no key store
97       *         configuration was provided
98       * @throws NoSuchProviderException  if a provider specified for one of the key
99       *                                  manager components is not known to the
100      *                                  platform
101      * @throws NoSuchAlgorithmException if an algorithm specified for one of the key
102      *                                  manager components is not known to the
103      *                                  relevant provider
104      * @throws KeyStoreException        if an error occurs in reading a key store
105      */
106     private KeyManager[] createKeyManagers(ContextAware context)
107             throws NoSuchProviderException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException {
108 
109         if (getKeyStore() == null)
110             return null;
111 
112         KeyStore keyStore = getKeyStore().createKeyStore();
113         context.addInfo("key store of type '" + keyStore.getType() + "' provider '" + keyStore.getProvider() + "': "
114                 + getKeyStore().getLocation());
115 
116         KeyManagerFactory kmf = getKeyManagerFactory().createKeyManagerFactory();
117         context.addInfo("key manager algorithm '" + kmf.getAlgorithm() + "' provider '" + kmf.getProvider() + "'");
118 
119         char[] passphrase = getKeyStore().getPassword().toCharArray();
120         kmf.init(keyStore, passphrase);
121         return kmf.getKeyManagers();
122     }
123 
124     /**
125      * Creates trust managers using the receiver's trust store configuration.
126      * 
127      * @param context context for status messages
128      * @return an array of trust managers or {@code null} if no trust store
129      *         configuration was provided
130      * @throws NoSuchProviderException  if a provider specified for one of the trust
131      *                                  manager components is not known to the
132      *                                  platform
133      * @throws NoSuchAlgorithmException if an algorithm specified for one of the
134      *                                  trust manager components is not known to the
135      *                                  relevant provider
136      * @throws KeyStoreException        if an error occurs in reading a key store
137      *                                  containing trust anchors
138      */
139     private TrustManager[] createTrustManagers(ContextAware context)
140             throws NoSuchProviderException, NoSuchAlgorithmException, KeyStoreException {
141 
142         if (getTrustStore() == null)
143             return null;
144 
145         KeyStore trustStore = getTrustStore().createKeyStore();
146         context.addInfo("trust store of type '" + trustStore.getType() + "' provider '" + trustStore.getProvider()
147                 + "': " + getTrustStore().getLocation());
148 
149         TrustManagerFactory tmf = getTrustManagerFactory().createTrustManagerFactory();
150         context.addInfo("trust manager algorithm '" + tmf.getAlgorithm() + "' provider '" + tmf.getProvider() + "'");
151 
152         tmf.init(trustStore);
153         return tmf.getTrustManagers();
154     }
155 
156     private SecureRandom createSecureRandom(ContextAware context)
157             throws NoSuchProviderException, NoSuchAlgorithmException {
158 
159         SecureRandom secureRandom = getSecureRandom().createSecureRandom();
160         context.addInfo("secure random algorithm '" + secureRandom.getAlgorithm() + "' provider '"
161                 + secureRandom.getProvider() + "'");
162 
163         return secureRandom;
164     }
165 
166     /**
167      * Gets the key store configuration.
168      * 
169      * @return key store factory bean or {@code null} if no key store configuration
170      *         was provided
171      */
172     public KeyStoreFactoryBean getKeyStore() {
173         if (keyStore == null) {
174             keyStore = keyStoreFromSystemProperties(JSSE_KEY_STORE_PROPERTY);
175         }
176         return keyStore;
177     }
178 
179     /**
180      * Sets the key store configuration.
181      * 
182      * @param keyStore the key store factory bean to set
183      */
184     public void setKeyStore(KeyStoreFactoryBean keyStore) {
185         this.keyStore = keyStore;
186     }
187 
188     /**
189      * Gets the trust store configuration.
190      * 
191      * @return trust store factory bean or {@code null} if no trust store
192      *         configuration was provided
193      */
194     public KeyStoreFactoryBean getTrustStore() {
195         if (trustStore == null) {
196             trustStore = keyStoreFromSystemProperties(JSSE_TRUST_STORE_PROPERTY);
197         }
198         return trustStore;
199     }
200 
201     /**
202      * Sets the trust store configuration.
203      * 
204      * @param trustStore the trust store factory bean to set
205      */
206     public void setTrustStore(KeyStoreFactoryBean trustStore) {
207         this.trustStore = trustStore;
208     }
209 
210     /**
211      * Constructs a key store factory bean using JSSE system properties.
212      * 
213      * @param property base property name (e.g. {@code javax.net.ssl.keyStore})
214      * @return key store or {@code null} if no value is defined for the base system
215      *         property name
216      */
217     private KeyStoreFactoryBean keyStoreFromSystemProperties(String property) {
218         if (System.getProperty(property) == null)
219             return null;
220         KeyStoreFactoryBean keyStore = new KeyStoreFactoryBean();
221         keyStore.setLocation(locationFromSystemProperty(property));
222         keyStore.setProvider(System.getProperty(property + "Provider"));
223         keyStore.setPassword(System.getProperty(property + "Password"));
224         keyStore.setType(System.getProperty(property + "Type"));
225         return keyStore;
226     }
227 
228     /**
229      * Constructs a resource location from a JSSE system property.
230      * 
231      * @param name property name (e.g. {@code javax.net.ssl.keyStore})
232      * @return URL for the location specified in the property or {@code null} if no
233      *         value is defined for the property
234      */
235     private String locationFromSystemProperty(String name) {
236         String location = System.getProperty(name);
237         if (location != null && !location.startsWith("file:")) {
238             location = "file:" + location;
239         }
240         return location;
241     }
242 
243     /**
244      * Gets the secure random generator configuration.
245      * 
246      * @return secure random factory bean; if no secure random generator
247      *         configuration has been set, a default factory bean is returned
248      */
249     public SecureRandomFactoryBean getSecureRandom() {
250         if (secureRandom == null) {
251             return new SecureRandomFactoryBean();
252         }
253         return secureRandom;
254     }
255 
256     /**
257      * Sets the secure random generator configuration.
258      * 
259      * @param secureRandom the secure random factory bean to set
260      */
261     public void setSecureRandom(SecureRandomFactoryBean secureRandom) {
262         this.secureRandom = secureRandom;
263     }
264 
265     /**
266      * Gets the key manager factory configuration.
267      * 
268      * @return factory bean; if no key manager factory configuration has been set, a
269      *         default factory bean is returned
270      */
271     public KeyManagerFactoryFactoryBean getKeyManagerFactory() {
272         if (keyManagerFactory == null) {
273             return new KeyManagerFactoryFactoryBean();
274         }
275         return keyManagerFactory;
276     }
277 
278     /**
279      * Sets the key manager factory configuration.
280      * 
281      * @param keyManagerFactory the key manager factory bean to set
282      */
283     public void setKeyManagerFactory(KeyManagerFactoryFactoryBean keyManagerFactory) {
284         this.keyManagerFactory = keyManagerFactory;
285     }
286 
287     /**
288      * Gets the trust manager factory configuration.
289      * 
290      * @return factory bean; if no trust manager factory configuration has been set,
291      *         a default factory bean is returned
292      */
293     public TrustManagerFactoryFactoryBean getTrustManagerFactory() {
294         if (trustManagerFactory == null) {
295             return new TrustManagerFactoryFactoryBean();
296         }
297         return trustManagerFactory;
298     }
299 
300     /**
301      * Sets the trust manager factory configuration.
302      * 
303      * @param trustManagerFactory the factory bean to set
304      */
305     public void setTrustManagerFactory(TrustManagerFactoryFactoryBean trustManagerFactory) {
306         this.trustManagerFactory = trustManagerFactory;
307     }
308 
309     /**
310      * Gets the secure transport protocol name.
311      * 
312      * @return protocol name (e.g. {@code SSL}, {@code TLS}); the
313      *         {@link SSL#DEFAULT_PROTOCOL} is returned if no protocol has been
314      *         configured
315      */
316     public String getProtocol() {
317         if (protocol == null) {
318             return SSL.DEFAULT_PROTOCOL;
319         }
320         return protocol;
321     }
322 
323     /**
324      * Sets the secure transport protocol name.
325      * 
326      * @param protocol a protocol name, which must be recognized by the provider
327      *                 specified by {@link #setProvider(String)} or by the
328      *                 platform's default provider if no platform was specified.
329      */
330     public void setProtocol(String protocol) {
331         this.protocol = protocol;
332     }
333 
334     /**
335      * Gets the JSSE provider name for the SSL context.
336      * 
337      * @return JSSE provider name
338      */
339     public String getProvider() {
340         return provider;
341     }
342 
343     /**
344      * Sets the JSSE provider name for the SSL context.
345      * 
346      * @param provider name of the JSSE provider to use in creating the SSL context
347      */
348     public void setProvider(String provider) {
349         this.provider = provider;
350     }
351 
352 }