You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by rg...@apache.org on 2017/11/26 15:11:33 UTC

qpid-broker-j git commit: QPID-7567 : Use SNI to select appropriate keystore alias

Repository: qpid-broker-j
Updated Branches:
  refs/heads/master ebe2fcae1 -> 09d7d8a19


QPID-7567 : Use SNI to select appropriate keystore alias


Project: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/commit/09d7d8a1
Tree: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/tree/09d7d8a1
Diff: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/diff/09d7d8a1

Branch: refs/heads/master
Commit: 09d7d8a19ed4e35d99c1b82e5e86aa135ea63c50
Parents: ebe2fca
Author: rgodfrey <rg...@apache.org>
Authored: Wed Nov 8 17:48:20 2017 +0100
Committer: rgodfrey <rg...@apache.org>
Committed: Sun Nov 26 16:10:05 2017 +0100

----------------------------------------------------------------------
 .../AutoGeneratedSelfSignedKeyStoreImpl.java    |  95 +-----
 .../qpid/server/security/FileKeyStore.java      |   4 +
 .../qpid/server/security/FileKeyStoreImpl.java  |  25 +-
 .../AuthIdentityConnectionPropertyEnricher.java |  71 +++++
 .../NonBlockingConnectionTLSDelegate.java       |   5 +
 .../security/ssl/QpidBestFitX509KeyManager.java | 202 +++++++++++++
 .../security/ssl/QpidClientX509KeyManager.java  | 125 --------
 .../security/ssl/QpidServerX509KeyManager.java  | 107 +++++++
 .../transport/network/security/ssl/SSLUtil.java | 301 ++++++++++++++++---
 .../qpid/systest/keystore/FileKeyStoreTest.java | 234 ++++++++++++++
 10 files changed, 912 insertions(+), 257 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/09d7d8a1/broker-core/src/main/java/org/apache/qpid/server/security/AutoGeneratedSelfSignedKeyStoreImpl.java
----------------------------------------------------------------------
diff --git a/broker-core/src/main/java/org/apache/qpid/server/security/AutoGeneratedSelfSignedKeyStoreImpl.java b/broker-core/src/main/java/org/apache/qpid/server/security/AutoGeneratedSelfSignedKeyStoreImpl.java
index 66d66e8..4757904 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/security/AutoGeneratedSelfSignedKeyStoreImpl.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/security/AutoGeneratedSelfSignedKeyStoreImpl.java
@@ -25,9 +25,7 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.Writer;
-import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.net.InetAddress;
 import java.net.InterfaceAddress;
 import java.net.NetworkInterface;
@@ -84,22 +82,6 @@ public class AutoGeneratedSelfSignedKeyStoreImpl
 
     private static final SecureRandom RANDOM = new SecureRandom();
 
-
-    private static Constructor<?> CONSTRUCTOR;
-    private static Method GENERATE_METHOD;
-    private static Method GET_PRIVATE_KEY_METHOD;
-    private static Method GET_SELF_CERTIFICATE_METHOD;
-    private static Constructor<?> X500_NAME_CONSTRUCTOR;
-    private static Constructor<?> DNS_NAME_CONSTRUCTOR;
-    private static Constructor<?> IP_ADDR_NAME_CONSTRUCTOR;
-    private static Constructor<?> GENERAL_NAMES_CONSTRUCTOR;
-    private static Constructor<?> GENERAL_NAME_CONSTRUCTOR;
-    private static Method ADD_NAME_TO_NAMES_METHOD;
-    private static Constructor<?> ALT_NAMES_CONSTRUCTOR;
-    private static Constructor<?> CERTIFICATE_EXTENSIONS_CONSTRUCTOR;
-    private static Method SET_EXTENSION_METHOD;
-    private static Method EXTENSION_GET_NAME_METHOD;
-
     private final Broker<?> _broker;
     private final EventLogger _eventLogger;
 
@@ -290,11 +272,7 @@ public class AutoGeneratedSelfSignedKeyStoreImpl
     {
         try
         {
-            Object certAndKeyGen = CONSTRUCTOR.newInstance(_keyAlgorithm, _signatureAlgorithm);
-            GENERATE_METHOD.invoke(certAndKeyGen, _keyLength);
-            _privateKey = (PrivateKey) GET_PRIVATE_KEY_METHOD.invoke(certAndKeyGen);
 
-            Object generalNames = GENERAL_NAMES_CONSTRUCTOR.newInstance();
 
             Set<InetAddress> addresses = new HashSet<>();
             for (NetworkInterface networkInterface : Collections.list(NetworkInterface.getNetworkInterfaces()))
@@ -321,23 +299,6 @@ public class AutoGeneratedSelfSignedKeyStoreImpl
                     dnsNames.add(canonicalHostName);
                 }
             }
-            for(String dnsName : dnsNames)
-            {
-                if(dnsName.matches("[\\w&&[^\\d]][\\w\\d.-]*"))
-                {
-                    ADD_NAME_TO_NAMES_METHOD.invoke(generalNames,
-                                                    GENERAL_NAME_CONSTRUCTOR.newInstance(DNS_NAME_CONSTRUCTOR.newInstance(
-                                                            dnsName)));
-                }
-            }
-
-            for(InetAddress inetAddress : addresses)
-            {
-                ADD_NAME_TO_NAMES_METHOD.invoke(generalNames, GENERAL_NAME_CONSTRUCTOR.newInstance(IP_ADDR_NAME_CONSTRUCTOR.newInstance(inetAddress.getHostAddress())));
-            }
-            Object altNamesExtension = ALT_NAMES_CONSTRUCTOR.newInstance(generalNames);
-            Object certificateExtensions = CERTIFICATE_EXTENSIONS_CONSTRUCTOR.newInstance();
-            SET_EXTENSION_METHOD.invoke(certificateExtensions, EXTENSION_GET_NAME_METHOD.invoke(altNamesExtension), altNamesExtension);
 
             long startTime = System.currentTimeMillis();
             Calendar calendar = Calendar.getInstance();
@@ -345,8 +306,17 @@ public class AutoGeneratedSelfSignedKeyStoreImpl
             calendar.add(Calendar.MONTH, _durationInMonths);
             long duration = (calendar.getTimeInMillis() - startTime)/1000;
 
