diff options
6 files changed, 265 insertions, 59 deletions
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java index 9bded39af4..4717a9495b 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java @@ -398,4 +398,9 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec } return 0; } + + protected org.apache.qpid.transport.Connection getQpidConnection() + { + return _qpidConnection; + } } diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQTestConnection_0_10.java b/java/client/src/main/java/org/apache/qpid/client/AMQTestConnection_0_10.java new file mode 100644 index 0000000000..540b6042bf --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/AMQTestConnection_0_10.java @@ -0,0 +1,16 @@ +package org.apache.qpid.client; + +import org.apache.qpid.transport.Connection; + +public class AMQTestConnection_0_10 extends AMQConnection +{ + public AMQTestConnection_0_10(String url) throws Exception + { + super(url); + } + + public Connection getConnection() + { + return((AMQConnectionDelegate_0_10)_delegate).getQpidConnection(); + } +} diff --git a/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java b/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java index e142d21e06..702746b3da 100644 --- a/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java +++ b/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java @@ -27,10 +27,13 @@ import java.io.InputStream; import java.security.GeneralSecurityException; import java.security.KeyStore; +import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; +import org.apache.qpid.transport.network.security.ssl.SSLUtil; + /** * Factory used to create SSLContexts. SSL needs to be configured * before this will work. @@ -68,7 +71,7 @@ public class SSLContextFactory { */ private String _trustStoreCertType; - + private KeyManager customKeyManager; public SSLContextFactory(String trustStorePath, String trustStorePassword, String trustStoreCertType) @@ -90,7 +93,7 @@ public class SSLContextFactory { _trustStorePath = trustStorePath; _trustStorePassword = trustStorePassword; - if (_trustStorePassword.equals("none")) + if (_trustStorePassword != null && _trustStorePassword.equals("none")) { _trustStorePassword = null; } @@ -99,7 +102,7 @@ public class SSLContextFactory { _keyStorePath = keyStorePath; _keyStorePassword = keyStorePassword; - if (_keyStorePassword.equals("none")) + if (_keyStorePassword != null && _keyStorePassword.equals("none")) { _keyStorePassword = null; } @@ -113,29 +116,63 @@ public class SSLContextFactory { } } + public SSLContextFactory(String trustStorePath, String trustStorePassword, String trustStoreCertType, + KeyManager customKeyManager) + { + + _trustStorePath = trustStorePath; + _trustStorePassword = trustStorePassword; + + if (_trustStorePassword != null && _trustStorePassword.equals("none")) + { + _trustStorePassword = null; + } + _trustStoreCertType = trustStoreCertType; + + if (_trustStorePath == null) { + throw new IllegalArgumentException("A TrustStore path or KeyStore path must be specified"); + } + if (_trustStoreCertType == null) { + throw new IllegalArgumentException("Cert type must be specified"); + } + + this.customKeyManager = customKeyManager; + } + + /** * Builds a SSLContext appropriate for use with a server * @return SSLContext * @throws GeneralSecurityException * @throws IOException */ + public SSLContext buildServerContext() throws GeneralSecurityException, IOException { - // Create keystore - KeyStore ks = getInitializedKeyStore(_keyStorePath,_keyStorePassword); - - // Set up key manager factory to use our key store - KeyManagerFactory kmf = KeyManagerFactory.getInstance(_keyStoreCertType); - kmf.init(ks, _keyStorePassword.toCharArray()); - - KeyStore ts = getInitializedKeyStore(_trustStorePath,_trustStorePassword); + KeyStore ts = SSLUtil.getInitializedKeyStore(_trustStorePath,_trustStorePassword); TrustManagerFactory tmf = TrustManagerFactory.getInstance(_trustStoreCertType); tmf.init(ts); // Initialize the SSLContext to work with our key managers. - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + SSLContext sslContext = SSLContext.getInstance("TLS"); + + if (customKeyManager != null) + { + sslContext.init(new KeyManager[]{customKeyManager}, + tmf.getTrustManagers(), null); + + } + else + { + // Create keystore + KeyStore ks = SSLUtil.getInitializedKeyStore(_keyStorePath,_keyStorePassword); + // Set up key manager factory to use our key store + KeyManagerFactory kmf = KeyManagerFactory.getInstance(_keyStoreCertType); + kmf.init(ks, _keyStorePassword.toCharArray()); + sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + } + return sslContext; } @@ -147,7 +184,7 @@ public class SSLContextFactory { */ public SSLContext buildClientContext() throws GeneralSecurityException, IOException { - KeyStore ks = getInitializedKeyStore(_trustStorePath,_trustStorePassword); + KeyStore ks = SSLUtil.getInitializedKeyStore(_trustStorePath,_trustStorePassword); TrustManagerFactory tmf = TrustManagerFactory.getInstance(_trustStoreCertType); tmf.init(ks); SSLContext context = SSLContext.getInstance("TLS"); @@ -155,41 +192,4 @@ public class SSLContextFactory { return context; } - private KeyStore getInitializedKeyStore(String storePath, String storePassword) throws GeneralSecurityException, IOException - { - KeyStore ks = KeyStore.getInstance("JKS"); - InputStream in = null; - try - { - File f = new File(storePath); - if (f.exists()) - { - in = new FileInputStream(f); - } - else - { - in = Thread.currentThread().getContextClassLoader().getResourceAsStream(storePath); - } - if (in == null) - { - throw new IOException("Unable to load keystore resource: " + storePath); - } - ks.load(in, storePassword.toCharArray()); - } - finally - { - if (in != null) - { - //noinspection EmptyCatchBlock - try - { - in.close(); - } - catch (IOException ignored) - { - } - } - } - return ks; - } } diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidClientX509KeyManager.java b/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidClientX509KeyManager.java new file mode 100644 index 0000000000..2a3aba9a95 --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidClientX509KeyManager.java @@ -0,0 +1,80 @@ +package org.apache.qpid.transport.network.security.ssl; + +import java.net.Socket; +import java.security.KeyStore; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.X509ExtendedKeyManager; + +import org.apache.qpid.transport.util.Logger; + +public class QpidClientX509KeyManager extends X509ExtendedKeyManager +{ + private static final Logger log = Logger.get(QpidClientX509KeyManager.class); + + X509ExtendedKeyManager delegate; + String alias; + + public QpidClientX509KeyManager(String alias, String keyStorePath, + String keyStorePassword,String keyStoreCertType) throws Exception + { + this.alias = alias; + KeyStore ks = SSLUtil.getInitializedKeyStore(keyStorePath,keyStorePassword); + KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyStoreCertType); + kmf.init(ks, keyStorePassword.toCharArray()); + this.delegate = (X509ExtendedKeyManager)kmf.getKeyManagers()[0]; + } + + @Override + public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) + { + log.debug("chooseClientAlias:Returning alias " + alias); + return alias; + } + + @Override + public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) + { + return delegate.chooseServerAlias(keyType, issuers, socket); + } + + @Override + public X509Certificate[] getCertificateChain(String alias) + { + return delegate.getCertificateChain(alias); + } + + @Override + public String[] getClientAliases(String keyType, Principal[] issuers) + { + log.debug("getClientAliases:Returning alias " + alias); + return new String[]{alias}; + } + + @Override + public PrivateKey getPrivateKey(String alias) + { + return delegate.getPrivateKey(alias); + } + + @Override + public String[] getServerAliases(String keyType, Principal[] issuers) + { + return delegate.getServerAliases(keyType, issuers); + } + + public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) + { + log.debug("chooseEngineClientAlias:Returning alias " + alias); + return alias; + } + + public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) + { + return delegate.chooseEngineServerAlias(keyType, issuers, engine); + } +} diff --git a/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLUtil.java b/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLUtil.java index 130ce04adc..6c5c56a175 100644 --- a/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLUtil.java +++ b/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLUtil.java @@ -1,5 +1,11 @@ package org.apache.qpid.transport.network.security.ssl; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.KeyStore; import java.security.Principal; import java.security.cert.Certificate; import java.security.cert.X509Certificate; @@ -63,7 +69,7 @@ public class SSLUtil StringBuffer id = new StringBuffer(); try { - Certificate cert = engine.getSession().getPeerCertificates()[0]; + Certificate cert = engine.getSession().getLocalCertificates()[0]; Principal p = ((X509Certificate)cert).getSubjectDN(); String dn = p.getName(); @@ -101,15 +107,71 @@ public class SSLUtil public static SSLContext createSSLContext(ConnectionSettings settings) throws Exception { + SSLContextFactory sslContextFactory; - SSLContextFactory sslContextFactory = new SSLContextFactory(settings.getTrustStorePath(), - settings.getTrustStorePassword(), - settings.getTrustStoreCertType(), - settings.getKeyStorePath(), - settings.getKeyStorePassword(), - settings.getKeyStoreCertType()); - + if (settings.getCertAlias() == null) + { + sslContextFactory = + new SSLContextFactory(settings.getTrustStorePath(), + settings.getTrustStorePassword(), + settings.getTrustStoreCertType(), + settings.getKeyStorePath(), + settings.getKeyStorePassword(), + settings.getKeyStoreCertType()); + + } else + { + sslContextFactory = + new SSLContextFactory(settings.getTrustStorePath(), + settings.getTrustStorePassword(), + settings.getTrustStoreCertType(), + new QpidClientX509KeyManager(settings.getCertAlias(), + settings.getKeyStorePath(), + settings.getKeyStorePassword(), + settings.getKeyStoreCertType())); + + log.debug("Using custom key manager"); + } + return sslContextFactory.buildServerContext(); } + + public static KeyStore getInitializedKeyStore(String storePath, String storePassword) throws GeneralSecurityException, IOException + { + KeyStore ks = KeyStore.getInstance("JKS"); + InputStream in = null; + try + { + File f = new File(storePath); + if (f.exists()) + { + in = new FileInputStream(f); + } + else + { + in = Thread.currentThread().getContextClassLoader().getResourceAsStream(storePath); + } + if (in == null) + { + throw new IOException("Unable to load keystore resource: " + storePath); + } + ks.load(in, storePassword.toCharArray()); + } + finally + { + if (in != null) + { + //noinspection EmptyCatchBlock + try + { + in.close(); + } + catch (IOException ignored) + { + } + } + } + return ks; + } } diff --git a/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java b/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java index 05118ffc0e..74326c02ec 100644 --- a/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java +++ b/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java @@ -6,10 +6,27 @@ import java.io.PrintStream; import javax.jms.Session; import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQTestConnection_0_10; import org.apache.qpid.test.utils.QpidTestCase; +import org.apache.qpid.transport.Connection; public class SSLTest extends QpidTestCase { + + @Override + protected void setUp() throws Exception + { + System.setProperty("javax.net.debug", "ssl"); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception + { + System.setProperty("javax.net.debug", ""); + super.tearDown(); + } + public void testCreateSSLContextFromConnectionURLParams() { if (Boolean.getBoolean("profile.use_ssl")) @@ -52,6 +69,32 @@ public class SSLTest extends QpidTestCase } } } + + public void testMultipleCertsInSingleStore() throws Exception + { + if (Boolean.getBoolean("profile.use_ssl")) + { + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:" + + System.getProperty("test.port.ssl") + + "?ssl='true'&ssl_cert_alias='app1''"; + + AMQTestConnection_0_10 con = new AMQTestConnection_0_10(url); + Connection transportCon = con.getConnection(); + String userID = transportCon.getSecurityLayer().getUserID(); + assertEquals("The correct certificate was not choosen","app1@acme.org",userID); + con.close(); + + url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:" + + System.getProperty("test.port.ssl") + + "?ssl='true'&ssl_cert_alias='app2''"; + + con = new AMQTestConnection_0_10(url); + transportCon = con.getConnection(); + userID = transportCon.getSecurityLayer().getUserID(); + assertEquals("The correct certificate was not choosen","app2@acme.org",userID); + con.close(); + } + } public void testVerifyHostName() { |