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 }