-            _certificate = (X509Certificate) GET_SELF_CERTIFICATE_METHOD.invoke(certAndKeyGen, X500_NAME_CONSTRUCTOR.newInstance("CN=Qpid"), new Date(startTime), duration, certificateExtensions);
-
+            final SSLUtil.KeyCertPair keyCertPair = SSLUtil.generateSelfSignedCertificate(_keyAlgorithm,
+                                                                                          _signatureAlgorithm,
+                                                                                          _keyLength,
+                                                                                          startTime,
+                                                                                          duration,
+                                                                                          "CN=Qpid",
+                                                                                          dnsNames,
+                                                                                          addresses);
+
+            _privateKey = keyCertPair.getPrivateKey();
+            _certificate = keyCertPair.getCertificate();
             _generated = true;
 
         }
@@ -402,48 +372,7 @@ public class AutoGeneratedSelfSignedKeyStoreImpl
 
     static boolean isAvailable()
     {
-        try
-        {
-            Class<?> certAndKeyGenClass;
-            try
-            {
-                certAndKeyGenClass = Class.forName("sun.security.x509.CertAndKeyGen");
-            }
-            catch (ClassNotFoundException e)
-            {
-                certAndKeyGenClass = Class.forName("sun.security.tools.keytool.CertAndKeyGen");
-            }
-
-            final Class<?> x500NameClass = Class.forName("sun.security.x509.X500Name");
-            final Class<?> certificateExtensionsClass = Class.forName("sun.security.x509.CertificateExtensions");
-            final Class<?> generalNamesClass = Class.forName("sun.security.x509.GeneralNames");
-            final Class<?> generalNameClass = Class.forName("sun.security.x509.GeneralName");
-            final Class<?> extensionClass = Class.forName("sun.security.x509.SubjectAlternativeNameExtension");
-
-
-            CONSTRUCTOR = certAndKeyGenClass.getConstructor(String.class, String.class);
-            GENERATE_METHOD = certAndKeyGenClass.getMethod("generate", Integer.TYPE);
-            GET_PRIVATE_KEY_METHOD = certAndKeyGenClass.getMethod("getPrivateKey");
-            GET_SELF_CERTIFICATE_METHOD = certAndKeyGenClass.getMethod("getSelfCertificate", x500NameClass, Date.class, Long.TYPE,
-                                                                       certificateExtensionsClass);
-            X500_NAME_CONSTRUCTOR = x500NameClass.getConstructor(String.class);
-            DNS_NAME_CONSTRUCTOR = Class.forName("sun.security.x509.DNSName").getConstructor(String.class);
-            IP_ADDR_NAME_CONSTRUCTOR = Class.forName("sun.security.x509.IPAddressName").getConstructor(String.class);
-            GENERAL_NAMES_CONSTRUCTOR = generalNamesClass.getConstructor();
-            GENERAL_NAME_CONSTRUCTOR = generalNameClass.getConstructor(Class.forName("sun.security.x509.GeneralNameInterface"));
-            ADD_NAME_TO_NAMES_METHOD =  generalNamesClass.getMethod("add", generalNameClass);
-            ALT_NAMES_CONSTRUCTOR = extensionClass.getConstructor(generalNamesClass);
-            CERTIFICATE_EXTENSIONS_CONSTRUCTOR = certificateExtensionsClass.getConstructor();
-            SET_EXTENSION_METHOD = certificateExtensionsClass.getMethod("set", String.class, Object.class);
-            EXTENSION_GET_NAME_METHOD = extensionClass.getMethod("getName");
-
-            return true;
-        }
-        catch (ClassNotFoundException | LinkageError | NoSuchMethodException e)
-        {
-            return false;
-        }
-
+        return SSLUtil.canGenerateCerts();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/09d7d8a1/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStore.java
----------------------------------------------------------------------
diff --git a/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStore.java b/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStore.java
index 884fbc6..4051ddb 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStore.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStore.java
@@ -36,6 +36,7 @@ public interface FileKeyStore<X extends FileKeyStore<X>> extends KeyStore<X>
     String KEY_STORE_TYPE = "keyStoreType";
     String PASSWORD = "password";
     String STORE_URL = "storeUrl";
+    String USE_HOST_NAME_MATCHING = "useHostNameMatching";
 
     @ManagedContextDefault(name = "keyStoreFile.keyStoreType")
     String DEFAULT_KEYSTORE_TYPE = java.security.KeyStore.getDefaultType();
@@ -64,4 +65,7 @@ public interface FileKeyStore<X extends FileKeyStore<X>> extends KeyStore<X>
 
     @ManagedAttribute( secure = true, mandatory = true )
     String getPassword();
+
+    @ManagedAttribute( defaultValue = "true")
+    boolean isUseHostNameMatching();
 }

http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/09d7d8a1/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStoreImpl.java
----------------------------------------------------------------------
diff --git a/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStoreImpl.java b/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStoreImpl.java
index 4c5ed1f..33090d0 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStoreImpl.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStoreImpl.java
@@ -51,10 +51,11 @@ import org.apache.qpid.server.model.ManagedObject;
 import org.apache.qpid.server.model.ManagedObjectFactoryConstructor;
 import org.apache.qpid.server.model.State;
 import org.apache.qpid.server.model.StateTransition;
+import org.apache.qpid.server.transport.network.security.ssl.QpidBestFitX509KeyManager;
+import org.apache.qpid.server.transport.network.security.ssl.QpidServerX509KeyManager;
+import org.apache.qpid.server.transport.network.security.ssl.SSLUtil;
 import org.apache.qpid.server.util.ServerScopedRuntimeException;
 import org.apache.qpid.server.util.urlstreamhandler.data.Handler;
-import org.apache.qpid.server.transport.network.security.ssl.QpidClientX509KeyManager;
-import org.apache.qpid.server.transport.network.security.ssl.SSLUtil;
 
 @ManagedObject( category = false )
 public class FileKeyStoreImpl extends AbstractKeyStore<FileKeyStoreImpl> implements FileKeyStore<FileKeyStoreImpl>
@@ -70,6 +71,8 @@ public class FileKeyStoreImpl extends AbstractKeyStore<FileKeyStoreImpl> impleme
     private String _keyManagerFactoryAlgorithm;
     @ManagedAttributeField(afterSet = "postSetStoreUrl")
     private String _storeUrl;
+    @ManagedAttributeField
+    private boolean _useHostNameMatching;
     private String _path;
 
     @ManagedAttributeField
@@ -80,6 +83,7 @@ public class FileKeyStoreImpl extends AbstractKeyStore<FileKeyStoreImpl> impleme
         Handler.register();
     }
 
