Author: rgodfrey
Date: Wed Jun  3 16:07:24 2015
New Revision: 1683380

URL: http://svn.apache.org/r1683380
Log:
QPID-6552 : [Java Client] Allow client to use certificate/key files for client 
auth

Modified:
    
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStoreImpl.java
    
qpid/java/trunk/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java
    qpid/java/trunk/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java
    
qpid/java/trunk/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java
    
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/ConnectionSettings.java
    
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/network/security/SecurityLayerFactory.java
    
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidClientX509KeyManager.java
    
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLUtil.java
    
qpid/java/trunk/common/src/test/java/org/apache/qpid/ssl/SSLContextFactoryTest.java
    qpid/java/trunk/doc/book/src/jms-client-0-8/JMS-Client-Connection-URL.xml
    qpid/java/trunk/doc/book/src/jms-client-0-8/JMS-Client-Understanding.xml
    
qpid/java/trunk/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java
    
qpid/java/trunk/systests/src/test/java/org/apache/qpid/client/ssl/SSLTest.java

Modified: 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStoreImpl.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStoreImpl.java?rev=1683380&r1=1683379&r2=1683380&view=diff
==============================================================================
--- 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStoreImpl.java
 (original)
+++ 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStoreImpl.java
 Wed Jun  3 16:07:24 2015
@@ -20,28 +20,16 @@
  */
 package org.apache.qpid.server.security;
 
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringReader;
-import java.math.BigInteger;
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
 import java.security.PrivateKey;
 import java.security.SecureRandom;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.spec.RSAPrivateCrtKeySpec;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -54,7 +42,6 @@ import javax.naming.ldap.LdapName;
 import javax.naming.ldap.Rdn;
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
-import javax.xml.bind.DatatypeConverter;
 
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -73,6 +60,7 @@ import org.apache.qpid.server.model.Port
 import org.apache.qpid.server.model.State;
 import org.apache.qpid.server.model.StateTransition;
 import org.apache.qpid.server.util.urlstreamhandler.data.Handler;
+import org.apache.qpid.transport.network.security.ssl.SSLUtil;
 
 @ManagedObject( category = false )
 public class NonJavaKeyStoreImpl extends 
