And changed the way SSL is setup so that it passes that test case.  Keep the SslContext in 
a ThreadLocal that is passed around to avoid setting it on a static field wich would
get mixed up between brokers.



git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@660591 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Hiram R. Chirino 2008-05-27 16:36:10 +00:00
parent 8ab5f45216
commit 7d87837e08
9 changed files with 329 additions and 71 deletions

View File

@ -21,6 +21,7 @@ import java.io.IOException;
import java.net.URI;
import java.security.KeyManagementException;
import java.security.SecureRandom;
import java.util.Arrays;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;
@ -90,9 +91,15 @@ public class SslBrokerService extends BrokerService {
// If given an SSL URI, use an SSL TransportFactory and configure
// it to use the given key and trust managers.
SslTransportFactory transportFactory = new SslTransportFactory();
transportFactory.setKeyAndTrustManagers(km, tm, random);
return transportFactory.doBind(brokerURI);
SslContext ctx = new SslContext(km, tm, random);
SslContext.setCurrentSslContext(ctx);
try {
return transportFactory.doBind(brokerURI);
} finally {
SslContext.setCurrentSslContext(null);
}
} else {
// Else, business as usual.
return TransportFactory.bind(this, brokerURI);

View File

@ -16,11 +16,16 @@
*/
package org.apache.activemq.broker;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
/**
@ -28,9 +33,34 @@ import javax.net.ssl.TrustManager;
*/
public class SslContext {
protected String protocol = "TLS";
protected String provider = null;
protected List<KeyManager> keyManagers = new ArrayList<KeyManager>();
protected List<TrustManager> trustManagers = new ArrayList<TrustManager>();
protected SecureRandom secureRandom;
private SSLContext sslContext;
private static final ThreadLocal<SslContext> current = new ThreadLocal<SslContext>();
public SslContext() {
}
public SslContext(KeyManager[] km, TrustManager[] tm, SecureRandom random) {
if( km!=null ) {
setKeyManagers(Arrays.asList(km));
}
if( tm!=null ) {
setTrustManagers(Arrays.asList(tm));
}
setSecureRandom(random);
}
static public void setCurrentSslContext(SslContext bs) {
current.set(bs);
}
static public SslContext getCurrentSslContext() {
return current.get();
}
public KeyManager[] getKeyManagersAsArray() {
KeyManager rc[] = new KeyManager[keyManagers.size()];
@ -73,4 +103,33 @@ public class SslContext {
this.secureRandom = secureRandom;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getProvider() {
return provider;
}
public void setProvider(String provider) {
this.provider = provider;
}
public SSLContext getSSLContext() throws NoSuchProviderException, NoSuchAlgorithmException, KeyManagementException {
if( sslContext == null ) {
if( provider == null ) {
sslContext = SSLContext.getInstance(protocol);
} else {
sslContext = SSLContext.getInstance(protocol, provider);
}
sslContext.init(getKeyManagersAsArray(), getTrustManagersAsArray(), getSecureRandom());
}
return sslContext;
}
public void setSSLContext(SSLContext sslContext) {
this.sslContext = sslContext;
}
}

View File

@ -22,6 +22,7 @@ import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.activemq.broker.SslContext;
import org.apache.activemq.command.DiscoveryEvent;
import org.apache.activemq.transport.Transport;
import org.apache.activemq.transport.TransportFactory;
@ -82,22 +83,29 @@ public class DiscoveryNetworkConnector extends NetworkConnector implements Disco
}
URI connectUri = uri;
LOG.info("Establishing network connection between from " + localURIName + " to " + connectUri);
Transport remoteTransport;
try {
remoteTransport = TransportFactory.connect(connectUri);
} catch (Exception e) {
LOG.warn("Could not connect to remote URI: " + localURIName + ": " + e.getMessage());
LOG.debug("Connection failure exception: " + e, e);
return;
}
Transport localTransport;
try {
localTransport = createLocalTransport();
} catch (Exception e) {
ServiceSupport.dispose(remoteTransport);
LOG.warn("Could not connect to local URI: " + localURIName + ": " + e.getMessage());
LOG.debug("Connection failure exception: " + e, e);
return;
// Allows the transport to access the broker's ssl configuration.
SslContext.setCurrentSslContext(getBrokerService().getSslContext());
try {
remoteTransport = TransportFactory.connect(connectUri);
} catch (Exception e) {
LOG.warn("Could not connect to remote URI: " + localURIName + ": " + e.getMessage());
LOG.debug("Connection failure exception: " + e, e);
return;
}
try {
localTransport = createLocalTransport();
} catch (Exception e) {
ServiceSupport.dispose(remoteTransport);
LOG.warn("Could not connect to local URI: " + localURIName + ": " + e.getMessage());
LOG.debug("Connection failure exception: " + e, e);
return;
}
} finally {
SslContext.setCurrentSslContext(null);
}
NetworkBridge bridge = createBridge(localTransport, remoteTransport, event);
bridges.put(uri, bridge);

View File

@ -26,8 +26,10 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import javax.net.ssl.SSLContext;
import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.broker.BrokerServiceAware;
import org.apache.activemq.broker.SslContext;
import org.apache.activemq.util.FactoryFinder;
import org.apache.activemq.util.IOExceptionSupport;
import org.apache.activemq.util.IntrospectionSupport;
@ -117,10 +119,14 @@ public abstract class TransportFactory {
public static TransportServer bind(BrokerService brokerService, URI location) throws IOException {
TransportFactory tf = findTransportFactory(location);
if (brokerService != null && tf instanceof BrokerServiceAware) {
((BrokerServiceAware)tf).setBrokerService(brokerService);
try {
if( brokerService!=null ) {
SslContext.setCurrentSslContext(brokerService.getSslContext());
}
return tf.doBind(location);
} finally {
SslContext.setCurrentSslContext(null);
}
return tf.doBind(location);
}
public Transport doConnect(URI location) throws Exception {

View File

@ -22,6 +22,7 @@ import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
@ -40,6 +41,7 @@ import org.apache.activemq.broker.SslContext;
import org.apache.activemq.openwire.OpenWireFormat;
import org.apache.activemq.transport.InactivityMonitor;
import org.apache.activemq.transport.Transport;
import org.apache.activemq.transport.TransportFactory;
import org.apache.activemq.transport.TransportLoggerFactory;
import org.apache.activemq.transport.TransportServer;
import org.apache.activemq.transport.WireFormatNegotiator;
@ -60,20 +62,10 @@ import org.apache.commons.logging.LogFactory;
* @author David Martin Clavo david(dot)martin(dot)clavo(at)gmail.com (logging improvement modifications)
* @version $Revision$
*/
public class SslTransportFactory extends TcpTransportFactory implements BrokerServiceAware {
public class SslTransportFactory extends TcpTransportFactory {
// The log this uses.,
private static final Log LOG = LogFactory.getLog(SslTransportFactory.class);
// The context used to creat ssl sockets.
private SSLContext sslContext;
/**
* Constructor. Nothing special.
*/
public SslTransportFactory() {
}
/**
* Overriding to use SslTransportServer and allow for proper reflection.
*/
@ -147,47 +139,25 @@ public class SslTransportFactory extends TcpTransportFactory implements BrokerSe
return new SslTransport(wf, (SSLSocketFactory)socketFactory, location, localLocation, false);
}
/**
* Sets the key and trust managers used in constructed socket factories.
* Passes given arguments to SSLContext.init(...).
*
* @param km The sources of authentication keys or null.
* @param tm The sources of peer authentication trust decisions or null.
* @param random The source of randomness for this generator or null.
*/
public void setKeyAndTrustManagers(KeyManager[] km, TrustManager[] tm, SecureRandom random) throws KeyManagementException {
// Killing old context and making a new one just to be safe.
try {
sslContext = SSLContext.getInstance("TLS");
} catch (NoSuchAlgorithmException e) {
// This should not happen unless this class is improperly modified.
throw new RuntimeException("Unknown SSL algorithm encountered.", e);
}
sslContext.init(km, tm, random);
}
public void setBrokerService(BrokerService brokerService) {
SslContext c = brokerService.getSslContext();
if( sslContext == null && c!=null ) {
try {
setKeyAndTrustManagers(c.getKeyManagersAsArray(), c.getTrustManagersAsArray(), c.getSecureRandom());
} catch (KeyManagementException e) {
throw new RuntimeException(e);
}
}
}
/**
* Creates a new SSL ServerSocketFactory. The given factory will use
* user-provided key and trust managers (if the user provided them).
*
* @return Newly created (Ssl)ServerSocketFactory.
* @throws IOException
*/
protected ServerSocketFactory createServerSocketFactory() {
if (sslContext == null) {
return SSLServerSocketFactory.getDefault();
protected ServerSocketFactory createServerSocketFactory() throws IOException {
if( SslContext.getCurrentSslContext()!=null ) {
SslContext ctx = SslContext.getCurrentSslContext();
try {
return ctx.getSSLContext().getServerSocketFactory();
} catch (Exception e) {
throw IOExceptionSupport.create(e);
}
} else {
return sslContext.getServerSocketFactory();
return SSLServerSocketFactory.getDefault();
}
}
@ -196,13 +166,33 @@ public class SslTransportFactory extends TcpTransportFactory implements BrokerSe
* key and trust managers (if the user provided them).
*
* @return Newly created (Ssl)SocketFactory.
* @throws IOException
*/
protected SocketFactory createSocketFactory() {
if (sslContext == null) {
return SSLSocketFactory.getDefault();
protected SocketFactory createSocketFactory() throws IOException {
if( SslContext.getCurrentSslContext()!=null ) {
SslContext ctx = SslContext.getCurrentSslContext();
try {
return ctx.getSSLContext().getSocketFactory();
} catch (Exception e) {
throw IOExceptionSupport.create(e);
}
} else {
return sslContext.getSocketFactory();
return SSLSocketFactory.getDefault();
}
}
/**
*
* @param km
* @param tm
* @param object
* @deprecated "Do not use anymore... using static initializers like this method only allows the JVM to use 1 SSL configuration per broker."
*/
public void setKeyAndTrustManagers(KeyManager[] km, TrustManager[] tm, SecureRandom random) {
SslContext ctx = new SslContext(km, tm, random);
SslContext.setCurrentSslContext(ctx);
}
}

View File

@ -149,11 +149,11 @@ public class TcpTransportFactory extends TransportFactory {
return new TcpTransport(wf, socketFactory, location, localLocation);
}
protected ServerSocketFactory createServerSocketFactory() {
protected ServerSocketFactory createServerSocketFactory() throws IOException {
return ServerSocketFactory.getDefault();
}
protected SocketFactory createSocketFactory() {
protected SocketFactory createSocketFactory() throws IOException {
return SocketFactory.getDefault();
}
}

View File

@ -0,0 +1,137 @@
/**
* 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.activemq.transport.tcp;
import java.net.URI;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import junit.framework.TestCase;
import org.apache.activemq.AMQDeadlockTest3;
import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.broker.TransportConnector;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SslContextNBrokerServiceTest extends TestCase {
private static final transient Log LOG = LogFactory.getLog(SslContextNBrokerServiceTest.class);
private ClassPathXmlApplicationContext context;
Map beansOfType;
public void testConfigurationIsolation() throws Exception {
assertTrue("dummy bean has dummy cert", verifyCredentials("dummy"));
assertTrue("good bean has amq cert", verifyCredentials("activemq.org"));
}
private boolean verifyCredentials(String name) throws Exception {
boolean result = false;
BrokerService broker = getBroker(name);
assertNotNull(name, broker);
broker.start();
try {
result = verifySslCredentials(broker);
} finally {
broker.stop();
}
return result;
}
private boolean verifySslCredentials(BrokerService broker) throws Exception {
TransportConnector connector = broker.getTransportConnectors().get(0);
URI brokerUri = connector.getConnectUri();
SSLContext context = SSLContext.getInstance("TLS");
CertChainCatcher catcher = new CertChainCatcher();
context.init(null, new TrustManager[] {catcher}, null);
SSLSocketFactory factory = context.getSocketFactory();
SSLSocket socket = (SSLSocket)factory.createSocket(brokerUri.getHost(), brokerUri.getPort());
socket.setSoTimeout(5000);
socket.startHandshake();
socket.close();
boolean matches = false;
if (catcher.serverCerts != null) {
for (int i = 0; i < catcher.serverCerts.length; i++) {
X509Certificate cert = catcher.serverCerts[i];
LOG.info(" " + (i + 1) + " Issuer " + cert.getIssuerDN());
}
if (catcher.serverCerts.length > 0) {
String issuer = catcher.serverCerts[0].getIssuerDN().toString();
if (issuer.indexOf(broker.getBrokerName()) != -1) {
matches = true;
}
}
}
return matches;
}
private BrokerService getBroker(String name) {
BrokerService result = null;
Iterator iterator = beansOfType.values().iterator();
while(iterator.hasNext()) {
BrokerService candidate = (BrokerService)iterator.next();
if (candidate.getBrokerName().equals(name)) {
result = candidate;
break;
}
}
return result;
}
protected void setUp() throws Exception {
//System.setProperty("javax.net.debug", "ssl");
Thread.currentThread().setContextClassLoader(SslContextNBrokerServiceTest.class.getClassLoader());
context = new ClassPathXmlApplicationContext("org/apache/activemq/transport/tcp/n-brokers-ssl.xml");
beansOfType = context.getBeansOfType(BrokerService.class);
}
@Override
protected void tearDown() throws Exception {
context.destroy();
}
class CertChainCatcher implements X509TrustManager {
X509Certificate[] serverCerts;
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
serverCerts = arg0;
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
}

Binary file not shown.

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">
<amq:broker useJmx="false" persistent="false" start="false" brokerName="dummy">
<amq:sslContext>
<amq:sslContext
keyStore="dummy.keystore" keyStorePassword="password"/>
</amq:sslContext>
<amq:transportConnectors>
<amq:transportConnector uri="ssl://localhost:62616" />
</amq:transportConnectors>
</amq:broker>
<amq:broker useJmx="false" persistent="false" start="false" brokerName="activemq.org">
<amq:sslContext>
<amq:sslContext
keyStore="server.keystore" keyStorePassword="password"
trustStore="client.keystore" trustStorePassword="password"/>
</amq:sslContext>
<amq:transportConnectors>
<amq:transportConnector uri="ssl://localhost:63616" />
</amq:transportConnectors>
</amq:broker>
</beans>