+
     @ManagedObjectFactoryConstructor
     public FileKeyStoreImpl(Map<String, Object> attributes, Broker<?> broker)
     {
@@ -221,6 +225,12 @@ public class FileKeyStoreImpl extends AbstractKeyStore<FileKeyStoreImpl> impleme
         return _password;
     }
 
+    @Override
+    public boolean isUseHostNameMatching()
+    {
+        return _useHostNameMatching;
+    }
+
     public void setPassword(String password)
     {
         _password = password;
@@ -233,11 +243,18 @@ public class FileKeyStoreImpl extends AbstractKeyStore<FileKeyStoreImpl> impleme
         try
         {
             URL url = getUrlFromString(_storeUrl);
-            if (_certificateAlias != null)
+            if(isUseHostNameMatching())
             {
                 return new KeyManager[] {
-                        new QpidClientX509KeyManager( _certificateAlias, url, _keyStoreType, getPassword(),
+                        new QpidBestFitX509KeyManager(_certificateAlias, url, _keyStoreType, getPassword(),
                                                       _keyManagerFactoryAlgorithm)
+                };
+            }
+            else if (_certificateAlias != null)
+            {
+                return new KeyManager[] {
+                        new QpidServerX509KeyManager(_certificateAlias, url, _keyStoreType, getPassword(),
+                                                     _keyManagerFactoryAlgorithm)
                                         };
 
             }

http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/09d7d8a1/broker-core/src/main/java/org/apache/qpid/server/security/auth/AuthIdentityConnectionPropertyEnricher.java
----------------------------------------------------------------------
diff --git a/broker-core/src/main/java/org/apache/qpid/server/security/auth/AuthIdentityConnectionPropertyEnricher.java b/broker-core/src/main/java/org/apache/qpid/server/security/auth/AuthIdentityConnectionPropertyEnricher.java
new file mode 100644
index 0000000..ce48568
--- /dev/null
+++ b/broker-core/src/main/java/org/apache/qpid/server/security/auth/AuthIdentityConnectionPropertyEnricher.java
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.qpid.server.security.auth;
+
+import java.security.Principal;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.server.model.preferences.GenericPrincipal;
+import org.apache.qpid.server.plugin.ConnectionPropertyEnricher;
+import org.apache.qpid.server.plugin.PluggableService;
+import org.apache.qpid.server.security.QpidPrincipal;
+import org.apache.qpid.server.security.group.GroupPrincipal;
+import org.apache.qpid.server.transport.AMQPConnection;
+
+@PluggableService
+public class AuthIdentityConnectionPropertyEnricher implements ConnectionPropertyEnricher
+{
+    private static final Logger LOG = LoggerFactory.getLogger(AuthIdentityConnectionPropertyEnricher.class);
+
+    @Override
+    public Map<String, Object> addConnectionProperties(final AMQPConnection<?> connection,
+                                                       final Map<String, Object> existingProperties)
+    {
+        Map<String,Object> modifiedProperties = new LinkedHashMap<>(existingProperties);
+
+        final Principal principal = connection.getAuthorizedPrincipal();
+        if(principal != null)
+        {
+            GenericPrincipal genericPrincipal = new GenericPrincipal((QpidPrincipal)principal);
+            Map<String,String> claims = new LinkedHashMap<>();
+            claims.put("sub", genericPrincipal.toExternalForm());
+            claims.put("preferred_username", genericPrincipal.getName());
+            modifiedProperties.put("authenticated-identity", claims);
+
+        }
+        Set<GroupPrincipal> groups = connection.getSubject().getPrincipals(GroupPrincipal.class);
+        List<String> groupNames = groups.stream().map(GroupPrincipal::getName).collect(Collectors.toList());
+        modifiedProperties.put("groups", groupNames);
+        return Collections.unmodifiableMap(modifiedProperties);
+    }
+
+
+    @Override
+    public String getType()
+    {
+        return "AUTH_IDENTITY";
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/09d7d8a1/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnectionTLSDelegate.java
----------------------------------------------------------------------
diff --git a/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnectionTLSDelegate.java b/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnectionTLSDelegate.java
index fabd213..3e52716 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnectionTLSDelegate.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnectionTLSDelegate.java
@@ -24,10 +24,12 @@ import java.security.Principal;
 import java.security.cert.Certificate;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
 
+import javax.net.ssl.SNIHostName;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLEngineResult;
 import javax.net.ssl.SSLException;
@@ -99,6 +101,9 @@ public class NonBlockingConnectionTLSDelegate implements NonBlockingConnectionDe
                     if (hostName != null)
                     {
                         _parent.setSelectedHost(hostName);
+                        SSLParameters sslParameters = _sslEngine.getSSLParameters();
+                        sslParameters.setServerNames(Collections.singletonList(new SNIHostName(hostName)));
+                        _sslEngine.setSSLParameters(sslParameters);
                     }
                     _hostChecked = true;
                 }

http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/09d7d8a1/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/QpidBestFitX509KeyManager.java
----------------------------------------------------------------------
diff --git a/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/QpidBestFitX509KeyManager.java b/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/QpidBestFitX509KeyManager.java
new file mode 100644
index 0000000..74f2738
--- /dev/null
+++ b/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/QpidBestFitX509KeyManager.java
@@ -0,0 +1,202 @@
+/*
+ * 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * 
+ */
+package org.apache.qpid.server.transport.network.security.ssl;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIServerName;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.X509ExtendedKeyManager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class QpidBestFitX509KeyManager extends X509ExtendedKeyManager
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(QpidBestFitX509KeyManager.class);
+    private static final long SIX_HOURS = 6L * 60L * 60L * 1000L;
+
+    private final X509ExtendedKeyManager _delegate;
+    private final String _defaultAlias;
+    private final List<String> _aliases;
+
+    public QpidBestFitX509KeyManager(String defaultAlias,
+                                     URL keyStoreUrl, String keyStoreType,
+                                     String keyStorePassword, String keyManagerFactoryAlgorithmName) throws GeneralSecurityException, IOException
+    {
+        KeyStore ks = SSLUtil.getInitializedKeyStore(keyStoreUrl,keyStorePassword,keyStoreType);
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithmName);
+        kmf.init(ks, keyStorePassword.toCharArray());
+        List<String> aliases = new ArrayList<>();
+        for(String alias : Collections.list(ks.aliases()))
+        {
+            if(ks.isKeyEntry(alias))
+            {
+                aliases.add(alias);
+            }
+        }
+        _aliases = Collections.unmodifiableList(aliases);
+        _delegate = (X509ExtendedKeyManager)kmf.getKeyManagers()[0];
+        _defaultAlias = defaultAlias;
+    }
+
+    
+
+    @Override
+    public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket)
+    {
+        return  _defaultAlias == null ? _delegate.chooseClientAlias(keyType, issuers, socket) : _defaultAlias;
+    }
+
+    @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)
+    {
+        return _delegate.getClientAliases(keyType, issuers);
+    }
+
+    @Override
+    public PrivateKey getPrivateKey(String alias)
+    {
+        return _delegate.getPrivateKey(alias);
+    }
+
+    @Override
+    public String[] getServerAliases(String keyType, Principal[] issuers)
+    {
+        return _delegate.getServerAliases(keyType, issuers);
+    }
+
+    @Override
+    public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine)
+    {
+        return _defaultAlias == null ? _delegate.chooseEngineClientAlias(keyType, issuers, engine) : _defaultAlias;
+    }
+
+    @Override
+    public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine)
+    {
+        Date currentDate = new Date();
+        final List<SNIServerName> serverNames = engine.getSSLParameters().getServerNames();
+        if(serverNames.isEmpty())
+        {
+            return getDefaultServerAlias(keyType, issuers, engine);
+        }
+        else
+        {
+            List<String> validAliases = new ArrayList<>();
+            List<String> invalidAliases = new ArrayList<>();
+
+            for(SNIServerName serverName : engine.getSSLParameters().getServerNames())
+            {
+                if(serverName instanceof SNIHostName)
+                {
+                    for(String alias : _aliases)
+                    {
+                        if(keyType.equalsIgnoreCase(getPrivateKey(alias).getAlgorithm()))
+                        {
+                            final X509Certificate[] certChain = getCertificateChain(alias);
+                            X509Certificate cert = certChain[0];
+                            if (SSLUtil.checkHostname(((SNIHostName) serverName).getAsciiName(), cert))
+                            {
+                                if (currentDate.after(cert.getNotBefore()) && currentDate.before(cert.getNotAfter()))
+                                {
+                                    validAliases.add(alias);
+                                }
+                                else
+                                {
+                                    invalidAliases.add(alias);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            if(validAliases.isEmpty())
+            {
+                if(invalidAliases.isEmpty())
+                {
+                    return getDefaultServerAlias(keyType, issuers, engine);
+                }
+                else
+                {
+                    // all invalid, we'll just pick one
+                    return invalidAliases.get(0);
+                }
+            }
+            else
+            {
+                if(validAliases.size() > 1)
+                {
+                    // return the first alias which has at least six hours validity before / after the current time
+                    for(String alias : validAliases)
+                    {
+                        final X509Certificate cert = getCertificateChain(alias)[0];
+                        if((currentDate.getTime() - cert.getNotBefore().getTime() > SIX_HOURS)
+                           && (cert.getNotAfter().getTime() - currentDate.getTime() > SIX_HOURS))
+                        {
+                            return alias;
+                        }
+                    }
+
+                }
+                return validAliases.get(0);
+            }
+        }
+    }
+
+    private String getDefaultServerAlias(final String keyType, final Principal[] issuers, final SSLEngine engine)
+    {
+        if(_defaultAlias != null)
+        {
+            return _defaultAlias;
+        }
+        else
+        {
+            return _delegate.chooseEngineServerAlias(keyType, issuers, engine);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/09d7d8a1/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/QpidClientX509KeyManager.java
----------------------------------------------------------------------
diff --git a/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/QpidClientX509KeyManager.java b/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/QpidClientX509KeyManager.java
deleted file mode 100644
index 130e23c..0000000
--- a/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/QpidClientX509KeyManager.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- * 
- *   http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * 
- */
-package org.apache.qpid.server.transport.network.security.ssl;
-
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.X509ExtendedKeyManager;
-import java.io.IOException;
-import java.net.Socket;
-import java.net.URL;
-import java.security.GeneralSecurityException;
-import java.security.KeyStore;
-import java.security.Principal;
-import java.security.PrivateKey;
-import java.security.cert.X509Certificate;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class QpidClientX509KeyManager extends X509ExtendedKeyManager
-{
-    private static final Logger LOGGER = LoggerFactory.getLogger(QpidClientX509KeyManager.class);
-
-    private X509ExtendedKeyManager delegate;
-    private String alias;
-    
-    public QpidClientX509KeyManager(String alias, String keyStorePath, String keyStoreType,
-                           String keyStorePassword, String keyManagerFactoryAlgorithmName) throws GeneralSecurityException, IOException
-    {
-        this.alias = alias;
-        KeyStore ks = SSLUtil.getInitializedKeyStore(keyStorePath,keyStorePassword,keyStoreType);
-        KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithmName);
-        kmf.init(ks, keyStorePassword.toCharArray());
-        this.delegate = (X509ExtendedKeyManager)kmf.getKeyManagers()[0];
-    }
-
-    public QpidClientX509KeyManager(String alias, URL keyStoreUrl, String keyStoreType,
-                           String keyStorePassword, String keyManagerFactoryAlgorithmName) throws GeneralSecurityException, IOException
-    {
-        this.alias = alias;
-        KeyStore ks = SSLUtil.getInitializedKeyStore(keyStoreUrl,keyStorePassword,keyStoreType);
-        KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithmName);
-        kmf.init(ks, keyStorePassword.toCharArray());
-        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];
-    }
-
-
-    @Override
-    public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket)
-    {
-        LOGGER.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)
-    {
-        LOGGER.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);
-    }
-
-    @Override
-    public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine)
-    {
-        LOGGER.debug("chooseEngineClientAlias:Returning alias {}", alias);
-        return alias;
-    }
-
-    @Override
-    public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine)
-    {
-        return delegate.chooseEngineServerAlias(keyType, issuers, engine);
-    }
-}

http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/09d7d8a1/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/QpidServerX509KeyManager.java
----------------------------------------------------------------------
diff --git a/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/QpidServerX509KeyManager.java b/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/QpidServerX509KeyManager.java
new file mode 100644
index 0000000..0abf44b
--- /dev/null
+++ b/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/QpidServerX509KeyManager.java
@@ -0,0 +1,107 @@
+/*
+ * 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * 
+ */
+package org.apache.qpid.server.transport.network.security.ssl;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class QpidServerX509KeyManager extends X509ExtendedKeyManager
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(QpidServerX509KeyManager.class);
+
+    private X509ExtendedKeyManager _delegate;
+    private String _alias;
+
+    public QpidServerX509KeyManager(String alias, URL keyStoreUrl, String keyStoreType,
+                                    String keyStorePassword, String keyManagerFactoryAlgorithmName) throws GeneralSecurityException, IOException
+    {
+        this._alias = alias;
+        KeyStore ks = SSLUtil.getInitializedKeyStore(keyStoreUrl,keyStorePassword,keyStoreType);
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithmName);
+        kmf.init(ks, keyStorePassword.toCharArray());
+        this._delegate = (X509ExtendedKeyManager)kmf.getKeyManagers()[0];
+    }
+
+
+    @Override
+    public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket)
+    {
+        return _alias == null ? _delegate.chooseClientAlias(keyType, issuers, socket) : _alias;
+    }
+
+    @Override
+    public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket)
+    {
+        LOGGER.debug("chooseServerAlias:Returning alias {}", _alias);
+        return _alias;
+    }
+
+    @Override
+    public X509Certificate[] getCertificateChain(String alias)
+    {
+        return _delegate.getCertificateChain(alias);
+    }
+
+    @Override
+    public String[] getClientAliases(String keyType, Principal[] issuers)
+    {
+        return _delegate.getClientAliases(keyType, issuers);
+    }
+
+    @Override
+    public PrivateKey getPrivateKey(String alias)
+    {
+        return _delegate.getPrivateKey(alias);
+    }
+
+    @Override
+    public String[] getServerAliases(String keyType, Principal[] issuers)
+    {
+        LOGGER.debug("getServerAliases:Returning alias {}", _alias);
+        return new String[]{_alias};
+    }
+
+    @Override
+    public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine)
+    {
+        return _alias == null ? _delegate.chooseEngineClientAlias(keyType, issuers, engine) : _alias;
+    }
+
+    @Override
+    public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine)
+    {
+        LOGGER.debug("chooseEngineServerAlias:Returning alias {}", _alias);
+        return _alias;
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/09d7d8a1/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/SSLUtil.java
----------------------------------------------------------------------
diff --git a/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/SSLUtil.java b/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/SSLUtil.java
index 4eadb92..9e6ab0a 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/SSLUtil.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/SSLUtil.java
@@ -27,7 +27,11 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.StringReader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.math.BigInteger;
+import java.net.InetAddress;
 import java.net.URL;
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
@@ -38,6 +42,7 @@ import java.security.KeyStore;
 import java.security.NoSuchAlgorithmException;
 import java.security.Principal;
 import java.security.PrivateKey;
+import java.security.SecureRandom;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
@@ -49,8 +54,10 @@ import java.security.spec.RSAPrivateCrtKeySpec;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
@@ -80,7 +87,105 @@ public class SSLUtil
     private static final Logger LOGGER = LoggerFactory.getLogger(SSLUtil.class);
 
     private static final Integer DNS_NAME_TYPE = 2;
-    public static final String[] TLS_PROTOCOL_PREFERENCES = new String[]{"TLSv1.2", "TLSv1.1", "TLS", "TLSv1"};
+    private static final String[] TLS_PROTOCOL_PREFERENCES = new String[]{"TLSv1.2", "TLSv1.1", "TLS", "TLSv1"};
+
+
+    private static final SecureRandom RANDOM = new SecureRandom();
+
+
+    private static final Constructor<?> CONSTRUCTOR;
+    private static final Method GENERATE_METHOD;
+    private static final Method GET_PRIVATE_KEY_METHOD;
+    private static final Method GET_SELF_CERTIFICATE_METHOD;
+    private static final Constructor<?> X500_NAME_CONSTRUCTOR;
+    private static final Constructor<?> DNS_NAME_CONSTRUCTOR;
+    private static final Constructor<?> IP_ADDR_NAME_CONSTRUCTOR;
+    private static final Constructor<?> GENERAL_NAMES_CONSTRUCTOR;
+    private static final Constructor<?> GENERAL_NAME_CONSTRUCTOR;
+    private static final Method ADD_NAME_TO_NAMES_METHOD;
+    private static final Constructor<?> ALT_NAMES_CONSTRUCTOR;
+    private static final Constructor<?> CERTIFICATE_EXTENSIONS_CONSTRUCTOR;
+    private static final Method SET_EXTENSION_METHOD;
+    private static final Method EXTENSION_GET_NAME_METHOD;
+    private static final boolean CAN_GENERATE_CERTS;
+
+
+    static
+    {
+
+        Constructor<?> constructor = null;
+        Method generateMethod = null;
+        Method getPrivateKeyMethod = null;
+        Method getSelfCertificateMethod = null;
+        Constructor<?> x500NameConstructor = null;
+        Constructor<?> dnsNameConstructor = null;
+        Constructor<?> ipAddrNameConstructor = null;
+        Constructor<?> generalNamesConstructor = null;
+        Constructor<?> generalNameConstructor = null;
+        Method addNameToNamesMethod = null;
+        Constructor<?> altNamesConstructor = null;
+        Constructor<?> certificateExtensionsConstructor = null;
+        Method setExtensionMethod = null;
+        Method extensionGetNameMethod = null;
+        boolean canGenerateCerrts = false;
+
+        try
+        {
+            Class<?> certAndKeyGenClass;
+            try
+            {
+                certAndKeyGenClass = Class.forName("sun.security.x509.CertAndKeyGen");
+            }
+            catch (ClassNotFoundException e)
+            {
+                certAndKeyGenClass = Class.forName("sun.security.tools.keytool.CertAndKeyGen");
+            }
+
+            final Class<?> x500NameClass = Class.forName("sun.security.x509.X500Name");
+            final Class<?> certificateExtensionsClass = Class.forName("sun.security.x509.CertificateExtensions");
+            final Class<?> generalNamesClass = Class.forName("sun.security.x509.GeneralNames");
+            final Class<?> generalNameClass = Class.forName("sun.security.x509.GeneralName");
+            final Class<?> extensionClass = Class.forName("sun.security.x509.SubjectAlternativeNameExtension");
+
+            constructor = certAndKeyGenClass.getConstructor(String.class, String.class);
+            generateMethod = certAndKeyGenClass.getMethod("generate", Integer.TYPE);
+            getPrivateKeyMethod = certAndKeyGenClass.getMethod("getPrivateKey");
+            getSelfCertificateMethod = certAndKeyGenClass.getMethod("getSelfCertificate", x500NameClass,
+                                                                    Date.class, Long.TYPE, certificateExtensionsClass);
+            x500NameConstructor = x500NameClass.getConstructor(String.class);
+            dnsNameConstructor = Class.forName("sun.security.x509.DNSName").getConstructor(String.class);
+            ipAddrNameConstructor = Class.forName("sun.security.x509.IPAddressName").getConstructor(String.class);
+            generalNamesConstructor = generalNamesClass.getConstructor();
+            generalNameConstructor = generalNameClass.getConstructor(Class.forName("sun.security.x509.GeneralNameInterface"));
+            addNameToNamesMethod = generalNamesClass.getMethod("add", generalNameClass);
+            altNamesConstructor = extensionClass.getConstructor(generalNamesClass);
+            certificateExtensionsConstructor = certificateExtensionsClass.getConstructor();
+            setExtensionMethod = certificateExtensionsClass.getMethod("set", String.class, Object.class);
+            extensionGetNameMethod = extensionClass.getMethod("getName");
+            canGenerateCerrts = true;
+
+        }
+        catch (ClassNotFoundException | LinkageError | NoSuchMethodException e)
+        {
+            // ignore
+        }
+        GET_SELF_CERTIFICATE_METHOD = getSelfCertificateMethod;
+        CONSTRUCTOR = constructor;
+        GENERATE_METHOD = generateMethod;
+        GET_PRIVATE_KEY_METHOD = getPrivateKeyMethod;
+        X500_NAME_CONSTRUCTOR = x500NameConstructor;
+        DNS_NAME_CONSTRUCTOR = dnsNameConstructor;
+        IP_ADDR_NAME_CONSTRUCTOR = ipAddrNameConstructor;
+        GENERAL_NAMES_CONSTRUCTOR = generalNamesConstructor;
+        GENERAL_NAME_CONSTRUCTOR = generalNameConstructor;
+        ADD_NAME_TO_NAMES_METHOD = addNameToNamesMethod;
+        ALT_NAMES_CONSTRUCTOR = altNamesConstructor;
+        CERTIFICATE_EXTENSIONS_CONSTRUCTOR = certificateExtensionsConstructor;
+        SET_EXTENSION_METHOD = setExtensionMethod;
+        EXTENSION_GET_NAME_METHOD = extensionGetNameMethod;
+        CAN_GENERATE_CERTS = canGenerateCerrts;
+    }
+
 
     private SSLUtil()
     {
@@ -109,59 +214,17 @@ public class SSLUtil
 
     public static void verifyHostname(final String hostnameExpected, final X509Certificate cert)
     {
-        Principal p = cert.getSubjectDN();
 
-        SortedSet<String> names = new TreeSet<>();
-        String dn = p.getName();
         try
         {
-            LdapName ldapName = new LdapName(dn);
-            for (Rdn part : ldapName.getRdns())
-            {
-                if (part.getType().equalsIgnoreCase("CN"))
-                {
-                    names.add(part.getValue().toString());
-                    break;
-                }
-            }
-
-            if(cert.getSubjectAlternativeNames() != null)
-            {
-                for (List<?> entry : cert.getSubjectAlternativeNames())
-                {
-                    if (DNS_NAME_TYPE.equals(entry.get(0)))
-                    {
-                        names.add((String) entry.get(1));
-                    }
-                }
-            }
+            SortedSet<String> names = getNamesFromCert(cert);
 
             if (names.isEmpty())
             {
                 throw new TransportException("SSL hostname verification failed. Certificate for did not contain CN or DNS subjectAlt");
             }
 
-            boolean match = false;
-
-            final String hostName = hostnameExpected.trim().toLowerCase();
-            for (String cn : names)
-            {
-
-                boolean doWildcard = cn.startsWith("*.") &&
-                                     cn.lastIndexOf('.') >= 3 &&
-                                     !cn.matches("\\*\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
-
-
-                match = doWildcard
-                        ? hostName.endsWith(cn.substring(1)) && hostName.indexOf(".") == (1 + hostName.length() - cn.length())
-                        : hostName.equals(cn);
-
-                if (match)
-                {
-                    break;
-                }
-
-            }
+            boolean match = verifyHostname(hostnameExpected, names);
             if (!match)
             {
                 throw new TransportException("SSL hostname verification failed." +
@@ -172,6 +235,8 @@ public class SSLUtil
         }
         catch (InvalidNameException e)
         {
+            Principal p = cert.getSubjectDN();
+            String dn = p.getName();
             throw new TransportException("SSL hostname verification failed. Could not parse name " + dn, e);
         }
         catch (CertificateParsingException e)
@@ -180,6 +245,73 @@ public class SSLUtil
         }
     }
 
+    public static boolean checkHostname(String hostname, X509Certificate cert)
+    {
+        try
+        {
+            return verifyHostname(hostname, getNamesFromCert(cert));
+        }
+        catch (InvalidNameException | CertificateParsingException e)
+        {
+            return false;
+        }
+    }
+
+    private static boolean verifyHostname(final String hostnameExpected, final SortedSet<String> names)
+    {
+        boolean match = false;
+
+        final String hostName = hostnameExpected.trim().toLowerCase();
+        for (String cn : names)
+        {
+
+            boolean doWildcard = cn.startsWith("*.") &&
+                                 cn.lastIndexOf('.') >= 3 &&
+                                 !cn.matches("\\*\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
+
+
+            match = doWildcard
+                    ? hostName.endsWith(cn.substring(1)) && hostName.indexOf(".") == (1 + hostName.length() - cn.length())
+                    : hostName.equals(cn);
+
+            if (match)
+            {
+                break;
+            }
+
+        }
+        return match;
+    }
+
+    private static SortedSet<String> getNamesFromCert(final X509Certificate cert)
+            throws InvalidNameException, CertificateParsingException
+    {
+        Principal p = cert.getSubjectDN();
+        String dn = p.getName();
+        SortedSet<String> names = new TreeSet<>();
+        LdapName ldapName = new LdapName(dn);
+        for (Rdn part : ldapName.getRdns())
+        {
+            if (part.getType().equalsIgnoreCase("CN"))
+            {
+                names.add(part.getValue().toString());
+                break;
+            }
+        }
+
+        if(cert.getSubjectAlternativeNames() != null)
+        {
+            for (List<?> entry : cert.getSubjectAlternativeNames())
+            {
+                if (DNS_NAME_TYPE.equals(entry.get(0)))
+                {
+                    names.add((String) entry.get(1));
+                }
+            }
+        }
+        return names;
+    }
+
     public static String getIdFromSubjectDN(String dn)
     {
         String cnStr = null;
@@ -818,4 +950,83 @@ public class SSLUtil
         }
         return sslContext;
     }
+
+    public static boolean canGenerateCerts()
+    {
+        return CAN_GENERATE_CERTS;
+    }
+
+    public static KeyCertPair generateSelfSignedCertificate(final String keyAlgorithm,
+                                                            final String signatureAlgorithm,
+                                                            final int keyLength,
+                                                            long startTime,
+                                                            long duration,
+                                                            String x500Name,
+                                                            Set<String> dnsNames,
+                                                            Set<InetAddress> addresses)
+            throws IllegalAccessException, InvocationTargetException, InstantiationException
+    {
+        Object certAndKeyGen = CONSTRUCTOR.newInstance(keyAlgorithm, signatureAlgorithm);
+        GENERATE_METHOD.invoke(certAndKeyGen, keyLength);
+        final PrivateKey _privateKey = (PrivateKey) GET_PRIVATE_KEY_METHOD.invoke(certAndKeyGen);
+
+        Object generalNames = GENERAL_NAMES_CONSTRUCTOR.newInstance();
+
+        for(String dnsName : dnsNames)
+        {
+            if(dnsName.matches("[\\w&&[^\\d]][\\w\\d.-]*"))
+            {
+                ADD_NAME_TO_NAMES_METHOD.invoke(generalNames,
+                                                GENERAL_NAME_CONSTRUCTOR.newInstance(DNS_NAME_CONSTRUCTOR.newInstance(
+                                                        dnsName)));
+            }
+        }
+
+        for(InetAddress inetAddress : addresses)
+        {
+            ADD_NAME_TO_NAMES_METHOD.invoke(generalNames, GENERAL_NAME_CONSTRUCTOR.newInstance(IP_ADDR_NAME_CONSTRUCTOR.newInstance(inetAddress.getHostAddress())));
+        }
+        Object certificateExtensions;
+        if(dnsNames.isEmpty() && addresses.isEmpty())
+        {
+            certificateExtensions = null;
+        }
+        else
+        {
+            Object altNamesExtension = ALT_NAMES_CONSTRUCTOR.newInstance(generalNames);
+            certificateExtensions = CERTIFICATE_EXTENSIONS_CONSTRUCTOR.newInstance();
+            SET_EXTENSION_METHOD.invoke(certificateExtensions,
+                                        EXTENSION_GET_NAME_METHOD.invoke(altNamesExtension),
+                                        altNamesExtension);
+        }
+
+        final X509Certificate _certificate = (X509Certificate) GET_SELF_CERTIFICATE_METHOD.invoke(certAndKeyGen,
+                                                                                                  X500_NAME_CONSTRUCTOR
+                                                                                                          .newInstance(x500Name),
+                                                                                                  new Date(startTime),
+                                                                                                  duration,
+                                                                                                  certificateExtensions);
+
+        return new KeyCertPair()
+        {
+            @Override
+            public PrivateKey getPrivateKey()
+            {
+                return _privateKey;
+            }
+
+            @Override
+            public X509Certificate getCertificate()
+            {
+                return _certificate;
+            }
+        };
+
+    }
+
+    public interface KeyCertPair
+    {
+        PrivateKey getPrivateKey();
+        X509Certificate getCertificate();
+    }
 }

http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/09d7d8a1/systests/src/test/java/org/apache/qpid/systest/keystore/FileKeyStoreTest.java
----------------------------------------------------------------------
diff --git a/systests/src/test/java/org/apache/qpid/systest/keystore/FileKeyStoreTest.java b/systests/src/test/java/org/apache/qpid/systest/keystore/FileKeyStoreTest.java
new file mode 100644
index 0000000..77a6436
--- /dev/null
+++ b/systests/src/test/java/org/apache/qpid/systest/keystore/FileKeyStoreTest.java
@@ -0,0 +1,234 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.qpid.systest.keystore;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.net.InetSocketAddress;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.qpid.server.model.DefaultVirtualHostAlias;
+import org.apache.qpid.server.model.Port;
+import org.apache.qpid.server.model.Transport;
+import org.apache.qpid.server.model.VirtualHostAlias;
+import org.apache.qpid.server.model.VirtualHostNameAlias;
+import org.apache.qpid.server.security.FileKeyStore;
+import org.apache.qpid.server.transport.network.security.ssl.SSLUtil;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.test.utils.TestBrokerConfiguration;
+
+public class FileKeyStoreTest extends QpidBrokerTestCase
+{
+    private File _keyStoreFile;
+    private SSLUtil.KeyCertPair _fooValid;
+    private SSLUtil.KeyCertPair _fooInvalid;
+    private SSLUtil.KeyCertPair _barInvalid;
+    private TrustManager[] _clientTrustManagers;
+
+    @Override
+    public void setUp() throws Exception
+    {
+        setSystemProperty("javax.net.debug", "ssl");
+        if(SSLUtil.canGenerateCerts())
+        {
+
+            _fooValid = SSLUtil.generateSelfSignedCertificate("RSA",
+                                                              "SHA256WithRSA",
+                                                              2048,
+                                                              Instant.now().minus(1, ChronoUnit.DAYS).toEpochMilli(),
+                                                              Duration.of(365, ChronoUnit.DAYS).getSeconds(),
+                                                              "CN=foo",
+                                                              Collections.emptySet(),
+                                                              Collections.emptySet());
+            _fooInvalid = SSLUtil.generateSelfSignedCertificate("RSA",
+                                                                "SHA256WithRSA",
+                                                                2048,
+                                                                Instant.now().plus(1, ChronoUnit.HOURS).toEpochMilli(),
+                                                                Duration.of(365, ChronoUnit.DAYS).getSeconds(),
+                                                                "CN=foo",
+                                                                Collections.emptySet(),
+                                                                Collections.emptySet());
+
+            _barInvalid = SSLUtil.generateSelfSignedCertificate("RSA",
+                                                                "SHA256WithRSA",
+                                                                2048,
+                                                                Instant.now().plus(1, ChronoUnit.HOURS).toEpochMilli(),
+                                                                Duration.of(365, ChronoUnit.DAYS).getSeconds(),
+                                                                "CN=Qpid",
+                                                                Collections.singleton("bar"),
+                                                                Collections.emptySet());
+
+            java.security.KeyStore inMemoryKeyStore =
+                    java.security.KeyStore.getInstance(java.security.KeyStore.getDefaultType());
+
+            inMemoryKeyStore.load(null, "password".toCharArray());
+            inMemoryKeyStore.setKeyEntry("foovalid",
+                                         _fooValid.getPrivateKey(),
+                                         "password".toCharArray(),
+                                         new X509Certificate[]{_fooValid.getCertificate()});
+
+            inMemoryKeyStore.setKeyEntry("fooinvalid",
+                                         _fooInvalid.getPrivateKey(),
+                                         "password".toCharArray(),
+                                         new X509Certificate[]{_fooInvalid.getCertificate()});
+
+            inMemoryKeyStore.setKeyEntry("barinvalid",
+                                         _barInvalid.getPrivateKey(),
+                                         "password".toCharArray(),
+                                         new X509Certificate[]{_barInvalid.getCertificate()});
+
+            _keyStoreFile = File.createTempFile("keyStore", "jks");
+            try (FileOutputStream os = new FileOutputStream(_keyStoreFile))
+            {
+                inMemoryKeyStore.store(os, "password".toCharArray());
+            }
+
+        }
+        super.setUp();
+    }
+
+    @Override
+    public void startDefaultBroker() throws Exception
+    {
+        // Do broker startup in tests
+    }
+
+    private void doBrokerStartup(boolean useMatching, String defaultAlias) throws Exception
+    {
+        getDefaultBrokerConfiguration().setObjectAttribute(org.apache.qpid.server.model.KeyStore.class, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE,
+                                                           FileKeyStore.STORE_URL, _keyStoreFile.toURI().toURL().toString());
+
+        getDefaultBrokerConfiguration().setObjectAttribute(org.apache.qpid.server.model.KeyStore.class, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE,
+                                                           FileKeyStore.PASSWORD, "password");
+        getDefaultBrokerConfiguration().setObjectAttribute(org.apache.qpid.server.model.KeyStore.class, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE,
+                                                           FileKeyStore.USE_HOST_NAME_MATCHING, String.valueOf(useMatching));
+
+        getDefaultBrokerConfiguration().setObjectAttribute(org.apache.qpid.server.model.KeyStore.class, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE,
+                                                           FileKeyStore.CERTIFICATE_ALIAS, defaultAlias);
+
+        Map<String, Object> sslPortAttributes = new HashMap<>();
+        sslPortAttributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL));
+        sslPortAttributes.put(Port.PORT, DEFAULT_SSL_PORT);
+        sslPortAttributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER);
+        sslPortAttributes.put(Port.NEED_CLIENT_AUTH, false);
+        sslPortAttributes.put(Port.WANT_CLIENT_AUTH, false);
+        sslPortAttributes.put(Port.NAME, TestBrokerConfiguration.ENTRY_NAME_SSL_PORT);
+        sslPortAttributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE);
+        sslPortAttributes.put(Port.TRUST_STORES, Collections.singleton(TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE));
+        sslPortAttributes.put(Port.PROTOCOLS, System.getProperty(TEST_AMQP_PORT_PROTOCOLS_PROPERTY));
+        getDefaultBrokerConfiguration().addObjectConfiguration(Port.class, sslPortAttributes);
+
+        Map<String, Object> aliasAttributes = new HashMap<>();
+        aliasAttributes.put(VirtualHostAlias.NAME, "defaultAlias");
+        aliasAttributes.put(VirtualHostAlias.TYPE, DefaultVirtualHostAlias.TYPE_NAME);
+        getDefaultBrokerConfiguration().addObjectConfiguration(Port.class, TestBrokerConfiguration.ENTRY_NAME_SSL_PORT, VirtualHostAlias.class, aliasAttributes);
+
+        aliasAttributes = new HashMap<>();
+        aliasAttributes.put(VirtualHostAlias.NAME, "nameAlias");
+        aliasAttributes.put(VirtualHostAlias.TYPE, VirtualHostNameAlias.TYPE_NAME);
+        getDefaultBrokerConfiguration().addObjectConfiguration(Port.class, TestBrokerConfiguration.ENTRY_NAME_SSL_PORT, VirtualHostAlias.class, aliasAttributes);
+
+        super.startDefaultBroker();
+    }
+
+    public void testValidCertChosen() throws Exception
+    {
+        performTest(true, "fooinvalid", "foo", _fooValid);
+    }
+
+    public void testMatchCertChosenEvenIfInvalid() throws Exception
+    {
+        performTest(true, "fooinvalid", "bar", _barInvalid);
+    }
+
+    public void testDefaultCertChose() throws Exception
+    {
+        performTest(true, "fooinvalid", null, _fooInvalid);
+    }
+
+    public void testMatchingCanBeDisabled() throws Exception
+    {
+        performTest(false, "fooinvalid", "foo", _fooInvalid);
+    }
+
+
+    private void performTest(final boolean useMatching,
+                             final String defaultAlias,
+                             final String sniHostName,
+                             final SSLUtil.KeyCertPair expectedCert) throws Exception
+    {
+        if (isJavaBroker() && SSLUtil.canGenerateCerts())
+        {
+            doBrokerStartup(useMatching, defaultAlias);
+            SSLContext context = SSLUtil.tryGetSSLContext();
+            context.init(null,
+                         new TrustManager[]
+                                 {
+                                         new X509TrustManager()
+                                         {
+                                             @Override
+                                             public X509Certificate[] getAcceptedIssuers()
+                                             {
+                                                 return null;
+                                             }
+
+                                             @Override
+                                             public void checkClientTrusted(X509Certificate[] certs, String authType)
+                                             {
+                                             }
+
+                                             @Override
+                                             public void checkServerTrusted(X509Certificate[] certs, String authType)
+                                             {
+                                             }
+                                         }
+                                 },
+                         null);
+
+            SSLSocketFactory socketFactory = context.getSocketFactory();
+            SSLSocket socket = (SSLSocket) socketFactory.createSocket();
+            SSLParameters parameters = socket.getSSLParameters();
+            if(sniHostName != null)
+            {
+                parameters.setServerNames(Collections.singletonList(new SNIHostName(sniHostName)));
+            }
+            socket.setSSLParameters(parameters);
+            InetSocketAddress address =
+                    new InetSocketAddress("localhost", getDefaultBroker().getAmqpTlsPort());
+            socket.connect(address);
+            final Certificate[] certs = socket.getSession().getPeerCertificates();
+            assertEquals(1, certs.length);
+            assertEquals(expectedCert.getCertificate(), certs[0]);
+        }
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org