/** * <code>TcpConnector</code> can bind or sent to a given tcp port on a given host. * * @version $Revision$ */ public class SslConnector extends TcpConnector { public static final String DEFAULT_KEYSTORE_TYPE = KeyStore.getDefaultType(); private SecurityProviderFactory spFactory = new AutoDiscoverySecurityProviderFactory(); private SecurityProviderInfo spInfo = spFactory.getSecurityProviderInfo(); private String keyStore = null; private String keyPassword = null; private String storePassword = null; private String keyStoreType = DEFAULT_KEYSTORE_TYPE; private String keyManagerAlgorithm = spInfo.getKeyManagerAlgorithm(); private Provider provider = spFactory.getProvider(); private String protocolHandler = spInfo.getProtocolHandler(); private String clientKeyStore = null; private String clientKeyStorePassword = null; private String trustStore = null; private String trustStorePassword = null; private String trustStoreType = DEFAULT_KEYSTORE_TYPE; // default to key manager algorithm, overridable private String trustManagerAlgorithm = spInfo.getKeyManagerAlgorithm(); private TrustManagerFactory trustManagerFactory; private boolean explicitTrustStoreOnly = false; private KeyManagerFactory keyManagerFactory = null; private boolean requireClientAuthentication = true; public void doInitialise() throws InitialisationException { if (getProvider() == null) { throw new NullPointerException("The security provider cannot be null"); } if (getKeyStore() != null) { if (getKeyPassword() == null) { throw new NullPointerException("The Key password cannot be null"); } if (getStorePassword() == null) { throw new NullPointerException("The KeyStore password cannot be null"); } if (getKeyManagerAlgorithm() == null) { throw new NullPointerException("The Key Manager Algorithm cannot be null"); } if (getKeyStoreType() == null) { throw new NullPointerException("The KeyStore type cannot be null"); } } if (getKeyStore() != null) { KeyStore keystore; try { Security.addProvider(getProvider()); // Create keyStore keystore = KeyStore.getInstance(keyStoreType); InputStream is = IOUtils.getResourceAsStream(getKeyStore(), getClass()); if (is == null) { throw new FileNotFoundException( "Failed to load keystore from classpath or local file: " + getKeyStore()); } keystore.load(is, getKeyPassword().toCharArray()); } catch (Exception e) { throw new InitialisationException( new Message(Messages.FAILED_LOAD_X, "KeyStore: " + getKeyStore()), e, this); } try { // Get key manager keyManagerFactory = KeyManagerFactory.getInstance(getKeyManagerAlgorithm()); // Initialize the KeyManagerFactory to work with our keyStore keyManagerFactory.init(keystore, getStorePassword().toCharArray()); } catch (Exception e) { throw new InitialisationException( new Message(Messages.FAILED_LOAD_X, "Key Manager (" + getKeyManagerAlgorithm() + ")"), e, this); } } if (getTrustStore() != null) { KeyStore truststore; try { truststore = KeyStore.getInstance(trustStoreType); InputStream is = IOUtils.getResourceAsStream(getTrustStore(), getClass()); if (is == null) { throw new FileNotFoundException( "Failed to load truststore from classpath or local file: " + getTrustStore()); } truststore.load(is, getTrustStorePassword().toCharArray()); } catch (Exception e) { throw new InitialisationException( new Message(Messages.FAILED_LOAD_X, "TrustStore: " + getTrustStore()), e, this); } try { trustManagerFactory = TrustManagerFactory.getInstance(getTrustManagerAlgorithm()); trustManagerFactory.init(truststore); } catch (Exception e) { throw new InitialisationException( new Message( Messages.FAILED_LOAD_X, "Trust Manager (" + getTrustManagerAlgorithm() + ")"), e, this); } } super.doInitialise(); if (protocolHandler != null) { System.setProperty("java.protocol.handler.pkgs", protocolHandler); } if (clientKeyStore != null) { try { String clientPath = FileUtils.getResourcePath(clientKeyStore, getClass()); System.setProperty("javax.net.ssl.keyStore", clientPath); System.setProperty("javax.net.ssl.keyStorePassword", clientKeyStorePassword); logger.info("Set Client Key store: javax.net.ssl.keyStore=" + clientPath); } catch (IOException e) { throw new InitialisationException( new Message(Messages.FAILED_LOAD_X, "Client KeyStore: " + clientKeyStore), e, this); } } if (trustStore != null) { System.setProperty("javax.net.ssl.trustStore", getTrustStore()); System.setProperty("javax.net.ssl.trustStorePassword", getTrustStorePassword()); logger.debug("Set Trust store: javax.net.ssl.trustStore=" + getTrustStore()); } else if (!isExplicitTrustStoreOnly()) { logger.info("Defaulting trust store to client Key Store"); trustStore = getClientKeyStore(); trustStorePassword = getClientKeyStorePassword(); System.setProperty("javax.net.ssl.trustStore", getTrustStore()); System.setProperty("javax.net.ssl.trustStorePassword", getTrustStorePassword()); logger.debug("Set Trust store: javax.net.ssl.trustStore=" + getTrustStore()); } } public String getProtocol() { return "SSL"; } public String getKeyStore() { return keyStore; } public void setKeyStore(String keyStore) { this.keyStore = keyStore; } public String getKeyPassword() { return keyPassword; } public void setKeyPassword(String keyPassword) { this.keyPassword = keyPassword; } public String getStorePassword() { return storePassword; } public void setStorePassword(String storePassword) { this.storePassword = storePassword; } public String getTrustStoreType() { return trustStoreType; } public void setTrustStoreType(String trustStoreType) { this.trustStoreType = trustStoreType; } public TrustManagerFactory getTrustManagerFactory() { return trustManagerFactory; } public void setTrustManagerFactory(TrustManagerFactory trustManagerFactory) { this.trustManagerFactory = trustManagerFactory; } public String getTrustManagerAlgorithm() { return trustManagerAlgorithm; } public void setTrustManagerAlgorithm(String trustManagerAlgorithm) { this.trustManagerAlgorithm = trustManagerAlgorithm; } public String getKeyStoreType() { return keyStoreType; } public void setKeyStoreType(String keyStoreType) { this.keyStoreType = keyStoreType; } public String getKeyManagerAlgorithm() { return keyManagerAlgorithm; } public void setKeyManagerAlgorithm(String keyManagerAlgorithm) { this.keyManagerAlgorithm = keyManagerAlgorithm; } public boolean isRequireClientAuthentication() { return requireClientAuthentication; } public void setRequireClientAuthentication(boolean requireClientAuthentication) { this.requireClientAuthentication = requireClientAuthentication; } public KeyManagerFactory getKeyManagerFactory() { return keyManagerFactory; } public Provider getProvider() { return provider; } public void setProvider(Provider provider) { this.provider = provider; } public String getProtocolHandler() { return protocolHandler; } public void setProtocolHandler(String protocolHandler) { this.protocolHandler = protocolHandler; } public String getClientKeyStore() { return clientKeyStore; } public void setClientKeyStore(String clientKeyStore) throws IOException { this.clientKeyStore = clientKeyStore; if (this.clientKeyStore != null) { this.clientKeyStore = FileUtils.getResourcePath(clientKeyStore, getClass()); logger.debug("Normalised clientKeyStore path to: " + getClientKeyStore()); } } public String getClientKeyStorePassword() { return clientKeyStorePassword; } public void setClientKeyStorePassword(String clientKeyStorePassword) { this.clientKeyStorePassword = clientKeyStorePassword; } public String getTrustStore() { return trustStore; } public void setTrustStore(String trustStore) throws IOException { this.trustStore = trustStore; if (this.trustStore != null) { this.trustStore = FileUtils.getResourcePath(trustStore, getClass()); logger.debug("Normalised trustStore path to: " + getTrustStore()); } } public String getTrustStorePassword() { return trustStorePassword; } public void setTrustStorePassword(String trustStorePassword) { this.trustStorePassword = trustStorePassword; } public boolean isExplicitTrustStoreOnly() { return explicitTrustStoreOnly; } public void setExplicitTrustStoreOnly(boolean explicitTrustStoreOnly) { this.explicitTrustStoreOnly = explicitTrustStoreOnly; } }
/** * Support for configuring TLS/SSL connections. * * <h2>Introduction</h2> * * This class was introduced to centralise the work of TLS/SSL configuration. It is intended to be * backwards compatible with earlier code (as much as possible) and so is perhaps more complex than * would be necessary if starting from zero - the main source of confusion is the distinction * between direct and indirect creation of sockets and stores. * * <h2>Configuration</h2> * * The documentation in this class is intended more for programmers than end uses. If you are * configuring a connector the interfaces {@link org.mule.umo.security.TlsIndirectTrustStore}, * {@link TlsDirectTrustStore}, {@link TlsDirectKeyStore} and {@link TlsIndirectKeyStore} should * provide guidance to individual properties. In addition you should check the documentation for the * specific protocol / connector used and may also need to read the discussion on direct and * indirect socket and store creation below (or, more simply, just use whichever key store interface * your connector implements!). * * <h2>Programming</h2> * * This class is intended to be used as a delegate as we typically want to add security to an * already existing connector (so we inherit from that connector, implement the appropriate * interfaces from {@link org.mule.umo.security.TlsIndirectTrustStore}, {@link TlsDirectTrustStore}, * {@link TlsDirectKeyStore} and {@link TlsIndirectKeyStore}, and then forward calls to the * interfaces to an instance of this class). * * <p>For setting System properties (and reading them) use {@link TlsPropertiesMapper}. This can * take a "namespace" which can then be used by {@link TlsPropertiesSocketFactory} to construct an * appropriate socket factory. This approach (storing to proeprties and then retrieving that * information later in a socket factory) lets us pass TLS/SSL configuration into libraries that are * configured by specifying on the socket factory class. * * <h2>Direct and Indirect Socket and Store Creation</h2> * * For the SSL transport, which historically defined parameters for many different secure * transports, the configuration interfaces worked as follows: * * <dl> * <dt>{@link TlsDirectTrustStore} * <dd>Used to generate trust store directly and indirectly for all TLS/SSL conections via System * properties * <dt>{@link TlsDirectKeyStore} * <dd>Used to generate key store directly * <dt>{@link TlsIndirectKeyStore} * <dd>Used to generate key store indirectly for all TLS/SSL conections via System properties * </dl> * * Historically, many other transports relied on the indirect configurations defined above. So they * implemented {@link org.mule.umo.security.TlsIndirectTrustStore} (a superclass of {@link * TlsDirectTrustStore}) and relied on {@link TlsIndirectKeyStore} from the SSL configuration. For * continuity these interfaces continue to be used, even though the configurations are now typically * (see individual connector/protocol documentation) specific to a protocol or connector. <em>Note - * these interfaces are new, but the original code had those methods, used as described. The new * interfaces only make things explicit.</em> * * <p><em>Note for programmers</em> One way to understand the above is to see that many protocols * are handled by libraries that are configured by providing either properties or a socket factory. * In both cases (the latter via {@link TlsPropertiesSocketFactory}) we continue to use properties * and the "indirect" interface. Note also that the mapping in {@link TlsPropertiesMapper} correctly * handles the asymmetry, so an initial call to {@link TlsConfiguration} uses the keystore defined * via {@link TlsDirectKeyStore}, but when a {@link TlsConfiguration} is retrieved from System * proerties using {@link TlsPropertiesMapper#readFromProperties(TlsConfiguration, * java.util.Properties)} the "indirect" properties are supplied as "direct" values, meaning that * the "indirect" socket factory can be retrieved from {@link #getKeyManagerFactory()}. It just * works. */ public final class TlsConfiguration implements TlsDirectTrustStore, TlsDirectKeyStore, TlsIndirectKeyStore { public static final String DEFAULT_KEYSTORE = ".keystore"; public static final String DEFAULT_KEYSTORE_TYPE = KeyStore.getDefaultType(); public static final String DEFAULT_SSL_TYPE = "SSLv3"; public static final String JSSE_NAMESPACE = "javax.net"; private Log logger = LogFactory.getLog(getClass()); private SecurityProviderFactory spFactory = new AutoDiscoverySecurityProviderFactory(); private SecurityProviderInfo spInfo = spFactory.getSecurityProviderInfo(); private Provider provider = spFactory.getProvider(); private String sslType = DEFAULT_SSL_TYPE; // global private String protocolHandler = spInfo.getProtocolHandler(); // this is the key store that is generated in-memory and available to connectors explicitly. // it is local to the socket. private String keyStoreName = DEFAULT_KEYSTORE; // was default in https but not ssl private String keyPassword = null; private String keyStorePassword = null; private String keystoreType = DEFAULT_KEYSTORE_TYPE; private String keyManagerAlgorithm = spInfo.getKeyManagerAlgorithm(); private KeyManagerFactory keyManagerFactory = null; // this is the key store defined in system properties that is used implicitly. // note that some transports use different namespaces within system properties, // so this is typically global across a particular transport. // it is also used as the trust store defined in system properties if no other trust // store is given and explicitTrustStoreOnly is false private String clientKeyStoreName = null; private String clientKeyStorePassword = null; private String clientKeyStoreType = DEFAULT_KEYSTORE_TYPE; // this is the trust store used to construct sockets both explicitly // and globally (if not set, see client key above) via the jvm defaults. private String trustStoreName = null; private String trustStorePassword = null; private String trustStoreType = DEFAULT_KEYSTORE_TYPE; private String trustManagerAlgorithm = spInfo.getKeyManagerAlgorithm(); private TrustManagerFactory trustManagerFactory = null; private boolean explicitTrustStoreOnly = false; private boolean requireClientAuthentication = false; /** * Support for TLS connections with a given initial value for the key store * * @param keyStore initial value for the key store */ public TlsConfiguration(String keyStore) { this.keyStoreName = keyStore; } // note - in what follows i'm using "raw" variables rather than accessors because // i think the names are clearer. the API names for the accessors are historical // and not a close fit to actual use (imho). /** * @param anon If the connection is anonymous then we don't care about client keys * @param namespace Namespace to use for global properties (for JSSE use JSSE_NAMESPACE) * @throws InitialisationException ON initialisation problems */ public void initialise(boolean anon, String namespace) throws InitialisationException { if (logger.isDebugEnabled()) { logger.debug("initialising: anon " + anon); } validate(anon); Security.addProvider(provider); System.setProperty("java.protocol.handler.pkgs", protocolHandler); if (!anon) { initKeyManagerFactory(); } initTrustManagerFactory(); if (null != namespace) { new TlsPropertiesMapper(namespace).writeToProperties(System.getProperties(), this); } } private void validate(boolean anon) throws InitialisationException { assertNotNull(getProvider(), "The security provider cannot be null"); if (!anon) { assertNotNull(getKeyStore(), "The KeyStore location cannot be null"); assertNotNull(getKeyPassword(), "The Key password cannot be null"); assertNotNull(getStorePassword(), "The KeyStore password cannot be null"); assertNotNull(getKeyManagerAlgorithm(), "The Key Manager Algorithm cannot be null"); } } private void initKeyManagerFactory() throws InitialisationException { if (logger.isDebugEnabled()) { logger.debug("initialising key manager factory from keystore data"); } KeyStore tempKeyStore; try { tempKeyStore = KeyStore.getInstance(keystoreType); InputStream is = IOUtils.getResourceAsStream(keyStoreName, getClass()); if (null == is) { throw new FileNotFoundException( CoreMessages.cannotLoadFromClasspath("Keystore: " + keyStoreName).getMessage()); } tempKeyStore.load(is, keyStorePassword.toCharArray()); } catch (Exception e) { throw new InitialisationException( CoreMessages.failedToLoad("KeyStore: " + keyStoreName), e, this); } try { keyManagerFactory = KeyManagerFactory.getInstance(getKeyManagerAlgorithm()); keyManagerFactory.init(tempKeyStore, keyPassword.toCharArray()); } catch (Exception e) { throw new InitialisationException(CoreMessages.failedToLoad("Key Manager"), e, this); } } private void initTrustManagerFactory() throws InitialisationException { if (null != trustStoreName) { trustStorePassword = null == trustStorePassword ? "" : trustStorePassword; KeyStore trustStore; try { trustStore = KeyStore.getInstance(trustStoreType); InputStream is = IOUtils.getResourceAsStream(trustStoreName, getClass()); if (null == is) { throw new FileNotFoundException( "Failed to load truststore from classpath or local file: " + trustStoreName); } trustStore.load(is, trustStorePassword.toCharArray()); } catch (Exception e) { throw new InitialisationException( CoreMessages.failedToLoad("TrustStore: " + trustStoreName), e, this); } try { trustManagerFactory = TrustManagerFactory.getInstance(trustManagerAlgorithm); trustManagerFactory.init(trustStore); } catch (Exception e) { throw new InitialisationException( CoreMessages.failedToLoad("Trust Manager (" + trustManagerAlgorithm + ")"), e, this); } } } private static void assertNotNull(Object value, String message) { if (null == value) { throw new IllegalArgumentException(message); } } private static String defaultForNull(String value, String deflt) { if (null == value) { return deflt; } else { return value; } } public SSLSocketFactory getSocketFactory() throws NoSuchAlgorithmException, KeyManagementException { return getSslContext().getSocketFactory(); } public SSLServerSocketFactory getServerSocketFactory() throws NoSuchAlgorithmException, KeyManagementException { return getSslContext().getServerSocketFactory(); } public SSLContext getSslContext() throws NoSuchAlgorithmException, KeyManagementException { KeyManager[] keyManagers = null == getKeyManagerFactory() ? null : getKeyManagerFactory().getKeyManagers(); TrustManager[] trustManagers = null == getTrustManagerFactory() ? null : getTrustManagerFactory().getTrustManagers(); SSLContext context = SSLContext.getInstance(getSslType()); // TODO - nice to have a configurable random number source set here context.init(keyManagers, trustManagers, null); return context; } public String getSslType() { return sslType; } public void setSslType(String sslType) { this.sslType = sslType; } public Provider getProvider() { return provider; } public void setProvider(Provider provider) { this.provider = provider; } public String getProtocolHandler() { return protocolHandler; } public void setProtocolHandler(String protocolHandler) { this.protocolHandler = protocolHandler; } public SecurityProviderFactory getSecurityProviderFactory() { return spFactory; } public void setSecurityProviderFactory(SecurityProviderFactory spFactory) { this.spFactory = spFactory; } // access to the explicit key store variables public String getKeyStore() { return keyStoreName; } public void setKeyStore(String name) throws IOException { keyStoreName = name; if (null != keyStoreName) { keyStoreName = FileUtils.getResourcePath(keyStoreName, getClass()); if (logger.isDebugEnabled()) { logger.debug("Normalised keyStore path to: " + keyStoreName); } } } public String getKeyPassword() { return keyPassword; } public void setKeyPassword(String keyPassword) { this.keyPassword = keyPassword; } public String getStorePassword() { return keyStorePassword; } public void setStorePassword(String storePassword) { this.keyStorePassword = storePassword; } public String getKeystoreType() { return keystoreType; } public void setKeystoreType(String keystoreType) { this.keystoreType = keystoreType; } public String getKeyManagerAlgorithm() { return keyManagerAlgorithm; } public void setKeyManagerAlgorithm(String keyManagerAlgorithm) { this.keyManagerAlgorithm = keyManagerAlgorithm; } public KeyManagerFactory getKeyManagerFactory() { return keyManagerFactory; } // access to the implicit key store variables public String getClientKeyStore() { return clientKeyStoreName; } public void setClientKeyStore(String name) throws IOException { clientKeyStoreName = name; if (null != clientKeyStoreName) { clientKeyStoreName = FileUtils.getResourcePath(clientKeyStoreName, getClass()); if (logger.isDebugEnabled()) { logger.debug("Normalised clientKeyStore path to: " + clientKeyStoreName); } } } public String getClientKeyStorePassword() { return clientKeyStorePassword; } public void setClientKeyStorePassword(String clientKeyStorePassword) { this.clientKeyStorePassword = clientKeyStorePassword; } public void setClientKeyStoreType(String clientKeyStoreType) { this.clientKeyStoreType = clientKeyStoreType; } public String getClientKeyStoreType() { return clientKeyStoreType; } // access to trust store variables public String getTrustStore() { return trustStoreName; } public void setTrustStore(String name) throws IOException { trustStoreName = name; if (null != trustStoreName) { trustStoreName = FileUtils.getResourcePath(trustStoreName, getClass()); if (logger.isDebugEnabled()) { logger.debug("Normalised trustStore path to: " + trustStoreName); } } } public String getTrustStorePassword() { return trustStorePassword; } public void setTrustStorePassword(String trustStorePassword) { this.trustStorePassword = trustStorePassword; } public String getTrustStoreType() { return trustStoreType; } public void setTrustStoreType(String trustStoreType) { this.trustStoreType = trustStoreType; } public String getTrustManagerAlgorithm() { return trustManagerAlgorithm; } public void setTrustManagerAlgorithm(String trustManagerAlgorithm) { this.trustManagerAlgorithm = defaultForNull(trustManagerAlgorithm, spInfo.getKeyManagerAlgorithm()); } public TrustManagerFactory getTrustManagerFactory() { return trustManagerFactory; } public void setTrustManagerFactory(TrustManagerFactory trustManagerFactory) { this.trustManagerFactory = trustManagerFactory; } public boolean isExplicitTrustStoreOnly() { return explicitTrustStoreOnly; } public void setExplicitTrustStoreOnly(boolean explicitTrustStoreOnly) { this.explicitTrustStoreOnly = explicitTrustStoreOnly; } public boolean isRequireClientAuthentication() { return requireClientAuthentication; } public void setRequireClientAuthentication(boolean requireClientAuthentication) { this.requireClientAuthentication = requireClientAuthentication; } }