AbstractConfiguredObject<NonJavaKeyStoreImpl> implements 
NonJavaKeyStore<NonJavaKeyStoreImpl>
@@ -228,11 +216,11 @@ public class NonJavaKeyStoreImpl extends
     {
         try
         {
-            readPrivateKey(getUrlFromString(keyStore.getPrivateKeyUrl()));
-            readCertificates(getUrlFromString(keyStore.getCertificateUrl()));
+            
SSLUtil.readPrivateKey(getUrlFromString(keyStore.getPrivateKeyUrl()));
+            
SSLUtil.readCertificates(getUrlFromString(keyStore.getCertificateUrl()));
             if(keyStore.getIntermediateCertificateUrl() != null)
             {
-                
readCertificates(getUrlFromString(keyStore.getIntermediateCertificateUrl()));
+                
SSLUtil.readCertificates(getUrlFromString(keyStore.getIntermediateCertificateUrl()));
             }
         }
         catch (IOException | GeneralSecurityException e )
@@ -248,12 +236,12 @@ public class NonJavaKeyStoreImpl extends
         {
             if (_privateKeyUrl != null && _certificateUrl != null)
             {
-                PrivateKey privateKey = 
readPrivateKey(getUrlFromString(_privateKeyUrl));
-                X509Certificate[] certs = 
readCertificates(getUrlFromString(_certificateUrl));
+                PrivateKey privateKey = 
SSLUtil.readPrivateKey(getUrlFromString(_privateKeyUrl));
+                X509Certificate[] certs = 
SSLUtil.readCertificates(getUrlFromString(_certificateUrl));
                 if(_intermediateCertificateUrl != null)
                 {
                     List<X509Certificate> allCerts = new 
ArrayList<>(Arrays.asList(certs));
-                    
allCerts.addAll(Arrays.asList(readCertificates(getUrlFromString(_intermediateCertificateUrl))));
+                    
allCerts.addAll(Arrays.asList(SSLUtil.readCertificates(getUrlFromString(_intermediateCertificateUrl))));
                     certs = allCerts.toArray(new 
X509Certificate[allCerts.size()]);
                 }
 
@@ -297,166 +285,5 @@ public class NonJavaKeyStoreImpl extends
         return url;
     }
 
-    public static X509Certificate[] readCertificates(URL certFile)
-            throws IOException, GeneralSecurityException
-    {
-        List<X509Certificate> crt = new ArrayList<>();
-        try (InputStream is = certFile.openStream())
-        {
-            do
-            {
-                CertificateFactory cf = 
CertificateFactory.getInstance("X.509");
-                crt.add( (X509Certificate) cf.generateCertificate(is));
-            } while(is.available() != 0);
-        }
-        catch(CertificateException e)
-        {
-            if(crt.isEmpty())
-            {
-                throw e;
-            }
-        }
-        return crt.toArray(new X509Certificate[crt.size()]);
-    }
-
-    private static PrivateKey readPrivateKey(final URL url)
-            throws IOException, GeneralSecurityException
-    {
-        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-        try (InputStream urlStream = url.openStream())
-        {
-            byte[] tmp = new byte[1024];
-            int read;
-            while((read = urlStream.read(tmp)) != -1)
-            {
-                buffer.write(tmp,0,read);
-            }
-        }
-
-        byte[] content = buffer.toByteArray();
-        String contentAsString = new String(content, 
StandardCharsets.US_ASCII);
-        if(contentAsString.contains("-----BEGIN ") && 
contentAsString.contains(" PRIVATE KEY-----"))
-        {
-            BufferedReader lineReader = new BufferedReader(new 
StringReader(contentAsString));
-
-            String line;
-            do
-            {
-                line = lineReader.readLine();
-            } while(line != null && !(line.startsWith("-----BEGIN ") && 
line.endsWith(" PRIVATE KEY-----")));
-
-            if(line != null)
-            {
-                StringBuilder keyBuilder = new StringBuilder();
-
-                while((line = lineReader.readLine()) != null)
-                {
-                    if(line.startsWith("-----END ") && line.endsWith(" PRIVATE 
KEY-----"))
-                    {
-                        break;
-                    }
-                    keyBuilder.append(line);
-                }
-
-                content = 
DatatypeConverter.parseBase64Binary(keyBuilder.toString());
-            }
-        }
-        PrivateKey key;
-        try
-        {
-            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(content);
-            KeyFactory kf = KeyFactory.getInstance("RSA");
-            key = kf.generatePrivate(keySpec);
-        }
-        catch(InvalidKeySpecException e)
-        {
-            // not in PCKS#8 format - try parsing as PKCS#1
-            RSAPrivateCrtKeySpec keySpec = getRSAKeySpec(content);
-            KeyFactory kf = KeyFactory.getInstance("RSA");
-            try
-            {
-                key = kf.generatePrivate(keySpec);
-            }
-            catch(InvalidKeySpecException e2)
-            {
-                throw new InvalidKeySpecException("Cannot parse the provided 
key as either PKCS#1 or PCKS#8 format");
-            }
-
-        }
-        return key;
-    }
-
-
-    private static RSAPrivateCrtKeySpec getRSAKeySpec(byte[] keyBytes) throws 
InvalidKeySpecException
-    {
-
-        ByteBuffer buffer = ByteBuffer.wrap(keyBytes);
-        try
-        {
-            // PKCS#1 is encoded as a DER sequence of:
-            // (version, modulus, publicExponent, privateExponent, primeP, 
primeQ,
-            //  primeExponentP, primeExponentQ, crtCoefficient)
-
-            int tag = ((int)buffer.get()) & 0xff;
-
-            // check tag is that of a sequence
-            if(((tag & 0x20) != 0x20) || ((tag & 0x1F) != 0x10))
-            {
-                throw new InvalidKeySpecException("Unable to parse key as 
PKCS#1 format");
-            }
-
-            int length = getLength(buffer);
-
-            buffer = buffer.slice();
-            buffer.limit(length);
-
-            // first tlv is version - which we'll ignore
-            byte versionTag = buffer.get();
-            int versionLength = getLength(buffer);
-            buffer.position(buffer.position()+versionLength);
-
-
-            RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(
-                    getInteger(buffer), getInteger(buffer), 
getInteger(buffer), getInteger(buffer), getInteger(buffer),
-                    getInteger(buffer), getInteger(buffer), 
getInteger(buffer));
-
-            return keySpec;
-        }
-        catch(BufferUnderflowException e)
-        {
-            throw new InvalidKeySpecException("Unable to parse key as PKCS#1 
format");
-        }
-    }
-
-    private static int getLength(ByteBuffer buffer)
-    {
-
-        int i = ((int) buffer.get()) & 0xff;
-
-        // length 0 <= i <= 127 encoded as a single byte
-        if ((i & ~0x7F) == 0)
-        {
-            return i;
-        }
-
-        // otherwise the first octet gives us the number of octets needed to 
read the length
-        byte[] bytes = new byte[i & 0x7f];
-        buffer.get(bytes);
-
-        return new BigInteger(1, bytes).intValue();
-    }
-
-    private static BigInteger getInteger(ByteBuffer buffer) throws 
InvalidKeySpecException
-    {
-        int tag = ((int) buffer.get()) & 0xff;
-        // 0x02 indicates an integer type
-        if((tag & 0x1f) != 0x02)
-        {
-            throw new InvalidKeySpecException("Unable to parse key as PKCS#1 
format");
-        }
-        byte[] num = new byte[getLength(buffer)];
-        buffer.get(num);
-        return new BigInteger(num);
-    }
 
 }

Modified: 
qpid/java/trunk/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java?rev=1683380&r1=1683379&r2=1683380&view=diff
==============================================================================
--- 
qpid/java/trunk/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java
 (original)
+++ 
qpid/java/trunk/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java
 Wed Jun  3 16:07:24 2015
@@ -477,6 +477,25 @@ public class AMQBrokerDetails implements
             conSettings.setCertAlias(
                     getProperty(BrokerDetails.OPTIONS_SSL_CERT_ALIAS));
         }
+
+        if (getProperty(BrokerDetails.OPTIONS_CLIENT_CERT_PRIV_KEY_PATH) != 
null)
+        {
+            conSettings.setClientCertificatePrivateKeyPath(
+                    
getProperty(BrokerDetails.OPTIONS_CLIENT_CERT_PRIV_KEY_PATH));
+        }
+
+        if (getProperty(BrokerDetails.OPTIONS_CLIENT_CERT_PATH) != null)
+        {
+            conSettings.setClientCertificatePath(
+                    getProperty(BrokerDetails.OPTIONS_CLIENT_CERT_PATH));
+        }
+
+        if 
(getProperty(BrokerDetails.OPTIONS_CLIENT_CERT_INTERMEDIARY_CERT_PATH) != null)
+        {
+            conSettings.setClientCertificateIntermediateCertsPath(
+                    
getProperty(BrokerDetails.OPTIONS_CLIENT_CERT_INTERMEDIARY_CERT_PATH));
+        }
+
         // ----------------------------
 
         boolean defaultSSLVerifyHostName = Boolean.parseBoolean(

Modified: 
qpid/java/trunk/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java?rev=1683380&r1=1683379&r2=1683380&view=diff
==============================================================================
--- qpid/java/trunk/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java 
(original)
+++ qpid/java/trunk/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java 
Wed Jun  3 16:07:24 2015
@@ -43,14 +43,17 @@ public interface BrokerDetails
     public static final String OPTIONS_TCP_NO_DELAY = "tcp_nodelay";
     public static final String OPTIONS_SASL_PROTOCOL_NAME = "sasl_protocol";
     public static final String OPTIONS_SASL_SERVER_NAME = "sasl_server";
-    
+
     public static final String OPTIONS_TRUST_STORE = "trust_store";
     public static final String OPTIONS_TRUST_STORE_PASSWORD = 
"trust_store_password";
     public static final String OPTIONS_KEY_STORE = "key_store";
     public static final String OPTIONS_KEY_STORE_PASSWORD = 
"key_store_password";
     public static final String OPTIONS_SSL_VERIFY_HOSTNAME = 
"ssl_verify_hostname";
     public static final String OPTIONS_SSL_CERT_ALIAS = "ssl_cert_alias";
-    
+    String OPTIONS_CLIENT_CERT_PRIV_KEY_PATH = "client_cert_priv_key_path";
+    String OPTIONS_CLIENT_CERT_PATH = "client_cert_path";
+    String OPTIONS_CLIENT_CERT_INTERMEDIARY_CERT_PATH = 
"client_cert_intermediary_cert_path" ;
+
     public static final int DEFAULT_PORT = 5672;
 
     public static final String TCP = "tcp";
@@ -70,7 +73,7 @@ public interface BrokerDetails
     public static final String VIRTUAL_HOST = "virtualhost";
     public static final String CLIENT_ID = "client_id";
     public static final String USERNAME = "username";
-    public static final String PASSWORD = "password";    
+    public static final String PASSWORD = "password";
 
     String getHost();
 

Modified: 
qpid/java/trunk/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java?rev=1683380&r1=1683379&r2=1683380&view=diff
==============================================================================
--- 
qpid/java/trunk/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java 
(original)
+++ 
qpid/java/trunk/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java 
Wed Jun  3 16:07:24 2015
@@ -21,7 +21,6 @@
 package org.apache.qpid.ssl;
 
 import org.apache.qpid.transport.network.security.ssl.QpidClientX509KeyManager;
-import org.apache.qpid.transport.network.security.ssl.QpidMultipleTrustManager;
 import org.apache.qpid.transport.network.security.ssl.SSLUtil;
 
 import javax.net.ssl.KeyManager;
@@ -29,14 +28,12 @@ import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509TrustManager;
 
 import java.io.IOException;
 import java.security.GeneralSecurityException;
+import java.security.KeyManagementException;
 import java.security.KeyStore;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
+import java.security.NoSuchAlgorithmException;
 
 /**
  * Factory used to create SSLContexts. SSL needs to be configured
@@ -52,56 +49,25 @@ public class SSLContextFactory
         //no instances
     }
 
-    public static SSLContext buildClientContext(final String trustStorePath,
-            final String trustStorePassword, final String trustStoreType,
-            final String trustManagerFactoryAlgorithm, final String 
keyStorePath,
-            final String keyStorePassword, final String keyStoreType,
-            final String keyManagerFactoryAlgorithm, final String certAlias)
-            throws GeneralSecurityException, IOException
-    {
-        return buildContext(trustStorePath,
-                trustStorePassword,
-                trustStoreType,
-                trustManagerFactoryAlgorithm,
-                keyStorePath,
-                keyStorePassword,
-                keyStoreType,
-                keyManagerFactoryAlgorithm,
-                certAlias);
-    }
-
-    private static SSLContext buildContext(String trustStorePath,
-                                           String trustStorePassword,
-                                           String trustStoreType,
-                                           String trustManagerFactoryAlgorithm,
-                                           String keyStorePath,
-                                           String keyStorePassword,
-                                           String keyStoreType,
-                                           String keyManagerFactoryAlgorithm, 
String certAlias)
-            throws GeneralSecurityException, IOException
+    public static SSLContext buildClientContext(final TrustManager[] 
trustManagers, final KeyManager[] keyManagers)
+            throws NoSuchAlgorithmException, KeyManagementException
     {
         // Initialize the SSLContext to work with our key managers.
         final SSLContext sslContext = SSLContext
                 .getInstance(TRANSPORT_LAYER_SECURITY_CODE);
 
-        final TrustManager[] trustManagers;
-        final KeyManager[] keyManagers;
-
-        if (trustStorePath != null)
-        {
-            final KeyStore ts = SSLUtil.getInitializedKeyStore(trustStorePath,
-                    trustStorePassword, trustStoreType);
-            final TrustManagerFactory tmf = TrustManagerFactory
-                    .getInstance(trustManagerFactoryAlgorithm);
-            tmf.init(ts);
+        sslContext.init(keyManagers, trustManagers, null);
 
-            trustManagers = tmf.getTrustManagers();
-        }
-        else
-        {
-            trustManagers = null;
-        }
+        return sslContext;
+    }
 
+    public static KeyManager[] getKeyManagers(final String keyStorePath,
+                                              final String keyStorePassword,
+                                              final String keyStoreType,
+                                              final String 
keyManagerFactoryAlgorithm,
+                                              final String certAlias) throws 
GeneralSecurityException, IOException
+    {
+        final KeyManager[] keyManagers;
         if (keyStorePath != null)
         {
             if (certAlias != null)
@@ -127,9 +93,31 @@ public class SSLContextFactory
         {
             keyManagers = null;
         }
+        return keyManagers;
+    }
 
-        sslContext.init(keyManagers, trustManagers, null);
+    public static TrustManager[] getTrustManagers(final String trustStorePath,
+                                                  final String 
trustStorePassword,
+                                                  final String trustStoreType,
+                                                  final String 
trustManagerFactoryAlgorithm)
+            throws GeneralSecurityException, IOException
+    {
+        final TrustManager[] trustManagers;
+        if (trustStorePath != null)
+        {
+            final KeyStore ts = SSLUtil.getInitializedKeyStore(trustStorePath,
+                                                               
trustStorePassword, trustStoreType);
+            final TrustManagerFactory tmf = TrustManagerFactory
+                    .getInstance(trustManagerFactoryAlgorithm);
+            tmf.init(ts);
 
-        return sslContext;
+            trustManagers = tmf.getTrustManagers();
+        }
+        else
+        {
+            trustManagers = null;
+        }
+        return trustManagers;
     }
+
 }

Modified: 
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/ConnectionSettings.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/ConnectionSettings.java?rev=1683380&r1=1683379&r2=1683380&view=diff
==============================================================================
--- 
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/ConnectionSettings.java
 (original)
+++ 
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/ConnectionSettings.java
 Wed Jun  3 16:07:24 2015
@@ -38,13 +38,29 @@ import static org.apache.qpid.configurat
 import static 
org.apache.qpid.configuration.ClientProperties.LEGACY_RECEIVE_BUFFER_SIZE_PROP_NAME;
 import static 
org.apache.qpid.configuration.ClientProperties.LEGACY_SEND_BUFFER_SIZE_PROP_NAME;
 
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
 import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 
+import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 
 import org.apache.qpid.configuration.QpidProperty;
+import org.apache.qpid.ssl.SSLContextFactory;
+import org.apache.qpid.transport.network.security.ssl.QpidClientX509KeyManager;
+import org.apache.qpid.transport.network.security.ssl.SSLUtil;
 
 
 /**
@@ -57,6 +73,7 @@ public class ConnectionSettings
 {
     public static final String WILDCARD_ADDRESS = "*";
 
+    private static final SecureRandom RANDOM = new SecureRandom();
 
     private String protocol = "tcp";
     private String host = "localhost";
@@ -86,7 +103,11 @@ public class ConnectionSettings
     private String trustStoreType = 
System.getProperty("javax.net.ssl.trustStoreType",KeyStore.getDefaultType());
     private String certAlias;
     private boolean verifyHostname;
-    
+
+    private String _clientCertificatePrivateKeyPath;
+    private String _clientCertificatePath;
+    private String _clientCertificateIntermediateCertsPath;
+
     // SASL props
     private String saslMechs = System.getProperty("qpid.sasl_mechs", null);
     private String saslProtocol = System.getProperty("qpid.sasl_protocol", 
"AMQP");
@@ -398,6 +419,36 @@ public class ConnectionSettings
         this.trustStoreType = trustStoreType;
     }
 
+    public String getClientCertificatePrivateKeyPath()
+    {
+        return _clientCertificatePrivateKeyPath;
+    }
+
+    public void setClientCertificatePrivateKeyPath(final String 
clientCertificatePrivateKeyPath)
+    {
+        _clientCertificatePrivateKeyPath = clientCertificatePrivateKeyPath;
+    }
+
+    public String getClientCertificatePath()
+    {
+        return _clientCertificatePath;
+    }
+
+    public void setClientCertificatePath(final String clientCertificatePath)
+    {
+        _clientCertificatePath = clientCertificatePath;
+    }
+
+    public String getClientCertificateIntermediateCertsPath()
+    {
+        return _clientCertificateIntermediateCertsPath;
+    }
+
+    public void setClientCertificateIntermediateCertsPath(final String 
clientCertificateIntermediateCertsPath)
+    {
+        _clientCertificateIntermediateCertsPath = 
clientCertificateIntermediateCertsPath;
+    }
+
     public int getConnectTimeout()
     {
         return connectTimeout;
@@ -428,4 +479,80 @@ public class ConnectionSettings
         this.writeBufferSize = writeBufferSize;
     }
 
+    public KeyManager[] getKeyManagers()
+            throws GeneralSecurityException, IOException
+    {
+        if(getKeyStorePath() != null)
+        {
+            return SSLContextFactory.getKeyManagers(getKeyStorePath(),
+                                                    getKeyStorePassword(),
+                                                    getKeyStoreType(),
+                                                    
getKeyManagerFactoryAlgorithm(),
+                                                    getCertAlias());
+        }
+        else if(getClientCertificatePrivateKeyPath() != null)
+        {
+            return getKeyManagers(getClientCertificatePrivateKeyPath(), 
getClientCertificatePath(), getClientCertificateIntermediateCertsPath(), 
getKeyManagerFactoryAlgorithm());
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+    public TrustManager[] getTrustManagers()
+            throws GeneralSecurityException, IOException
+    {
+        final TrustManager[] trustManagers;
+        trustManagers =
+                SSLContextFactory.getTrustManagers(getTrustStorePath(),
+                                                   getTrustStorePassword(),
+                                                   getTrustStoreType(),
+                                                   
getTrustManagerFactoryAlgorithm());
+        return trustManagers;
+    }
+
+    private KeyManager[] getKeyManagers(String privateKeyFile,
+                                        String certFile,
+                                        String intermediateFile,
+                                        String keyManagerFactoryAlgorithm) 
throws GeneralSecurityException, IOException
+    {
+        System.err.println("**** RG : in getKeyManagers[] privateKey: "
+                           + privateKeyFile
+                           + " ; certFile: "
+                           + certFile
+                           + " ; intermediate: "
+                           + intermediateFile);
+        try (FileInputStream privateKeyStream = new 
FileInputStream(privateKeyFile);
+             FileInputStream certFileStream = new FileInputStream(certFile))
+        {
+            PrivateKey privateKey = SSLUtil.readPrivateKey(privateKeyStream);
+            X509Certificate[] certs = SSLUtil.readCertificates(certFileStream);
+            if (intermediateFile != null)
+            {
+                try (FileInputStream intermediateFileStream = new 
FileInputStream(intermediateFile))
+                {
+                    List<X509Certificate> allCerts = new 
ArrayList<>(Arrays.asList(certs));
+                    
allCerts.addAll(Arrays.asList(SSLUtil.readCertificates(intermediateFileStream)));
+                    certs = allCerts.toArray(new 
X509Certificate[allCerts.size()]);
+                }
+            }
+            System.err.println("*** RG : cert count - " + certs.length);
+            java.security.KeyStore inMemoryKeyStore =
+                    
java.security.KeyStore.getInstance(java.security.KeyStore.getDefaultType());
+
+            byte[] bytes = new byte[64];
+            char[] chars = new char[64];
+            RANDOM.nextBytes(bytes);
+            
StandardCharsets.US_ASCII.decode(ByteBuffer.wrap(bytes)).get(chars);
+            inMemoryKeyStore.load(null, chars);
+            inMemoryKeyStore.setKeyEntry("1", privateKey, chars, certs);
+
+
+            return new KeyManager[]{new QpidClientX509KeyManager("1",
+                                                                 
inMemoryKeyStore,
+                                                                 new 
String(chars),
+                                                                 
keyManagerFactoryAlgorithm)};
+        }
+    }
 }

Modified: 
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/network/security/SecurityLayerFactory.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/network/security/SecurityLayerFactory.java?rev=1683380&r1=1683379&r2=1683380&view=diff
==============================================================================
--- 
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/network/security/SecurityLayerFactory.java
 (original)
+++ 
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/network/security/SecurityLayerFactory.java
 Wed Jun  3 16:07:24 2015
@@ -20,8 +20,10 @@
  */
 package org.apache.qpid.transport.network.security;
 
+import javax.net.ssl.KeyManager;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
+import javax.net.ssl.TrustManager;
 
 import org.apache.qpid.ssl.SSLContextFactory;
 import org.apache.qpid.transport.ByteBufferReceiver;
@@ -74,16 +76,16 @@ public class SecurityLayerFactory
             _layer = layer;
             try
             {
-                sslCtx = SSLContextFactory
-                        .buildClientContext(settings.getTrustStorePath(),
-                                settings.getTrustStorePassword(),
-                                settings.getTrustStoreType(),
-                                settings.getTrustManagerFactoryAlgorithm(),
-                                settings.getKeyStorePath(),
-                                settings.getKeyStorePassword(),
-                                settings.getKeyStoreType(),
-                                settings.getKeyManagerFactoryAlgorithm(),
-                                settings.getCertAlias());
+
+                final TrustManager[] trustManagers;
+                final KeyManager[] keyManagers;
+
+                trustManagers = settings.getTrustManagers();
+
+                keyManagers = settings.getKeyManagers();
+
+
+                sslCtx = SSLContextFactory.buildClientContext(trustManagers, 
keyManagers);
             }
             catch (Exception e)
             {

Modified: 
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidClientX509KeyManager.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidClientX509KeyManager.java?rev=1683380&r1=1683379&r2=1683380&view=diff
==============================================================================
--- 
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidClientX509KeyManager.java
 (original)
+++ 
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidClientX509KeyManager.java
 Wed Jun  3 16:07:24 2015
@@ -20,6 +20,7 @@
  */
 package org.apache.qpid.transport.network.security.ssl;
 
+import org.apache.qpid.transport.util.Functions;
 import org.apache.qpid.transport.util.Logger;
 
 import javax.net.ssl.KeyManagerFactory;
@@ -44,7 +45,7 @@ public class QpidClientX509KeyManager ex
     public QpidClientX509KeyManager(String alias, String keyStorePath, String 
keyStoreType,
                            String keyStorePassword, String 
keyManagerFactoryAlgorithmName) throws GeneralSecurityException, IOException
     {
-        this.alias = alias;    
+        this.alias = alias;
         KeyStore ks = 
SSLUtil.getInitializedKeyStore(keyStorePath,keyStorePassword,keyStoreType);
         KeyManagerFactory kmf = 
KeyManagerFactory.getInstance(keyManagerFactoryAlgorithmName);
         kmf.init(ks, keyStorePassword.toCharArray());
@@ -61,6 +62,16 @@ public class QpidClientX509KeyManager ex
         this.delegate = (X509ExtendedKeyManager)kmf.getKeyManagers()[0];
     }
 
+    public QpidClientX509KeyManager(String alias, KeyStore ks,
+                                    String keyStorePassword, String 
keyManagerFactoryAlgorithmName) throws GeneralSecurityException, IOException
+    {
+        this.alias = alias;
+        KeyManagerFactory kmf = 
KeyManagerFactory.getInstance(keyManagerFactoryAlgorithmName);
+        kmf.init(ks, keyStorePassword.toCharArray());
+        this.delegate = (X509ExtendedKeyManager)kmf.getKeyManagers()[0];
+    }
+
+
     public String chooseClientAlias(String[] keyType, Principal[] issuers, 
Socket socket)
     {
         log.debug("chooseClientAlias:Returning alias " + alias);

Modified: 
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLUtil.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLUtil.java?rev=1683380&r1=1683379&r2=1683380&view=diff
==============================================================================
--- 
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLUtil.java
 (original)
+++ 
qpid/java/trunk/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLUtil.java
 Wed Jun  3 16:07:24 2015
@@ -20,20 +20,34 @@
  */
 package org.apache.qpid.transport.network.security.ssl;
 
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.StringReader;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
+import java.math.BigInteger;
 import java.net.URL;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
 import java.security.KeyStore;
 import java.security.Principal;
+import java.security.PrivateKey;
 import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
 import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -50,6 +64,7 @@ import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.SSLSocket;
+import javax.xml.bind.DatatypeConverter;
 
 import org.apache.qpid.transport.TransportException;
 import org.apache.qpid.transport.util.Logger;
@@ -272,6 +287,183 @@ public class SSLUtil
         return ks;
     }
 
+    public static X509Certificate[] readCertificates(URL certFile)
+            throws IOException, GeneralSecurityException
+    {
+        try (InputStream is = certFile.openStream())
+        {
+            return readCertificates(certFile.openStream());
+        }
+    }
+
+    public static X509Certificate[] readCertificates(InputStream input)
+            throws IOException, GeneralSecurityException
+    {
+        List<X509Certificate> crt = new ArrayList<>();
+        try
+        {
+            do
+            {
+                CertificateFactory cf = 
CertificateFactory.getInstance("X.509");
+                crt.add( (X509Certificate) cf.generateCertificate(input));
+            } while(input.available() != 0);
+        }
+        catch(CertificateException e)
+        {
+            if(crt.isEmpty())
+            {
+                throw e;
+            }
+        }
+        return crt.toArray(new X509Certificate[crt.size()]);
+    }
+
+    public static PrivateKey readPrivateKey(final URL url)
+            throws IOException, GeneralSecurityException
+    {
+        try (InputStream urlStream = url.openStream())
+        {
+            return readPrivateKey(urlStream);
+        }
+    }
+
+    public static PrivateKey readPrivateKey(InputStream input)
+            throws IOException, GeneralSecurityException
+    {
+        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+        byte[] tmp = new byte[1024];
+        int read;
+        while ((read = input.read(tmp)) != -1)
+        {
+            buffer.write(tmp, 0, read);
+        }
+
+        byte[] content = buffer.toByteArray();
+        String contentAsString = new String(content, 
StandardCharsets.US_ASCII);
+        if(contentAsString.contains("-----BEGIN ") && 
contentAsString.contains(" PRIVATE KEY-----"))
+        {
+            BufferedReader lineReader = new BufferedReader(new 
StringReader(contentAsString));
+
+            String line;
+            do
+            {
+                line = lineReader.readLine();
+            } while(line != null && !(line.startsWith("-----BEGIN ") && 
line.endsWith(" PRIVATE KEY-----")));
+
+            if(line != null)
+            {
+                StringBuilder keyBuilder = new StringBuilder();
+
+                while((line = lineReader.readLine()) != null)
+                {
+                    if(line.startsWith("-----END ") && line.endsWith(" PRIVATE 
KEY-----"))
+                    {
+                        break;
+                    }
+                    keyBuilder.append(line);
+                }
+
+                content = 
DatatypeConverter.parseBase64Binary(keyBuilder.toString());
+            }
+        }
+        PrivateKey key;
+        try
+        {
+            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(content);
+            KeyFactory kf = KeyFactory.getInstance("RSA");
+            key = kf.generatePrivate(keySpec);
+        }
+        catch(InvalidKeySpecException e)
+        {
+            // not in PCKS#8 format - try parsing as PKCS#1
+            RSAPrivateCrtKeySpec keySpec = getRSAKeySpec(content);
+            KeyFactory kf = KeyFactory.getInstance("RSA");
+            try
+            {
+                key = kf.generatePrivate(keySpec);
+            }
+            catch(InvalidKeySpecException e2)
+            {
+                throw new InvalidKeySpecException("Cannot parse the provided 
key as either PKCS#1 or PCKS#8 format");
+            }
+
+        }
+        return key;
+    }
+
+    private static RSAPrivateCrtKeySpec getRSAKeySpec(byte[] keyBytes) throws 
InvalidKeySpecException
+    {
+
+        ByteBuffer buffer = ByteBuffer.wrap(keyBytes);
+        try
+        {
+            // PKCS#1 is encoded as a DER sequence of:
+            // (version, modulus, publicExponent, privateExponent, primeP, 
primeQ,
+            //  primeExponentP, primeExponentQ, crtCoefficient)
+
+            int tag = ((int)buffer.get()) & 0xff;
+
+            // check tag is that of a sequence
+            if(((tag & 0x20) != 0x20) || ((tag & 0x1F) != 0x10))
+            {
+                throw new InvalidKeySpecException("Unable to parse key as 
PKCS#1 format");
+            }
+
+            int length = getLength(buffer);
+
+            buffer = buffer.slice();
+            buffer.limit(length);
+
+            // first tlv is version - which we'll ignore
+            byte versionTag = buffer.get();
+            int versionLength = getLength(buffer);
+            buffer.position(buffer.position()+versionLength);
+
+
+            RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(
+                    getInteger(buffer), getInteger(buffer), 
getInteger(buffer), getInteger(buffer), getInteger(buffer),
+                    getInteger(buffer), getInteger(buffer), 
getInteger(buffer));
+
+            return keySpec;
+        }
+        catch(BufferUnderflowException e)
+        {
+            throw new InvalidKeySpecException("Unable to parse key as PKCS#1 
format");
+        }
+    }
+
+    private static int getLength(ByteBuffer buffer)
+    {
+
+        int i = ((int) buffer.get()) & 0xff;
+
+        // length 0 <= i <= 127 encoded as a single byte
+        if ((i & ~0x7F) == 0)
+        {
+            return i;
+        }
+
+        // otherwise the first octet gives us the number of octets needed to 
read the length
+        byte[] bytes = new byte[i & 0x7f];
+        buffer.get(bytes);
+
+        return new BigInteger(1, bytes).intValue();
+    }
+
+    private static BigInteger getInteger(ByteBuffer buffer) throws 
InvalidKeySpecException
+    {
+        int tag = ((int) buffer.get()) & 0xff;
+        // 0x02 indicates an integer type
+        if((tag & 0x1f) != 0x02)
+        {
+            throw new InvalidKeySpecException("Unable to parse key as PKCS#1 
format");
+        }
+        byte[] num = new byte[getLength(buffer)];
+        buffer.get(num);
+        return new BigInteger(num);
+    }
+
     private static interface SSLEntity
     {
         String[] getEnabledCipherSuites();

Modified: 
qpid/java/trunk/common/src/test/java/org/apache/qpid/ssl/SSLContextFactoryTest.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/common/src/test/java/org/apache/qpid/ssl/SSLContextFactoryTest.java?rev=1683380&r1=1683379&r2=1683380&view=diff
==============================================================================
--- 
qpid/java/trunk/common/src/test/java/org/apache/qpid/ssl/SSLContextFactoryTest.java
 (original)
+++ 
qpid/java/trunk/common/src/test/java/org/apache/qpid/ssl/SSLContextFactoryTest.java
 Wed Jun  3 16:07:24 2015
@@ -19,8 +19,10 @@ package org.apache.qpid.ssl;
 
 import org.apache.qpid.test.utils.QpidTestCase;
 
+import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 
 import java.io.IOException;
@@ -41,7 +43,26 @@ public class SSLContextFactoryTest exten
     {
         try
         {
-            SSLContextFactory.buildClientContext("/path/to/nothing", 
STORE_PASSWORD, STORE_TYPE, DEFAULT_TRUST_MANAGER_ALGORITHM, 
CLIENT_KEYSTORE_PATH, STORE_PASSWORD, STORE_TYPE, 
DEFAULT_KEY_MANAGER_ALGORITHM, null);
+
+            final TrustManager[] trustManagers;
+            final KeyManager[] keyManagers;
+
+            trustManagers =
+                    SSLContextFactory.getTrustManagers("/path/to/nothing",
+                                                       STORE_PASSWORD,
+                                                       STORE_TYPE,
+                                                       
DEFAULT_TRUST_MANAGER_ALGORITHM);
+
+            keyManagers =
+                    SSLContextFactory.getKeyManagers(CLIENT_KEYSTORE_PATH,
+                                                     STORE_PASSWORD,
+                                                     STORE_TYPE,
+                                                     
DEFAULT_KEY_MANAGER_ALGORITHM,
+                                                     null);
+
+            SSLContextFactory.buildClientContext(trustManagers, keyManagers);
+
+
             fail("Exception was not thrown due to incorrect path");
         }
         catch (IOException e)
@@ -52,19 +73,69 @@ public class SSLContextFactoryTest exten
 
     public void testBuildClientContextForSSLEncryptionOnly() throws Exception
     {
-        SSLContext context = 
SSLContextFactory.buildClientContext(CLIENT_TRUSTSTORE_PATH, STORE_PASSWORD, 
STORE_TYPE, DEFAULT_TRUST_MANAGER_ALGORITHM, null, null, null, null, null);
+
+        final TrustManager[] trustManagers;
+        final KeyManager[] keyManagers;
+
+        trustManagers =
+                SSLContextFactory.getTrustManagers(CLIENT_TRUSTSTORE_PATH,
+                                                   STORE_PASSWORD,
+                                                   STORE_TYPE,
+                                                   
DEFAULT_TRUST_MANAGER_ALGORITHM);
+
+        keyManagers =
+                SSLContextFactory.getKeyManagers(null, null, null, null, null);
+
+
+        SSLContext context = 
SSLContextFactory.buildClientContext(trustManagers, keyManagers);
         assertNotNull("SSLContext should not be null", context);
     }
 
     public void testBuildClientContextWithForClientAuth() throws Exception
     {
-        SSLContext context = 
SSLContextFactory.buildClientContext(CLIENT_TRUSTSTORE_PATH, STORE_PASSWORD, 
STORE_TYPE, DEFAULT_TRUST_MANAGER_ALGORITHM, CLIENT_KEYSTORE_PATH, 
STORE_PASSWORD, STORE_TYPE, DEFAULT_KEY_MANAGER_ALGORITHM, null);
+
+        final TrustManager[] trustManagers;
+        final KeyManager[] keyManagers;
+
+        trustManagers =
+                SSLContextFactory.getTrustManagers(CLIENT_TRUSTSTORE_PATH,
+                                                   STORE_PASSWORD,
+                                                   STORE_TYPE,
+                                                   
DEFAULT_TRUST_MANAGER_ALGORITHM);
+
+        keyManagers =
+                SSLContextFactory.getKeyManagers(CLIENT_KEYSTORE_PATH,
+                                                 STORE_PASSWORD,
+                                                 STORE_TYPE,
+                                                 DEFAULT_KEY_MANAGER_ALGORITHM,
+                                                 null);
+
+
+        SSLContext context = 
SSLContextFactory.buildClientContext(trustManagers, keyManagers);
         assertNotNull("SSLContext should not be null", context);
     }
 
     public void testBuildClientContextWithForClientAuthWithCertAlias() throws 
Exception
     {
-        SSLContext context = 
SSLContextFactory.buildClientContext(CLIENT_TRUSTSTORE_PATH, STORE_PASSWORD, 
STORE_TYPE, DEFAULT_TRUST_MANAGER_ALGORITHM, CLIENT_KEYSTORE_PATH, 
STORE_PASSWORD, STORE_TYPE, DEFAULT_KEY_MANAGER_ALGORITHM, CERT_ALIAS_APP1);
+
+        final TrustManager[] trustManagers;
+        final KeyManager[] keyManagers;
+
+        trustManagers =
+                SSLContextFactory.getTrustManagers(CLIENT_TRUSTSTORE_PATH,
+                                                   STORE_PASSWORD,
+                                                   STORE_TYPE,
+                                                   
DEFAULT_TRUST_MANAGER_ALGORITHM);
+
+        keyManagers =
+                SSLContextFactory.getKeyManagers(CLIENT_KEYSTORE_PATH,
+                                                 STORE_PASSWORD,
+                                                 STORE_TYPE,
+                                                 DEFAULT_KEY_MANAGER_ALGORITHM,
+                                                 CERT_ALIAS_APP1);
+
+
+        SSLContext context = 
SSLContextFactory.buildClientContext(trustManagers, keyManagers);
         assertNotNull("SSLContext should not be null", context);
     }
 }

Modified: 
qpid/java/trunk/doc/book/src/jms-client-0-8/JMS-Client-Connection-URL.xml
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/doc/book/src/jms-client-0-8/JMS-Client-Connection-URL.xml?rev=1683380&r1=1683379&r2=1683380&view=diff
==============================================================================
--- qpid/java/trunk/doc/book/src/jms-client-0-8/JMS-Client-Connection-URL.xml 
(original)
+++ qpid/java/trunk/doc/book/src/jms-client-0-8/JMS-Client-Connection-URL.xml 
Wed Jun  3 16:07:24 2015
@@ -289,7 +289,32 @@
                                        <entry> key_store_password </entry>
                                        <entry> String </entry>
                                        <entry> Key store password. Password 
used to open the key store. </entry>
-                               </row>
+                                </row>
+
+                               <row 
id="JMS-Client-0-8-Connection-URL-BrokerOptions-ClientCertPath">
+                                       <entry> client_cert_path </entry>
+                                       <entry> String </entry>
+                                        <entry> Path to the client certificate 
file (in PEM or DER format). Used as an 
+                                                alternative to using a Java 
KeyStore to hold key information
+                                                for TLS client auth. When 
used, the <literal>client_cert_priv_key_path</literal>
+                                                must also be supplied. </entry>
+                                </row>
+                               <row 
id="JMS-Client-0-8-Connection-URL-BrokerOptions-ClientCertPrivKeyPath">
+                                       <entry> client_cert_priv_key_path 
</entry>
+                                       <entry> String </entry>
+                                        <entry> Path to the client certificate 
private key file (in PEM or DER format).
+                                                Used when supplying the key 
information for TLS client auth using PEM/DER
+                                                files rather than a Java 
KeyStore. </entry>
+                                </row>
+                               <row 
id="JMS-Client-0-8-Connection-URL-BrokerOptions-ClientCertsIntermediaryCertPath">
+                                       <entry> 
client_cert_intermediary_cert_path </entry>
+                                       <entry> String </entry>
+                                        <entry> Path to a file containing any 
intermediary certificates (in PEM or DER format).
+                                                Used when supplying the key 
information for TLS client auth using PEM/DER 
+                                                files rather than a Java 
KeyStore. Only required where intermediary certificates
+                                                are required in the 
certificate chain. </entry>
+                                </row>
+
                                <row 
id="JMS-Client-0-8-Connection-URL-BrokerOptions-SslCertAlias">
                                        <entry> ssl_cert_alias </entry>
                                        <entry> String </entry>

Modified: 
qpid/java/trunk/doc/book/src/jms-client-0-8/JMS-Client-Understanding.xml
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/doc/book/src/jms-client-0-8/JMS-Client-Understanding.xml?rev=1683380&r1=1683379&r2=1683380&view=diff
==============================================================================
--- qpid/java/trunk/doc/book/src/jms-client-0-8/JMS-Client-Understanding.xml 
(original)
+++ qpid/java/trunk/doc/book/src/jms-client-0-8/JMS-Client-Understanding.xml 
Wed Jun  3 16:07:24 2015
@@ -230,6 +230,18 @@ amqp://guest:guest@clientid/?brokerlist=
 
amqp://guest:guest@clientid/?brokerlist='localhost:5671?key_store='/path/to/app1_client_cert.ks'&key_store_password='secret''&ssl='true']]>
             </screen>
           </example>
+          <para>Alternatively we can use <link
+              
linkend="JMS-Client-0-8-Connection-URL-BrokerOptions-ClientCertPath"
+                ><literal>client_cert_path</literal></link> and <link
+              
linkend="JMS-Client-0-8-Connection-URL-BrokerOptions-ClientCertPrivKeyPath"
+                ><literal>client_cert_priv_key_ath</literal></link> to specify 
a path to a certificate file (in PEM or DER format)
+            and the private key information (again in either PEM or DER 
format) respectively.</para>
+          <example>
+            <title>Connection URL configured for SSL - SSL client-auth 
(2)</title>
+            <screen><![CDATA[
+amqp://guest:guest@clientid/?brokerlist='localhost:5671?client_cert_path='/path/to/app1_client.crt'&client_cert_priv_key_path='/path/to/app1_client.key''&ssl='true']]>
+            </screen>
+          </example>
         </listitem>
       </itemizedlist>
     </section>

Modified: 
qpid/java/trunk/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java?rev=1683380&r1=1683379&r2=1683380&view=diff
==============================================================================
--- 
qpid/java/trunk/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java
 (original)
+++ 
qpid/java/trunk/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java
 Wed Jun  3 16:07:24 2015
@@ -45,9 +45,11 @@ import java.util.List;
 import java.util.Map;
 
 import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 import javax.servlet.http.HttpServletResponse;
 import javax.xml.bind.DatatypeConverter;
@@ -141,11 +143,24 @@ public class RestTestHelper
                 // We have to use a SSLSocketFactory from a new SSLContext so 
that we don't re-use
                 // the JVM's defaults that may have been initialised in 
previous tests.
 
-                SSLContext sslContext = SSLContextFactory.buildClientContext(
-                        TRUSTSTORE, TRUSTSTORE_PASSWORD,
-                        KeyStore.getDefaultType(),
-                        TrustManagerFactory.getDefaultAlgorithm(),
-                        KEYSTORE, KEYSTORE_PASSWORD, 
KeyStore.getDefaultType(), KeyManagerFactory.getDefaultAlgorithm(), 
CERT_ALIAS_APP1);
+                final TrustManager[] trustManagers;
+                final KeyManager[] keyManagers;
+
+                trustManagers =
+                        SSLContextFactory.getTrustManagers(TRUSTSTORE,
+                                                           TRUSTSTORE_PASSWORD,
+                                                           
KeyStore.getDefaultType(),
+                                                           
TrustManagerFactory.getDefaultAlgorithm());
+
+                keyManagers =
+                        SSLContextFactory.getKeyManagers(KEYSTORE,
+                                                         KEYSTORE_PASSWORD,
+                                                         
KeyStore.getDefaultType(),
+                                                         
KeyManagerFactory.getDefaultAlgorithm(),
+                                                         CERT_ALIAS_APP1);
+
+
+                SSLContext sslContext = 
SSLContextFactory.buildClientContext(trustManagers, keyManagers);
 
                 SSLSocketFactory sslSocketFactory = 
sslContext.getSocketFactory();
 
@@ -163,11 +178,20 @@ public class RestTestHelper
                 // We have to use a SSLSocketFactory from a new SSLContext so 
that we don't re-use
                 // the JVM's defaults that may have been initialised in 
previous tests.
 
-                SSLContext sslContext = SSLContextFactory.buildClientContext(
-                        TRUSTSTORE, TRUSTSTORE_PASSWORD,
-                        KeyStore.getDefaultType(),
-                        TrustManagerFactory.getDefaultAlgorithm(),
-                        null, null, null, null, null);
+                final TrustManager[] trustManagers;
+                final KeyManager[] keyManagers;
+
+                trustManagers =
+                        SSLContextFactory.getTrustManagers(TRUSTSTORE,
+                                                           TRUSTSTORE_PASSWORD,
+                                                           
KeyStore.getDefaultType(),
+                                                           
TrustManagerFactory.getDefaultAlgorithm());
+
+                keyManagers =
+                        SSLContextFactory.getKeyManagers(null, null, null, 
null, null);
+
+
+                SSLContext sslContext = 
SSLContextFactory.buildClientContext(trustManagers, keyManagers);
 
                 SSLSocketFactory sslSocketFactory = 
sslContext.getSocketFactory();
 

Modified: 
qpid/java/trunk/systests/src/test/java/org/apache/qpid/client/ssl/SSLTest.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/systests/src/test/java/org/apache/qpid/client/ssl/SSLTest.java?rev=1683380&r1=1683379&r2=1683380&view=diff
==============================================================================
--- 
qpid/java/trunk/systests/src/test/java/org/apache/qpid/client/ssl/SSLTest.java 
(original)
+++ 
qpid/java/trunk/systests/src/test/java/org/apache/qpid/client/ssl/SSLTest.java 
Wed Jun  3 16:07:24 2015
@@ -26,7 +26,13 @@ import static org.apache.qpid.test.utils
 import static org.apache.qpid.test.utils.TestSSLConstants.TRUSTSTORE_PASSWORD;
 
 import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
 import java.io.PrintStream;
+import java.security.Key;
+import java.security.cert.Certificate;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -35,6 +41,7 @@ import java.util.Map;
 import javax.jms.Connection;
 import javax.jms.JMSException;
 import javax.jms.Session;
+import javax.xml.bind.DatatypeConverter;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -49,6 +56,7 @@ import org.apache.qpid.server.model.Virt
 import org.apache.qpid.server.model.VirtualHostNameAlias;
 import org.apache.qpid.test.utils.QpidBrokerTestCase;
 import org.apache.qpid.test.utils.TestBrokerConfiguration;
+import org.apache.qpid.test.utils.TestFileUtils;
 
 public class SSLTest extends QpidBrokerTestCase
 {
@@ -464,7 +472,31 @@ public class SSLTest extends QpidBrokerT
         }
     }
 
+    public void testCreateSSLWithCertFileAndPrivateKey() throws Exception
+    {
+        if (shouldPerformTest())
+        {
+            clearSslStoreSystemProperties();
+            File[] certAndKeyFiles = extractResourcesFromTestKeyStore(true);
+            //Start the broker (WANTing client certificate authentication)
+            configureJavaBrokerIfNecessary(true, true, true, false, false);
+            super.setUp();
+
+
+            String url = 
"amqp://guest:guest@test/?brokerlist='tcp://localhost:%s" +
+                         "?ssl='true'" +
+                         
"&trust_store='%s'&ssl_verify_hostname='false'&trust_store_password='%s'" +
+                         
"&client_cert_path='%s'&client_cert_priv_key_path='%s''";
 
+            url = String.format(url,QpidBrokerTestCase.DEFAULT_SSL_PORT, 
TRUSTSTORE,TRUSTSTORE_PASSWORD, certAndKeyFiles[1].getCanonicalPath(), 
certAndKeyFiles[0].getCanonicalPath());
+
+            final AMQConnectionURL connectionURL = new AMQConnectionURL(url);
+            Connection con = getConnection(connectionURL);
+            assertNotNull("connection should be successful", con);
+            Session ssn = con.createSession(false,Session.AUTO_ACKNOWLEDGE);
+            assertNotNull("create session should be successful", ssn);
+        }
+    }
     private boolean shouldPerformTest()
     {
         // We run the SSL tests on all the Java broker profiles
@@ -524,4 +556,57 @@ public class SSLTest extends QpidBrokerT
         setSystemProperty("javax.net.ssl.trustStore", null);
         setSystemProperty("javax.net.ssl.trustStorePassword", null);
     }
+
+    private File[] extractResourcesFromTestKeyStore(boolean pem) throws 
Exception
+    {
+        java.security.KeyStore ks = 
java.security.KeyStore.getInstance(java.security.KeyStore.getDefaultType());
+        try(InputStream is = new FileInputStream(KEYSTORE))
+        {
+            ks.load(is, KEYSTORE_PASSWORD.toCharArray() );
+        }
+
+
+        File privateKeyFile = TestFileUtils.createTempFile(this, 
".private-key.der");
+        try(FileOutputStream kos = new FileOutputStream(privateKeyFile))
+        {
+            Key pvt = ks.getKey(CERT_ALIAS_APP1, 
KEYSTORE_PASSWORD.toCharArray());
+            kos.write("-----BEGIN PRIVATE KEY-----\n".getBytes());
+            String base64encoded = 
DatatypeConverter.printBase64Binary(pvt.getEncoded());
+            while(base64encoded.length() > 76)
+            {
+                kos.write(base64encoded.substring(0,76).getBytes());
+                kos.write("\n".getBytes());
+                base64encoded = base64encoded.substring(76);
+            }
+
+            kos.write(base64encoded.getBytes());
+            kos.write("\n-----END PRIVATE KEY-----".getBytes());
+            kos.flush();
+        }
+
+        File certificateFile = TestFileUtils.createTempFile(this, 
".certificate.der");
+
+        try(FileOutputStream cos = new FileOutputStream(certificateFile))
+        {
+            Certificate[] chain = ks.getCertificateChain(CERT_ALIAS_APP1);
+            for(Certificate pub : chain)
+            {
+                cos.write("-----BEGIN CERTIFICATE-----\n".getBytes());
+                String base64encoded = 
DatatypeConverter.printBase64Binary(pub.getEncoded());
+                while (base64encoded.length() > 76)
+                {
+                    cos.write(base64encoded.substring(0, 76).getBytes());
+                    cos.write("\n".getBytes());
+                    base64encoded = base64encoded.substring(76);
+                }
+                cos.write(base64encoded.getBytes());
+
+                cos.write("\n-----END CERTIFICATE-----\n".getBytes());
+            }
+            cos.flush();
+        }
+
+        return new File[]{privateKeyFile,certificateFile};
+    }
+
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to