View Javadoc

1   /*
2    * $Id: TlsConfiguration.java 10489 2008-01-23 17:53:38Z dfeist $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.api.security.tls;
12  
13  import org.mule.api.lifecycle.CreateException;
14  import org.mule.api.security.TlsDirectKeyStore;
15  import org.mule.api.security.TlsDirectTrustStore;
16  import org.mule.api.security.TlsIndirectKeyStore;
17  import org.mule.api.security.TlsProtocolHandler;
18  import org.mule.api.security.provider.AutoDiscoverySecurityProviderFactory;
19  import org.mule.api.security.provider.SecurityProviderFactory;
20  import org.mule.api.security.provider.SecurityProviderInfo;
21  import org.mule.config.i18n.CoreMessages;
22  import org.mule.util.FileUtils;
23  import org.mule.util.IOUtils;
24  
25  import java.io.FileNotFoundException;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.security.KeyManagementException;
29  import java.security.KeyStore;
30  import java.security.NoSuchAlgorithmException;
31  import java.security.Provider;
32  import java.security.Security;
33  
34  import javax.net.ssl.KeyManager;
35  import javax.net.ssl.KeyManagerFactory;
36  import javax.net.ssl.SSLContext;
37  import javax.net.ssl.SSLServerSocketFactory;
38  import javax.net.ssl.SSLSocketFactory;
39  import javax.net.ssl.TrustManager;
40  import javax.net.ssl.TrustManagerFactory;
41  
42  import org.apache.commons.logging.Log;
43  import org.apache.commons.logging.LogFactory;
44  
45  /**
46   * Support for configuring TLS/SSL connections.
47   * <p/>
48   * <h2>Introduction</h2>
49   * <p/>
50   * This class was introduced to centralise the work of TLS/SSL configuration.  It is intended
51   * to be backwards compatible with earlier code (as much as possible) and so is perhaps more
52   * complex than would be necessary if starting from zero - the main source of confusion is the
53   * distinction between direct and indirect creation of sockets and stores.
54   * <p/>
55   * <h2>Configuration</h2>
56   * <p/>
57   * The documentation in this class is intended more for programmers than end uses.  If you are
58   * configuring a connector the interfaces {@link org.mule.api.security.TlsIndirectTrustStore},
59   * {@link TlsDirectTrustStore},
60   * {@link TlsDirectKeyStore} and {@link TlsIndirectKeyStore} should provide guidance to individual
61   * properties.  In addition you should check the documentation for the specific protocol / connector
62   * used and may also need to read the discussion on direct and indirect socket and store creation
63   * below (or, more simply, just use whichever key store interface your connector implements!).
64   * <p/>
65   * <h2>Programming</h2>
66   * <p/>
67   * This class is intended to be used as a delegate as we typically want to add security to an
68   * already existing connector (so we inherit from that connector, implement the appropriate
69   * interfaces from {@link org.mule.api.security.TlsIndirectTrustStore}, {@link TlsDirectTrustStore},
70   * {@link TlsDirectKeyStore} and {@link TlsIndirectKeyStore}, and then forward calls to the
71   * interfaces to an instance of this class).
72   * <p/>
73   * <p>For setting System properties (and reading them) use {@link TlsPropertiesMapper}.  This
74   * can take a "namespace" which can then be used by {@link TlsPropertiesSocketFactory} to
75   * construct an appropriate socket factory.  This approach (storing to proeprties and then
76   * retrieving that information later in a socket factory) lets us pass TLS/SSL configuration
77   * into libraries that are configured by specifying on the socket factory class.</p>
78   * <p/>
79   * <h2>Direct and Indirect Socket and Store Creation</h2>
80   * <p/>
81   * For the SSL transport, which historically defined parameters for many different secure
82   * transports, the configuration interfaces worked as follows:
83   * <p/>
84   * <dl>
85   * <dt>{@link TlsDirectTrustStore}</dt><dd>Used to generate trust store directly and indirectly
86   * for all TLS/SSL conections via System properties</dd>
87   * <dt>{@link TlsDirectKeyStore}</dt><dd>Used to generate key store directly</dd>
88   * <dt>{@link TlsIndirectKeyStore}</dt><dd>Used to generate key store indirectly for all
89   * TLS/SSL conections via System properties</dd>
90   * </dl>
91   * <p/>
92   * Historically, many other transports relied on the indirect configurations defined above.
93   * So they implemented {@link org.mule.api.security.TlsIndirectTrustStore}
94   * (a superclass of {@link TlsDirectTrustStore})
95   * and relied on {@link TlsIndirectKeyStore} from the SSL configuration.  For continuity these
96   * interfaces continue to be used, even though
97   * the configurations are now typically (see individual connector/protocol documentation) specific
98   * to a protocol or connector.  <em>Note - these interfaces are new, but the original code had
99   * those methods, used as described.  The new interfaces only make things explicit.</em>
100  * <p/>
101  * <p><em>Note for programmers</em> One way to understand the above is to see that many
102  * protocols are handled by libraries that are configured by providing either properties or
103  * a socket factory.  In both cases (the latter via {@link TlsPropertiesSocketFactory}) we
104  * continue to use properties and the "indirect" interface.  Note also that the mapping
105  * in {@link TlsPropertiesMapper} correctly handles the asymmetry, so an initial call to
106  * {@link TlsConfiguration} uses the keystore defined via {@link TlsDirectKeyStore}, but
107  * when a {@link TlsConfiguration} is retrieved from System proerties using
108  * {@link TlsPropertiesMapper#readFromProperties(TlsConfiguration,java.util.Properties)}
109  * the "indirect" properties are supplied as "direct" values, meaning that the "indirect"
110  * socket factory can be retrieved from {@link #getKeyManagerFactory()}.  It just works.</p>
111  */
112 public final class TlsConfiguration
113         implements TlsDirectTrustStore, TlsDirectKeyStore, TlsIndirectKeyStore, TlsProtocolHandler
114 {
115 
116     public static final String DEFAULT_KEYSTORE = ".keystore";
117     public static final String DEFAULT_KEYSTORE_TYPE = KeyStore.getDefaultType();
118     public static final String DEFAULT_SSL_TYPE = "SSLv3";
119     public static final String JSSE_NAMESPACE = "javax.net";
120 
121     private Log logger = LogFactory.getLog(getClass());
122 
123     private SecurityProviderFactory spFactory = new AutoDiscoverySecurityProviderFactory();
124     private SecurityProviderInfo spInfo = spFactory.getSecurityProviderInfo();
125     private Provider provider = spFactory.getProvider();
126     private String sslType = DEFAULT_SSL_TYPE;
127 
128     // global
129     private String protocolHandler = spInfo.getProtocolHandler();
130 
131     // this is the key store that is generated in-memory and available to connectors explicitly.
132     // it is local to the socket.
133     private String keyStoreName = DEFAULT_KEYSTORE; // was default in https but not ssl
134     private String keyPassword = null;
135     private String keyStorePassword = null;
136     private String keystoreType = DEFAULT_KEYSTORE_TYPE;
137     private String keyManagerAlgorithm = spInfo.getKeyManagerAlgorithm();
138     private KeyManagerFactory keyManagerFactory = null;
139 
140     // this is the key store defined in system properties that is used implicitly.
141     // note that some transports use different namespaces within system properties,
142     // so this is typically global across a particular transport.
143     // it is also used as the trust store defined in system properties if no other trust
144     // store is given and explicitTrustStoreOnly is false
145     private String clientKeyStoreName = null;
146     private String clientKeyStorePassword = null;
147     private String clientKeyStoreType = DEFAULT_KEYSTORE_TYPE;
148 
149     // this is the trust store used to construct sockets both explicitly
150     // and globally (if not set, see client key above) via the jvm defaults.
151     private String trustStoreName = null;
152     private String trustStorePassword = null;
153     private String trustStoreType = DEFAULT_KEYSTORE_TYPE;
154     private String trustManagerAlgorithm = spInfo.getKeyManagerAlgorithm();
155     private TrustManagerFactory trustManagerFactory = null;
156     private boolean explicitTrustStoreOnly = false;
157     private boolean requireClientAuthentication = false;
158 
159 
160     /**
161      * Support for TLS connections with a given initial value for the key store
162      *
163      * @param keyStore initial value for the key store
164      */
165     public TlsConfiguration(String keyStore)
166     {
167         this.keyStoreName = keyStore;
168     }
169 
170     // note - in what follows i'm using "raw" variables rather than accessors because
171     // i think the names are clearer.  the API names for the accessors are historical
172     // and not a close fit to actual use (imho).
173 
174     /**
175      * @param anon      If the connection is anonymous then we don't care about client keys
176      * @param namespace Namespace to use for global properties (for JSSE use JSSE_NAMESPACE)
177      * @throws CreateException ON initialisation problems
178      */
179     public void initialise(boolean anon, String namespace) throws CreateException
180     {
181         if (logger.isDebugEnabled())
182         {
183             logger.debug("initialising: anon " + anon);
184         }
185         validate(anon);
186 
187         Security.addProvider(provider);
188         System.setProperty("java.protocol.handler.pkgs", protocolHandler);
189 
190         if (!anon)
191         {
192             initKeyManagerFactory();
193         }
194         initTrustManagerFactory();
195 
196         if (null != namespace)
197         {
198             new TlsPropertiesMapper(namespace).writeToProperties(System.getProperties(), this);
199         }
200     }
201 
202     private void validate(boolean anon) throws CreateException
203     {
204         assertNotNull(getProvider(), "The security provider cannot be null");
205         if (!anon)
206         {
207             assertNotNull(getKeyStore(), "The KeyStore location cannot be null");
208             assertNotNull(getKeyPassword(), "The Key password cannot be null");
209             assertNotNull(getKeyStorePassword(), "The KeyStore password cannot be null");
210             assertNotNull(getKeyManagerAlgorithm(), "The Key Manager Algorithm cannot be null");
211         }
212     }
213 
214     private void initKeyManagerFactory() throws CreateException
215     {
216         if (logger.isDebugEnabled())
217         {
218             logger.debug("initialising key manager factory from keystore data");
219         }
220         KeyStore tempKeyStore;
221         try
222         {
223             tempKeyStore = KeyStore.getInstance(keystoreType);
224             InputStream is = IOUtils.getResourceAsStream(keyStoreName, getClass());
225             if (null == is)
226             {
227                 throw new FileNotFoundException(
228                         CoreMessages.cannotLoadFromClasspath("Keystore: " + keyStoreName).getMessage());
229             }
230             tempKeyStore.load(is, keyStorePassword.toCharArray());
231         }
232         catch (Exception e)
233         {
234             throw new CreateException(
235                     CoreMessages.failedToLoad("KeyStore: " + keyStoreName), e, this);
236         }
237         try
238         {
239             keyManagerFactory = KeyManagerFactory.getInstance(getKeyManagerAlgorithm());
240             keyManagerFactory.init(tempKeyStore, keyPassword.toCharArray());
241         }
242         catch (Exception e)
243         {
244             throw new CreateException(CoreMessages.failedToLoad("Key Manager"), e, this);
245         }
246     }
247 
248     private void initTrustManagerFactory() throws CreateException
249     {
250         if (null != trustStoreName)
251         {
252             trustStorePassword = null == trustStorePassword ? "" : trustStorePassword;
253 
254             KeyStore trustStore;
255             try
256             {
257                 trustStore = KeyStore.getInstance(trustStoreType);
258                 InputStream is = IOUtils.getResourceAsStream(trustStoreName, getClass());
259                 if (null == is)
260                 {
261                     throw new FileNotFoundException(
262                             "Failed to load truststore from classpath or local file: " + trustStoreName);
263                 }
264                 trustStore.load(is, trustStorePassword.toCharArray());
265             }
266             catch (Exception e)
267             {
268                 throw new CreateException(
269                         CoreMessages.failedToLoad("TrustStore: " + trustStoreName), e, this);
270             }
271 
272             try
273             {
274                 trustManagerFactory = TrustManagerFactory.getInstance(trustManagerAlgorithm);
275                 trustManagerFactory.init(trustStore);
276             }
277             catch (Exception e)
278             {
279                 throw new CreateException(
280                         CoreMessages.failedToLoad("Trust Manager (" + trustManagerAlgorithm + ")"), e, this);
281             }
282         }
283     }
284 
285 
286     private static void assertNotNull(Object value, String message)
287     {
288         if (null == value)
289         {
290             throw new IllegalArgumentException(message);
291         }
292     }
293 
294     private static String defaultForNull(String value, String deflt)
295     {
296         if (null == value)
297         {
298             return deflt;
299         }
300         else
301         {
302             return value;
303         }
304     }
305 
306 
307     public SSLSocketFactory getSocketFactory() throws NoSuchAlgorithmException, KeyManagementException
308     {
309         return getSslContext().getSocketFactory();
310     }
311 
312     public SSLServerSocketFactory getServerSocketFactory()
313             throws NoSuchAlgorithmException, KeyManagementException
314     {
315         return getSslContext().getServerSocketFactory();
316     }
317 
318     public SSLContext getSslContext() throws NoSuchAlgorithmException, KeyManagementException
319     {
320         KeyManager[] keyManagers =
321                 null == getKeyManagerFactory() ? null : getKeyManagerFactory().getKeyManagers();
322         TrustManager[] trustManagers =
323                 null == getTrustManagerFactory() ? null : getTrustManagerFactory().getTrustManagers();
324 
325         SSLContext context = SSLContext.getInstance(getSslType());
326         // TODO - nice to have a configurable random number source set here
327         context.init(keyManagers, trustManagers, null);
328         return context;
329     }
330 
331 
332     public String getSslType()
333     {
334         return sslType;
335     }
336 
337     public void setSslType(String sslType)
338     {
339         this.sslType = sslType;
340     }
341 
342     public Provider getProvider()
343     {
344         return provider;
345     }
346 
347     public void setProvider(Provider provider)
348     {
349         this.provider = provider;
350     }
351 
352     public String getProtocolHandler()
353     {
354         return protocolHandler;
355     }
356 
357     public void setProtocolHandler(String protocolHandler)
358     {
359         this.protocolHandler = protocolHandler;
360     }
361 
362     public SecurityProviderFactory getSecurityProviderFactory()
363     {
364         return spFactory;
365     }
366 
367     public void setSecurityProviderFactory(SecurityProviderFactory spFactory)
368     {
369         this.spFactory = spFactory;
370     }
371 
372     // access to the explicit key store variables
373 
374     public String getKeyStore()
375     {
376         return keyStoreName;
377     }
378 
379     public void setKeyStore(String name) throws IOException
380     {
381         keyStoreName = name;
382         if (null != keyStoreName)
383         {
384             keyStoreName = FileUtils.getResourcePath(keyStoreName, getClass());
385             if (logger.isDebugEnabled())
386             {
387                 logger.debug("Normalised keyStore path to: " + keyStoreName);
388             }
389         }
390     }
391 
392     public String getKeyPassword()
393     {
394         return keyPassword;
395     }
396 
397     public void setKeyPassword(String keyPassword)
398     {
399         this.keyPassword = keyPassword;
400     }
401 
402     public String getKeyStorePassword()
403     {
404         return keyStorePassword;
405     }
406 
407     public void setKeyStorePassword(String storePassword)
408     {
409         this.keyStorePassword = storePassword;
410     }
411 
412     public String getKeyStoreType()
413     {
414         return keystoreType;
415     }
416 
417     public void setKeyStoreType(String keystoreType)
418     {
419         this.keystoreType = keystoreType;
420     }
421 
422     public String getKeyManagerAlgorithm()
423     {
424         return keyManagerAlgorithm;
425     }
426 
427     public void setKeyManagerAlgorithm(String keyManagerAlgorithm)
428     {
429         this.keyManagerAlgorithm = keyManagerAlgorithm;
430     }
431 
432     public KeyManagerFactory getKeyManagerFactory()
433     {
434         return keyManagerFactory;
435     }
436 
437     // access to the implicit key store variables
438 
439     public String getClientKeyStore()
440     {
441         return clientKeyStoreName;
442     }
443 
444     public void setClientKeyStore(String name) throws IOException
445     {
446         clientKeyStoreName = name;
447         if (null != clientKeyStoreName)
448         {
449             clientKeyStoreName = FileUtils.getResourcePath(clientKeyStoreName, getClass());
450             if (logger.isDebugEnabled())
451             {
452                 logger.debug("Normalised clientKeyStore path to: " + clientKeyStoreName);
453             }
454         }
455     }
456 
457     public String getClientKeyStorePassword()
458     {
459         return clientKeyStorePassword;
460     }
461 
462     public void setClientKeyStorePassword(String clientKeyStorePassword)
463     {
464         this.clientKeyStorePassword = clientKeyStorePassword;
465     }
466 
467     public void setClientKeyStoreType(String clientKeyStoreType)
468     {
469         this.clientKeyStoreType = clientKeyStoreType;
470     }
471 
472     public String getClientKeyStoreType()
473     {
474         return clientKeyStoreType;
475     }
476 
477     // access to trust store variables
478 
479     public String getTrustStore()
480     {
481         return trustStoreName;
482     }
483 
484     public void setTrustStore(String name) throws IOException
485     {
486         trustStoreName = name;
487         if (null != trustStoreName)
488         {
489             trustStoreName = FileUtils.getResourcePath(trustStoreName, getClass());
490             if (logger.isDebugEnabled())
491             {
492                 logger.debug("Normalised trustStore path to: " + trustStoreName);
493             }
494         }
495     }
496 
497     public String getTrustStorePassword()
498     {
499         return trustStorePassword;
500     }
501 
502     public void setTrustStorePassword(String trustStorePassword)
503     {
504         this.trustStorePassword = trustStorePassword;
505     }
506 
507     public String getTrustStoreType()
508     {
509         return trustStoreType;
510     }
511 
512     public void setTrustStoreType(String trustStoreType)
513     {
514         this.trustStoreType = trustStoreType;
515     }
516 
517     public String getTrustManagerAlgorithm()
518     {
519         return trustManagerAlgorithm;
520     }
521 
522     public void setTrustManagerAlgorithm(String trustManagerAlgorithm)
523     {
524         this.trustManagerAlgorithm = defaultForNull(trustManagerAlgorithm, spInfo.getKeyManagerAlgorithm());
525     }
526 
527     public TrustManagerFactory getTrustManagerFactory()
528     {
529         return trustManagerFactory;
530     }
531 
532     public void setTrustManagerFactory(TrustManagerFactory trustManagerFactory)
533     {
534         this.trustManagerFactory = trustManagerFactory;
535     }
536 
537     public boolean isExplicitTrustStoreOnly()
538     {
539         return explicitTrustStoreOnly;
540     }
541 
542     public void setExplicitTrustStoreOnly(boolean explicitTrustStoreOnly)
543     {
544         this.explicitTrustStoreOnly = explicitTrustStoreOnly;
545     }
546 
547     public boolean isRequireClientAuthentication()
548     {
549         return requireClientAuthentication;
550     }
551 
552     public void setRequireClientAuthentication(boolean requireClientAuthentication)
553     {
554         this.requireClientAuthentication = requireClientAuthentication;
555     }
556 
557 }
558 
559