View Javadoc

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