001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.core.net.ssl;
015
016import java.security.KeyManagementException;
017import java.security.KeyStore;
018import java.security.KeyStoreException;
019import java.security.NoSuchAlgorithmException;
020import java.security.NoSuchProviderException;
021import java.security.SecureRandom;
022import java.security.UnrecoverableKeyException;
023import java.security.cert.CertificateException;
024
025import javax.net.ssl.KeyManager;
026import javax.net.ssl.KeyManagerFactory;
027import javax.net.ssl.SSLContext;
028import javax.net.ssl.TrustManager;
029import javax.net.ssl.TrustManagerFactory;
030
031import ch.qos.logback.core.spi.ContextAware;
032
033/**
034 * A factory bean for a JSSE {@link SSLContext}.
035 * <p>
036 * This object holds the configurable properties for an SSL context and uses
037 * them to create an {@link SSLContext} instance.
038 *
039 * @author Carl Harris
040 */
041public class SSLContextFactoryBean {
042
043    private static final String JSSE_KEY_STORE_PROPERTY = "javax.net.ssl.keyStore";
044    private static final String JSSE_TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore";
045
046    private KeyStoreFactoryBean keyStore;
047    private KeyStoreFactoryBean trustStore;
048    private SecureRandomFactoryBean secureRandom;
049    private KeyManagerFactoryFactoryBean keyManagerFactory;
050    private TrustManagerFactoryFactoryBean trustManagerFactory;
051    private String protocol;
052    private String provider;
053
054    /**
055     * Creates a new {@link SSLContext} using the receiver's configuration.
056     * 
057     * @param context context for status messages
058     * @return {@link SSLContext} object
059     * @throws NoSuchProviderException   if a provider specified for one of the JCA
060     *                                   or JSSE components utilized in creating the
061     *                                   context is not known to the platform
062     * @throws NoSuchAlgorithmException  if a JCA or JSSE algorithm, protocol, or
063     *                                   type name specified for one of the
064     *                                   context's components is not known to a
065     *                                   given provider (or platform default
066     *                                   provider for the component)
067     * @throws KeyManagementException    if an error occurs in creating a
068     *                                   {@link KeyManager} for the context
069     * @throws UnrecoverableKeyException if a private key needed by a
070     *                                   {@link KeyManager} cannot be obtained from
071     *                                   a key store
072     * @throws KeyStoreException         if an error occurs in reading the contents
073     *                                   of a key store
074     * @throws CertificateException      if an error occurs in reading the contents
075     *                                   of a certificate
076     */
077    public SSLContext createContext(ContextAware context) throws NoSuchProviderException, NoSuchAlgorithmException,
078            KeyManagementException, UnrecoverableKeyException, KeyStoreException, CertificateException {
079
080        SSLContext sslContext = getProvider() != null ? SSLContext.getInstance(getProtocol(), getProvider())
081                : SSLContext.getInstance(getProtocol());
082
083        context.addInfo("SSL protocol '" + sslContext.getProtocol() + "' provider '" + sslContext.getProvider() + "'");
084
085        KeyManager[] keyManagers = createKeyManagers(context);
086        TrustManager[] trustManagers = createTrustManagers(context);
087        SecureRandom secureRandom = createSecureRandom(context);
088        sslContext.init(keyManagers, trustManagers, secureRandom);
089        return sslContext;
090    }
091
092    /**
093     * Creates key managers using the receiver's key store configuration.
094     * 
095     * @param context context for status messages
096     * @return an array of key managers or {@code null} if no key store
097     *         configuration was provided
098     * @throws NoSuchProviderException  if a provider specified for one of the key
099     *                                  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}