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