View Javadoc

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