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       * @param context context for status messages
57       * @return {@link SSLContext} object
58       * @throws NoSuchProviderException if a provider specified for one of the
59       *    JCA or JSSE components utilized in creating the context is not
60       *    known to the platform
61       * @throws NoSuchAlgorithmException if a JCA or JSSE algorithm, protocol, 
62       *    or type name specified for one of the context's components is not
63       *    known to a given provider (or platform default provider for the
64       *    component)
65       * @throws KeyManagementException if an error occurs in creating a 
66       *    {@link KeyManager} for the context
67       * @throws UnrecoverableKeyException if a private key needed by a 
68       *    {@link KeyManager} cannot be obtained from a key store
69       * @throws KeyStoreException if an error occurs in reading the
70       *    contents of a key store
71       * @throws CertificateException if an error occurs in reading the
72       *    contents of a certificate
73       */
74      public SSLContext createContext(ContextAware context) throws NoSuchProviderException, NoSuchAlgorithmException, KeyManagementException,
75                      UnrecoverableKeyException, KeyStoreException, CertificateException {
76  
77          SSLContext sslContext = getProvider() != null ? SSLContext.getInstance(getProtocol(), getProvider()) : SSLContext.getInstance(getProtocol());
78  
79          context.addInfo("SSL protocol '" + sslContext.getProtocol() + "' provider '" + sslContext.getProvider() + "'");
80  
81          KeyManager[] keyManagers = createKeyManagers(context);
82          TrustManager[] trustManagers = createTrustManagers(context);
83          SecureRandom secureRandom = createSecureRandom(context);
84          sslContext.init(keyManagers, trustManagers, secureRandom);
85          return sslContext;
86      }
87  
88      /**
89       * Creates key managers using the receiver's key store configuration.
90       * @param context context for status messages
91       * @return an array of key managers or {@code null} if no key store
92       *    configuration was provided
93       * @throws NoSuchProviderException if a provider specified for one
94       *    of the key manager components is not known to the platform
95       * @throws NoSuchAlgorithmException if an algorithm specified for
96       *    one of the key manager components is not known to the relevant
97       *    provider
98       * @throws KeyStoreException if an error occurs in reading a key store
99       */
100     private KeyManager[] createKeyManagers(ContextAware context) throws NoSuchProviderException, NoSuchAlgorithmException, UnrecoverableKeyException,
101                     KeyStoreException {
102 
103         if (getKeyStore() == null)
104             return null;
105 
106         KeyStore keyStore = getKeyStore().createKeyStore();
107         context.addInfo("key store of type '" + keyStore.getType() + "' provider '" + keyStore.getProvider() + "': " + getKeyStore().getLocation());
108 
109         KeyManagerFactory kmf = getKeyManagerFactory().createKeyManagerFactory();
110         context.addInfo("key manager algorithm '" + kmf.getAlgorithm() + "' provider '" + kmf.getProvider() + "'");
111 
112         char[] passphrase = getKeyStore().getPassword().toCharArray();
113         kmf.init(keyStore, passphrase);
114         return kmf.getKeyManagers();
115     }
116 
117     /**
118      * Creates trust managers using the receiver's trust store configuration.
119      * @param context context for status messages
120      * @return an array of trust managers or {@code null} if no trust store
121      *    configuration was provided
122      * @throws NoSuchProviderException if a provider specified for one
123      *    of the trust manager components is not known to the platform
124      * @throws NoSuchAlgorithmException if an algorithm specified for
125      *    one of the trust manager components is not known to the relevant
126      *    provider
127      * @throws KeyStoreException if an error occurs in reading a key
128      *    store containing trust anchors 
129      */
130     private TrustManager[] createTrustManagers(ContextAware context) throws NoSuchProviderException, NoSuchAlgorithmException, KeyStoreException {
131 
132         if (getTrustStore() == null)
133             return null;
134 
135         KeyStore trustStore = getTrustStore().createKeyStore();
136         context.addInfo("trust store of type '" + trustStore.getType() + "' provider '" + trustStore.getProvider() + "': " + getTrustStore().getLocation());
137 
138         TrustManagerFactory tmf = getTrustManagerFactory().createTrustManagerFactory();
139         context.addInfo("trust manager algorithm '" + tmf.getAlgorithm() + "' provider '" + tmf.getProvider() + "'");
140 
141         tmf.init(trustStore);
142         return tmf.getTrustManagers();
143     }
144 
145     private SecureRandom createSecureRandom(ContextAware context) throws NoSuchProviderException, NoSuchAlgorithmException {
146 
147         SecureRandom secureRandom = getSecureRandom().createSecureRandom();
148         context.addInfo("secure random algorithm '" + secureRandom.getAlgorithm() + "' provider '" + secureRandom.getProvider() + "'");
149 
150         return secureRandom;
151     }
152 
153     /**
154      * Gets the key store configuration.
155      * @return key store factory bean or {@code null} if no key store 
156      *    configuration was provided
157      */
158     public KeyStoreFactoryBean getKeyStore() {
159         if (keyStore == null) {
160             keyStore = keyStoreFromSystemProperties(JSSE_KEY_STORE_PROPERTY);
161         }
162         return keyStore;
163     }
164 
165     /**
166      * Sets the key store configuration.
167      * @param keyStore the key store factory bean to set
168      */
169     public void setKeyStore(KeyStoreFactoryBean keyStore) {
170         this.keyStore = keyStore;
171     }
172 
173     /**
174      * Gets the trust store configuration.
175      * @return trust store factory bean or {@code null} if no trust store 
176      *    configuration was provided
177      */
178     public KeyStoreFactoryBean getTrustStore() {
179         if (trustStore == null) {
180             trustStore = keyStoreFromSystemProperties(JSSE_TRUST_STORE_PROPERTY);
181         }
182         return trustStore;
183     }
184 
185     /**
186      * Sets the trust store configuration.
187      * @param trustStore the trust store factory bean to set
188      */
189     public void setTrustStore(KeyStoreFactoryBean trustStore) {
190         this.trustStore = trustStore;
191     }
192 
193     /**
194      * Constructs a key store factory bean using JSSE system properties.
195      * @param property base property name (e.g. {@code javax.net.ssl.keyStore})
196      * @return key store or {@code null} if no value is defined for the
197      *    base system property name
198      */
199     private KeyStoreFactoryBean keyStoreFromSystemProperties(String property) {
200         if (System.getProperty(property) == null)
201             return null;
202         KeyStoreFactoryBean keyStore = new KeyStoreFactoryBean();
203         keyStore.setLocation(locationFromSystemProperty(property));
204         keyStore.setProvider(System.getProperty(property + "Provider"));
205         keyStore.setPassword(System.getProperty(property + "Password"));
206         keyStore.setType(System.getProperty(property + "Type"));
207         return keyStore;
208     }
209 
210     /**
211      * Constructs a resource location from a JSSE system property.
212      * @param name property name (e.g. {@code javax.net.ssl.keyStore})
213      * @return URL for the location specified in the property or {@code null}
214      *    if no value is defined for the property
215      */
216     private String locationFromSystemProperty(String name) {
217         String location = System.getProperty(name);
218         if (location != null && !location.startsWith("file:")) {
219             location = "file:" + location;
220         }
221         return location;
222     }
223 
224     /**
225      * Gets the secure random generator configuration.
226      * @return secure random factory bean; if no secure random generator 
227      *    configuration has been set, a default factory bean is returned
228      */
229     public SecureRandomFactoryBean getSecureRandom() {
230         if (secureRandom == null) {
231             return new SecureRandomFactoryBean();
232         }
233         return secureRandom;
234     }
235 
236     /**
237      * Sets the secure random generator configuration.
238      * @param secureRandom the secure random factory bean to set
239      */
240     public void setSecureRandom(SecureRandomFactoryBean secureRandom) {
241         this.secureRandom = secureRandom;
242     }
243 
244     /**
245      * Gets the key manager factory configuration.
246      * @return factory bean; if no key manager factory 
247      *    configuration has been set, a default factory bean is returned
248      */
249     public KeyManagerFactoryFactoryBean getKeyManagerFactory() {
250         if (keyManagerFactory == null) {
251             return new KeyManagerFactoryFactoryBean();
252         }
253         return keyManagerFactory;
254     }
255 
256     /**
257      * Sets the key manager factory configuration.
258      * @param keyManagerFactory the key manager factory factory bean to set
259      */
260     public void setKeyManagerFactory(KeyManagerFactoryFactoryBean keyManagerFactory) {
261         this.keyManagerFactory = keyManagerFactory;
262     }
263 
264     /**
265      * Gets the trust manager factory configuration.
266      * @return factory bean; if no trust manager factory 
267      *    configuration has been set, a default factory bean is returned
268      */
269     public TrustManagerFactoryFactoryBean getTrustManagerFactory() {
270         if (trustManagerFactory == null) {
271             return new TrustManagerFactoryFactoryBean();
272         }
273         return trustManagerFactory;
274     }
275 
276     /**
277      * Sets the trust manager factory configuration.
278      * @param trustManagerFactory the factory bean to set
279      */
280     public void setTrustManagerFactory(TrustManagerFactoryFactoryBean trustManagerFactory) {
281         this.trustManagerFactory = trustManagerFactory;
282     }
283 
284     /**
285      * Gets the secure transport protocol name.
286      * @return protocol name (e.g. {@code SSL}, {@code TLS}); the
287      *    {@link SSL#DEFAULT_PROTOCOL} is returned if no protocol has been
288      *    configured
289      */
290     public String getProtocol() {
291         if (protocol == null) {
292             return SSL.DEFAULT_PROTOCOL;
293         }
294         return protocol;
295     }
296 
297     /**
298      * Sets the secure transport protocol name.
299      * @param protocol a protocol name, which must be recognized by the provider
300      *    specified by {@link #setProvider(String)} or by the platform's
301      *    default provider if no platform was specified.
302      */
303     public void setProtocol(String protocol) {
304         this.protocol = protocol;
305     }
306 
307     /**
308      * Gets the JSSE provider name for the SSL context.
309      * @return JSSE provider name
310      */
311     public String getProvider() {
312         return provider;
313     }
314 
315     /**
316      * Sets the JSSE provider name for the SSL context.
317      * @param provider name of the JSSE provider to use in creating the
318      *    SSL context
319      */
320     public void setProvider(String provider) {
321         this.provider = provider;
322     }
323 
324 }