Coverage Report - org.mule.umo.security.tls.TlsConfiguration
 
Classes in this File Line Coverage Branch Coverage Complexity
TlsConfiguration
65%
104/159
47%
18/38
1.571
 
 1  
 /*
 2  
  * $Id: TlsConfiguration.java 7963 2007-08-21 08:53:15Z 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  2
     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  8
     private Log logger = LogFactory.getLog(getClass());
 120  
 
 121  8
     private SecurityProviderFactory spFactory = new AutoDiscoverySecurityProviderFactory();
 122  8
     private SecurityProviderInfo spInfo = spFactory.getSecurityProviderInfo();
 123  8
     private Provider provider = spFactory.getProvider();
 124  8
     private String sslType = DEFAULT_SSL_TYPE;
 125  
 
 126  
     // global
 127  8
     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  8
     private String keyStoreName = DEFAULT_KEYSTORE; // was default in https but not ssl
 132  8
     private String keyPassword = null;
 133  8
     private String keyStorePassword = null;
 134  8
     private String keystoreType = DEFAULT_KEYSTORE_TYPE;
 135  8
     private String keyManagerAlgorithm = spInfo.getKeyManagerAlgorithm();
 136  8
     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  8
     private String clientKeyStoreName = null;
 144  8
     private String clientKeyStorePassword = null;
 145  8
     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  8
     private String trustStoreName = null;
 150  8
     private String trustStorePassword = null;
 151  8
     private String trustStoreType = DEFAULT_KEYSTORE_TYPE;
 152  8
     private String trustManagerAlgorithm = spInfo.getKeyManagerAlgorithm();
 153  8
     private TrustManagerFactory trustManagerFactory = null;
 154  8
     private boolean explicitTrustStoreOnly = false;
 155  8
     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  8
     {
 164  8
         this.keyStoreName = keyStore;
 165  8
     }
 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  12
         if (logger.isDebugEnabled())
 179  
         {
 180  0
             logger.debug("initialising: anon " + anon);
 181  
         }
 182  12
         validate(anon);
 183  
 
 184  8
         Security.addProvider(provider);
 185  8
         System.setProperty("java.protocol.handler.pkgs", protocolHandler);
 186  
 
 187  8
         if (!anon) 
 188  
         {
 189  6
             initKeyManagerFactory();
 190  
         }
 191  6
         initTrustManagerFactory();
 192  
 
 193  6
         if (null != namespace)
 194  
         {
 195  6
             new TlsPropertiesMapper(namespace).writeToProperties(System.getProperties(), this);
 196  
         }
 197  6
     }
 198  
 
 199  
     private void validate(boolean anon) throws InitialisationException
 200  
     {
 201  12
         assertNotNull(getProvider(), "The security provider cannot be null");
 202  12
         if (!anon)
 203  
         {
 204  10
             assertNotNull(getKeyStore(), "The KeyStore location cannot be null");
 205  10
             assertNotNull(getKeyPassword(), "The Key password cannot be null");
 206  8
             assertNotNull(getStorePassword(), "The KeyStore password cannot be null");
 207  6
             assertNotNull(getKeyManagerAlgorithm(), "The Key Manager Algorithm cannot be null");
 208  
         }
 209  8
     }
 210  
 
 211  
     private void initKeyManagerFactory() throws InitialisationException
 212  
     {
 213  6
         if (logger.isDebugEnabled())
 214  
         {
 215  0
             logger.debug("initialising key manager factory from keystore data");
 216  
         }
 217  
         KeyStore tempKeyStore;
 218  
         try
 219  
         {
 220  6
             tempKeyStore = KeyStore.getInstance(keystoreType);
 221  6
             InputStream is = IOUtils.getResourceAsStream(keyStoreName, getClass());
 222  6
             if (null == is)
 223  
             {
 224  0
                 throw new FileNotFoundException(
 225  
                     CoreMessages.cannotLoadFromClasspath("Keystore: " + keyStoreName).getMessage());
 226  
             }
 227  6
             tempKeyStore.load(is, keyStorePassword.toCharArray());
 228  
         }
 229  2
         catch (Exception e)
 230  
         {
 231  2
             throw new InitialisationException(
 232  
                 CoreMessages.failedToLoad("KeyStore: " + keyStoreName), e, this);
 233  4
         }
 234  
         try
 235  
         {
 236  4
             keyManagerFactory = KeyManagerFactory.getInstance(getKeyManagerAlgorithm());
 237  4
             keyManagerFactory.init(tempKeyStore, keyPassword.toCharArray());
 238  
         }
 239  0
         catch (Exception e)
 240  
         {
 241  0
             throw new InitialisationException(CoreMessages.failedToLoad("Key Manager"), e, this);
 242  4
         }
 243  4
     }
 244  
 
 245  
     private void initTrustManagerFactory() throws InitialisationException 
 246  
     {
 247  6
         if (null != trustStoreName)
 248  
         {
 249  0
             trustStorePassword = null == trustStorePassword ? "" : trustStorePassword;
 250  
 
 251  
             KeyStore trustStore;
 252  
             try
 253  
             {
 254  0
                 trustStore = KeyStore.getInstance(trustStoreType);
 255  0
                 InputStream is = IOUtils.getResourceAsStream(trustStoreName, getClass());
 256  0
                 if (null == is)
 257  
                 {
 258  0
                     throw new FileNotFoundException(
 259  
                         "Failed to load truststore from classpath or local file: " + trustStoreName);
 260  
                 }
 261  0
                 trustStore.load(is, trustStorePassword.toCharArray());
 262  
             }
 263  0
             catch (Exception e)
 264  
             {
 265  0
                 throw new InitialisationException(
 266  
                     CoreMessages.failedToLoad("TrustStore: " + trustStoreName), e, this);
 267  0
             }
 268  
 
 269  
             try
 270  
             {
 271  0
                 trustManagerFactory = TrustManagerFactory.getInstance(trustManagerAlgorithm);
 272  0
                 trustManagerFactory.init(trustStore);
 273  
             }
 274  0
             catch (Exception e)
 275  
             {
 276  0
                 throw new InitialisationException(
 277  
                     CoreMessages.failedToLoad("Trust Manager (" + trustManagerAlgorithm + ")"), e, this);
 278  0
             }
 279  
         }
 280  6
     }
 281  
 
 282  
 
 283  
     private static void assertNotNull(Object value, String message)
 284  
     {
 285  46
         if (null == value)
 286  
         {
 287  4
             throw new IllegalArgumentException(message);
 288  
         }
 289  42
     }
 290  
 
 291  
     private static String defaultForNull(String value, String deflt)
 292  
     {
 293  2
         if (null == value)
 294  
         {
 295  0
             return deflt;
 296  
         }
 297  
         else
 298  
         {
 299  2
             return value;
 300  
         }
 301  
     }
 302  
 
 303  
 
 304  
     public SSLSocketFactory getSocketFactory() throws NoSuchAlgorithmException, KeyManagementException
 305  
     {
 306  4
         return getSslContext().getSocketFactory();
 307  
     }
 308  
 
 309  
     public SSLServerSocketFactory getServerSocketFactory()
 310  
             throws NoSuchAlgorithmException, KeyManagementException
 311  
     {
 312  0
         return getSslContext().getServerSocketFactory();
 313  
     }
 314  
 
 315  
     public SSLContext getSslContext() throws NoSuchAlgorithmException, KeyManagementException
 316  
     {
 317  4
         KeyManager[] keyManagers =
 318  
             null == getKeyManagerFactory() ? null : getKeyManagerFactory().getKeyManagers();
 319  4
         TrustManager[] trustManagers =
 320  
             null == getTrustManagerFactory() ? null :  getTrustManagerFactory().getTrustManagers();
 321  
 
 322  4
         SSLContext context = SSLContext.getInstance(getSslType());
 323  
         // TODO - nice to have a configurable random number source set here
 324  4
         context.init(keyManagers, trustManagers, null);
 325  4
         return context;
 326  
     }
 327  
 
 328  
 
 329  
     public String getSslType()
 330  
     {
 331  4
         return sslType;
 332  
     }
 333  
 
 334  
     public void setSslType(String sslType)
 335  
     {
 336  0
         this.sslType = sslType;
 337  0
     }
 338  
 
 339  
     public Provider getProvider()
 340  
     {
 341  12
         return provider;
 342  
     }
 343  
 
 344  
     public void setProvider(Provider provider)
 345  
     {
 346  0
         this.provider = provider;
 347  0
     }
 348  
 
 349  
     public String getProtocolHandler()
 350  
     {
 351  0
         return protocolHandler;
 352  
     }
 353  
 
 354  
     public void setProtocolHandler(String protocolHandler)
 355  
     {
 356  0
         this.protocolHandler = protocolHandler;
 357  0
     }
 358  
 
 359  
     public SecurityProviderFactory getSecurityProviderFactory()
 360  
     {
 361  0
         return spFactory;
 362  
     }
 363  
 
 364  
     public void setSecurityProviderFactory(SecurityProviderFactory spFactory)
 365  
     {
 366  0
         this.spFactory = spFactory;
 367  0
     }
 368  
 
 369  
 
 370  
     // access to the explicit key store variables
 371  
 
 372  
     public String getKeyStore()
 373  
     {
 374  12
         return keyStoreName;
 375  
     }
 376  
 
 377  
     public void setKeyStore(String name) throws IOException
 378  
     {
 379  8
         keyStoreName = name;
 380  8
         if (null != keyStoreName)
 381  
         {
 382  8
             keyStoreName = FileUtils.getResourcePath(keyStoreName, getClass());
 383  8
             if (logger.isDebugEnabled())
 384  
             {
 385  0
                 logger.debug("Normalised keyStore path to: " + keyStoreName);
 386  
             }
 387  
         }
 388  8
     }
 389  
 
 390  
     public String getKeyPassword()
 391  
     {
 392  10
         return keyPassword;
 393  
     }
 394  
 
 395  
     public void setKeyPassword(String keyPassword)
 396  
     {
 397  6
         this.keyPassword = keyPassword;
 398  6
     }
 399  
 
 400  
     public String getStorePassword()
 401  
     {
 402  10
         return keyStorePassword;
 403  
     }
 404  
 
 405  
     public void setStorePassword(String storePassword)
 406  
     {
 407  8
         this.keyStorePassword = storePassword;
 408  8
     }
 409  
 
 410  
     public String getKeystoreType()
 411  
     {
 412  2
         return keystoreType;
 413  
     }
 414  
 
 415  
     public void setKeystoreType(String keystoreType)
 416  
     {
 417  2
         this.keystoreType = keystoreType;
 418  2
     }
 419  
 
 420  
     public String getKeyManagerAlgorithm()
 421  
     {
 422  10
         return keyManagerAlgorithm;
 423  
     }
 424  
 
 425  
     public void setKeyManagerAlgorithm(String keyManagerAlgorithm)
 426  
     {
 427  0
         this.keyManagerAlgorithm = keyManagerAlgorithm;
 428  0
     }
 429  
 
 430  
     public KeyManagerFactory getKeyManagerFactory()
 431  
     {
 432  6
         return keyManagerFactory;
 433  
     }
 434  
 
 435  
 
 436  
     // access to the implicit key store variables
 437  
 
 438  
     public String getClientKeyStore()
 439  
     {
 440  12
         return clientKeyStoreName;
 441  
     }
 442  
 
 443  
     public void setClientKeyStore(String name) throws IOException
 444  
     {
 445  0
         clientKeyStoreName = name;
 446  0
         if (null != clientKeyStoreName)
 447  
         {
 448  0
             clientKeyStoreName = FileUtils.getResourcePath(clientKeyStoreName, getClass());
 449  0
             if (logger.isDebugEnabled())
 450  
             {
 451  0
                 logger.debug("Normalised clientKeyStore path to: " + clientKeyStoreName);
 452  
             }
 453  
         }
 454  0
     }
 455  
 
 456  
     public String getClientKeyStorePassword()
 457  
     {
 458  6
         return clientKeyStorePassword;
 459  
     }
 460  
 
 461  
     public void setClientKeyStorePassword(String clientKeyStorePassword)
 462  
     {
 463  0
         this.clientKeyStorePassword = clientKeyStorePassword;
 464  0
     }
 465  
 
 466  
     public void setClientKeyStoreType(String clientKeyStoreType)
 467  
     {
 468  0
         this.clientKeyStoreType = clientKeyStoreType;
 469  0
     }
 470  
 
 471  
     public String getClientKeyStoreType()
 472  
     {
 473  0
         return clientKeyStoreType;
 474  
     }
 475  
 
 476  
 
 477  
     // access to trust store variables
 478  
 
 479  
     public String getTrustStore()
 480  
     {
 481  8
         return trustStoreName;
 482  
     }
 483  
 
 484  
     public void setTrustStore(String name) throws IOException
 485  
     {
 486  2
         trustStoreName = name;
 487  2
         if (null != trustStoreName)
 488  
         {
 489  0
             trustStoreName = FileUtils.getResourcePath(trustStoreName, getClass());
 490  0
             if (logger.isDebugEnabled())
 491  
             {
 492  0
                 logger.debug("Normalised trustStore path to: " + trustStoreName);
 493  
             }
 494  
         }
 495  2
     }
 496  
 
 497  
     public String getTrustStorePassword()
 498  
     {
 499  8
         return trustStorePassword;
 500  
     }
 501  
 
 502  
     public void setTrustStorePassword(String trustStorePassword)
 503  
     {
 504  2
         this.trustStorePassword = trustStorePassword;
 505  2
     }
 506  
 
 507  
     public String getTrustStoreType()
 508  
     {
 509  2
         return trustStoreType;
 510  
     }
 511  
 
 512  
     public void setTrustStoreType(String trustStoreType)
 513  
     {
 514  2
         this.trustStoreType = trustStoreType;
 515  2
     }
 516  
 
 517  
     public String getTrustManagerAlgorithm()
 518  
     {
 519  2
         return trustManagerAlgorithm;
 520  
     }
 521  
 
 522  
     public void setTrustManagerAlgorithm(String trustManagerAlgorithm)
 523  
     {
 524  2
         this.trustManagerAlgorithm = defaultForNull(trustManagerAlgorithm, spInfo.getKeyManagerAlgorithm());
 525  2
     }
 526  
 
 527  
     public TrustManagerFactory getTrustManagerFactory()
 528  
     {
 529  4
         return trustManagerFactory;
 530  
     }
 531  
 
 532  
     public void setTrustManagerFactory(TrustManagerFactory trustManagerFactory)
 533  
     {
 534  0
         this.trustManagerFactory = trustManagerFactory;
 535  0
     }
 536  
 
 537  
     public boolean isExplicitTrustStoreOnly()
 538  
     {
 539  6
         return explicitTrustStoreOnly;
 540  
     }
 541  
 
 542  
     public void setExplicitTrustStoreOnly(boolean explicitTrustStoreOnly)
 543  
     {
 544  0
         this.explicitTrustStoreOnly = explicitTrustStoreOnly;
 545  0
     }
 546  
 
 547  
     public boolean isRequireClientAuthentication()
 548  
     {
 549  0
         return requireClientAuthentication;
 550  
     }
 551  
 
 552  
     public void setRequireClientAuthentication(boolean requireClientAuthentication)
 553  
     {
 554  0
         this.requireClientAuthentication = requireClientAuthentication;
 555  0
     }
 556  
 
 557  
 }
 558  
 
 559