diff options
| author | Robert Gemmell <robbie@apache.org> | 2013-03-14 17:19:20 +0000 |
|---|---|---|
| committer | Robert Gemmell <robbie@apache.org> | 2013-03-14 17:19:20 +0000 |
| commit | 7d3e03d5035e13fd8acc147a0e5d741ebd37b4fa (patch) | |
| tree | 7d71c54b1755f060bd405ec4158ed1703180c344 /qpid/java/common | |
| parent | 098f65fa6eca15ee673392681bcaabcbb049bd04 (diff) | |
| download | qpid-python-7d3e03d5035e13fd8acc147a0e5d741ebd37b4fa.tar.gz | |
QPID-4636: add support for a broker 'peerStore' that can be used to perform SSL client auth based on specific 'trusted peer' certs existing in it, rather than via use of a trusted CA cert.
Applied patch from Michal Zerola
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1456554 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java/common')
3 files changed, 275 insertions, 20 deletions
diff --git a/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java b/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java index b2967bb0bb..01381ad23f 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java @@ -21,6 +21,8 @@ package org.apache.qpid.ssl; import org.apache.qpid.transport.network.security.ssl.QpidClientX509KeyManager; +import org.apache.qpid.transport.network.security.ssl.QpidMultipleTrustManager; +import org.apache.qpid.transport.network.security.ssl.QpidPeersOnlyTrustManager; import org.apache.qpid.transport.network.security.ssl.SSLUtil; import javax.net.ssl.KeyManager; @@ -28,9 +30,16 @@ import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; +import java.security.cert.X509Certificate; + import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyStore; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; /** * Factory used to create SSLContexts. SSL needs to be configured @@ -40,6 +49,26 @@ import java.security.KeyStore; public class SSLContextFactory { public static final String TRANSPORT_LAYER_SECURITY_CODE = "TLS"; + + public static class TrustStoreWrapper + { + private final String trustStorePath; + private final String trustStorePassword; + private final String trustStoreType; + private final Boolean trustStorePeersOnly; + private String trustManagerFactoryAlgorithm; + + public TrustStoreWrapper(final String trustStorePath, final String trustStorePassword, + final String trustStoreType, final Boolean trustStorePeersOnly, + final String trustManagerFactoryAlgorithm) + { + this.trustStorePath = trustStorePath; + this.trustStorePassword = trustStorePassword; + this.trustStoreType = trustStoreType; + this.trustStorePeersOnly = trustStorePeersOnly; + this.trustManagerFactoryAlgorithm = trustManagerFactoryAlgorithm; + } + } private SSLContextFactory() { @@ -51,25 +80,34 @@ public class SSLContextFactory final String keyManagerFactoryAlgorithm) throws GeneralSecurityException, IOException { - return buildContext(null, null, null, null, keyStorePath, keyStorePassword, keyStoreType, - keyManagerFactoryAlgorithm, null); + return buildContext(Collections.<TrustStoreWrapper>emptyList(), keyStorePath, + keyStorePassword, keyStoreType, keyManagerFactoryAlgorithm, null); + } + + public static SSLContext buildClientContext(Collection<TrustStoreWrapper> trustStores, + final String keyStorePath, final String keyStorePassword, + final String keyStoreType, final String keyManagerFactoryAlgorithm, + final String certAlias) throws GeneralSecurityException, IOException + { + return buildContext(trustStores, keyStorePath, keyStorePassword, keyStoreType, + keyManagerFactoryAlgorithm, certAlias); } public static SSLContext buildClientContext(final String trustStorePath, final String trustStorePassword, final String trustStoreType, - final String trustManagerFactoryAlgorithm, final String keyStorePath, - final String keyStorePassword, final String keyStoreType, + final String trustManagerFactoryAlgorithm, final String keyStorePath, + final String keyStorePassword, final String keyStoreType, final String keyManagerFactoryAlgorithm, final String certAlias) throws GeneralSecurityException, IOException { - return buildContext(trustStorePath, trustStorePassword, trustStoreType, - trustManagerFactoryAlgorithm, keyStorePath, keyStorePassword, keyStoreType, - keyManagerFactoryAlgorithm, certAlias); + TrustStoreWrapper trstWrapper = new TrustStoreWrapper(trustStorePath, trustStorePassword, + trustStoreType, Boolean.FALSE, + trustManagerFactoryAlgorithm); + return buildContext(Collections.singletonList(trstWrapper), keyStorePath, + keyStorePassword, keyStoreType, keyManagerFactoryAlgorithm, certAlias); } - private static SSLContext buildContext(final String trustStorePath, - final String trustStorePassword, final String trustStoreType, - final String trustManagerFactoryAlgorithm, + private static SSLContext buildContext(final Collection<TrustStoreWrapper> trstWrappers, final String keyStorePath, final String keyStorePassword, final String keyStoreType, final String keyManagerFactoryAlgorithm, final String certAlias) @@ -81,21 +119,54 @@ public class SSLContextFactory final TrustManager[] trustManagers; final KeyManager[] keyManagers; - - if (trustStorePath != null) + + final Collection<TrustManager> trustManagersCol = new ArrayList<TrustManager>(); + final QpidMultipleTrustManager mulTrustManager = new QpidMultipleTrustManager(); + for (TrustStoreWrapper tsw : trstWrappers) { - final KeyStore ts = SSLUtil.getInitializedKeyStore(trustStorePath, - trustStorePassword, trustStoreType); - final TrustManagerFactory tmf = TrustManagerFactory - .getInstance(trustManagerFactoryAlgorithm); - tmf.init(ts); - - trustManagers = tmf.getTrustManagers(); + if (tsw.trustStorePath != null) + { + final KeyStore ts = SSLUtil.getInitializedKeyStore(tsw.trustStorePath, + tsw.trustStorePassword, tsw.trustStoreType); + final TrustManagerFactory tmf = TrustManagerFactory + .getInstance(tsw.trustManagerFactoryAlgorithm); + tmf.init(ts); + TrustManager[] delegateManagers = tmf.getTrustManagers(); + for (TrustManager tm : delegateManagers) + { + if (tm instanceof X509TrustManager) + { + if (Boolean.TRUE.equals(tsw.trustStorePeersOnly)) + { + // truststore is supposed to trust only clients which peers certificates + // are directly in the store. CA signing will not be considered. + mulTrustManager.addTrustManager(new QpidPeersOnlyTrustManager(ts, (X509TrustManager) tm)); + } + else + { + mulTrustManager.addTrustManager((X509TrustManager) tm); + } + } + else + { + trustManagersCol.add(tm); + } + } + } } - else + if (! mulTrustManager.isEmpty()) + { + trustManagersCol.add(mulTrustManager); + } + + if (trustManagersCol.isEmpty()) { trustManagers = null; } + else + { + trustManagers = trustManagersCol.toArray(new TrustManager[trustManagersCol.size()]); + } if (keyStorePath != null) { diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidMultipleTrustManager.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidMultipleTrustManager.java new file mode 100644 index 0000000000..0705f31fcb --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidMultipleTrustManager.java @@ -0,0 +1,103 @@ +/* +* +* 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.transport.network.security.ssl; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import javax.net.ssl.X509TrustManager; + +/** + * Supports multiple X509TrustManager(s). Check succeeds if any of the + * underlying managers succeeds. + */ +public class QpidMultipleTrustManager implements X509TrustManager { + + private List<X509TrustManager> trustManagers; + + public QpidMultipleTrustManager() { + this.trustManagers = new ArrayList<X509TrustManager>(); + } + + public boolean isEmpty() + { + return trustManagers.isEmpty(); + } + + public void addTrustManager(final X509TrustManager trustManager) + { + this.trustManagers.add(trustManager); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + for (X509TrustManager trustManager : this.trustManagers) + { + try + { + trustManager.checkClientTrusted(chain, authType); + // this trustManager check succeeded, no need to check another one + return; + } + catch(CertificateException ex) + { + // do nothing, try another one in a loop + } + } + // no trustManager call succeeded, throw an exception + throw new CertificateException(); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + for (X509TrustManager trustManager : this.trustManagers) + { + try + { + trustManager.checkServerTrusted(chain, authType); + // this trustManager check succeeded, no need to check another one + return; + } + catch(CertificateException ex) + { + // do nothing, try another one in a loop + } + } + // no trustManager call succeeded, throw an exception + throw new CertificateException(); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + final Collection<X509Certificate> accIssuersCol = new ArrayList<X509Certificate>(); + for (X509TrustManager trustManager : this.trustManagers) + { + accIssuersCol.addAll(Arrays.asList(trustManager.getAcceptedIssuers())); + } + return accIssuersCol.toArray(new X509Certificate[accIssuersCol.size()]); + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidPeersOnlyTrustManager.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidPeersOnlyTrustManager.java new file mode 100644 index 0000000000..c988ff8d69 --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidPeersOnlyTrustManager.java @@ -0,0 +1,81 @@ +/* +* +* 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.transport.network.security.ssl; + +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +import javax.net.ssl.X509TrustManager; + +/** + * TrustManager implementation which accepts the client certificate + * only if the underlying check by the delegate pass through and + * the certificate is physically saved in the truststore. + */ +public class QpidPeersOnlyTrustManager implements X509TrustManager { + + final private KeyStore ts; + final private X509TrustManager delegate; + final List<Certificate> trustedCerts = new ArrayList<Certificate>(); + + public QpidPeersOnlyTrustManager(KeyStore ts, X509TrustManager trustManager) throws KeyStoreException { + this.ts = ts; + this.delegate = trustManager; + Enumeration<String> aliases = this.ts.aliases(); + while (aliases.hasMoreElements()) + { + trustedCerts.add(ts.getCertificate(aliases.nextElement())); + } + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + this.delegate.checkClientTrusted(chain, authType); + for (Certificate serverTrustedCert : this.trustedCerts) + { + // first position in the chain contains the peer's own certificate + if (chain[0].equals(serverTrustedCert)) + return; // peer's certificate found in the store + } + // peer's certificate was not found in the store, do not trust the client + throw new CertificateException(); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + this.delegate.checkServerTrusted(chain, authType); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + // return empty array since this implementation of TrustManager doesn't + // rely on certification authorities + return new X509Certificate[0]; + } +} |
