You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by gt...@apache.org on 2011/02/25 16:25:21 UTC
svn commit: r1074577 - in /activemq/trunk/activemq-core/src:
main/java/org/apache/activemq/ActiveMQSslConnectionFactory.java
test/java/org/apache/activemq/ActiveMQSslConnectionFactoryTest.java
Author: gtully
Date: Fri Feb 25 15:25:21 2011
New Revision: 1074577
URL: http://svn.apache.org/viewvc?rev=1074577&view=rev
Log:
https://issues.apache.org/jira/browse/AMQ-3192 - Add setTrustStore() and setKeyStore() methods to ActiveMQSslConnectionFactory class
Added:
activemq/trunk/activemq-core/src/test/java/org/apache/activemq/ActiveMQSslConnectionFactoryTest.java (with props)
Modified:
activemq/trunk/activemq-core/src/main/java/org/apache/activemq/ActiveMQSslConnectionFactory.java
Modified: activemq/trunk/activemq-core/src/main/java/org/apache/activemq/ActiveMQSslConnectionFactory.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/main/java/org/apache/activemq/ActiveMQSslConnectionFactory.java?rev=1074577&r1=1074576&r2=1074577&view=diff
==============================================================================
--- activemq/trunk/activemq-core/src/main/java/org/apache/activemq/ActiveMQSslConnectionFactory.java (original)
+++ activemq/trunk/activemq-core/src/main/java/org/apache/activemq/ActiveMQSslConnectionFactory.java Fri Feb 25 15:25:21 2011
@@ -17,15 +17,30 @@
package org.apache.activemq;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyStore;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
import java.security.SecureRandom;
import javax.jms.JMSException;
import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.broker.SslContext;
import org.apache.activemq.transport.Transport;
import org.apache.activemq.transport.tcp.SslTransportFactory;
import org.apache.activemq.util.JMSExceptionSupport;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
/**
* An ActiveMQConnectionFactory that allows access to the key and trust managers
@@ -34,13 +49,38 @@ import org.apache.activemq.util.JMSExcep
* code. In fact, if the URI passed to this class does not have an "ssl" scheme,
* this class will pass all work on to its superclass.
*
+ * There are two alternative approaches you can use to provide X.509 certificates
+ * for the SSL connections:
+ *
+ * Call <code>setTrustStore</code>, <code>setTrustStorePassword</code>, <code>setKeyStore</code>,
+ * and <code>setKeyStorePassword</code>.
+ *
+ * Call <code>setKeyAndTrustManagers</code>.
+ *
* @author sepandm@gmail.com
*/
public class ActiveMQSslConnectionFactory extends ActiveMQConnectionFactory {
+ private static final Log LOG = LogFactory.getLog(ActiveMQSslConnectionFactory.class);
// The key and trust managers used to initialize the used SSLContext.
protected KeyManager[] keyManager;
protected TrustManager[] trustManager;
protected SecureRandom secureRandom;
+ protected String trustStore;
+ protected String trustStorePassword;
+ protected String keyStore;
+ protected String keyStorePassword;
+
+ public ActiveMQSslConnectionFactory() {
+ super();
+ }
+
+ public ActiveMQSslConnectionFactory(String brokerURL) {
+ super(brokerURL);
+ }
+
+ public ActiveMQSslConnectionFactory(URI brokerURL) {
+ super(brokerURL);
+ }
/**
* Sets the key and trust managers used when creating SSL connections.
@@ -70,6 +110,11 @@ public class ActiveMQSslConnectionFactor
}
try {
+ if (keyManager == null || trustManager == null) {
+ trustManager = createTrustManager();
+ keyManager = createKeyManager();
+ // secureRandom can be left as null
+ }
SslTransportFactory sslFactory = new SslTransportFactory();
SslContext ctx = new SslContext(keyManager, trustManager, secureRandom);
SslContext.setCurrentSslContext(ctx);
@@ -78,4 +123,134 @@ public class ActiveMQSslConnectionFactor
throw JMSExceptionSupport.create("Could not create Transport. Reason: " + e, e);
}
}
+
+ protected TrustManager[] createTrustManager() throws Exception {
+ TrustManager[] trustStoreManagers = null;
+ KeyStore trustedCertStore = KeyStore.getInstance("jks");
+
+ InputStream tsStream = getUrlOrResourceAsStream(trustStore);
+
+ trustedCertStore.load(tsStream, trustStorePassword.toCharArray());
+ TrustManagerFactory tmf =
+ TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+
+ tmf.init(trustedCertStore);
+ trustStoreManagers = tmf.getTrustManagers();
+ return trustStoreManagers;
+ }
+
+ protected KeyManager[] createKeyManager() throws Exception {
+ KeyManagerFactory kmf =
+ KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ KeyStore ks = KeyStore.getInstance("jks");
+ KeyManager[] keystoreManagers = null;
+
+ byte[] sslCert = loadClientCredential(keyStore);
+
+
+ if (sslCert != null && sslCert.length > 0) {
+ ByteArrayInputStream bin = new ByteArrayInputStream(sslCert);
+ ks.load(bin, keyStorePassword.toCharArray());
+ kmf.init(ks, keyStorePassword.toCharArray());
+ keystoreManagers = kmf.getKeyManagers();
+ }
+ return keystoreManagers;
+ }
+
+ protected byte[] loadClientCredential(String fileName) throws IOException {
+ if (fileName == null) {
+ return null;
+ }
+ InputStream in = getUrlOrResourceAsStream(fileName);
+ //FileInputStream in = new FileInputStream(fileName);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] buf = new byte[512];
+ int i = in.read(buf);
+ while (i > 0) {
+ out.write(buf, 0, i);
+ i = in.read(buf);
+ }
+ in.close();
+ return out.toByteArray();
+ }
+
+ protected InputStream getUrlOrResourceAsStream(String urlOrResource) throws IOException {
+ InputStream ins = null;
+ try {
+ URL url = new URL(urlOrResource);
+ ins = url.openStream();
+ }
+ catch (MalformedURLException ignore) {
+ ins = null;
+ }
+
+ // Alternatively, treat as classpath resource
+ if (ins == null) {
+ ins = getClass().getClassLoader().getResourceAsStream(urlOrResource);
+ }
+
+ if (ins == null) {
+ throw new java.io.IOException("Could not load resource: " + urlOrResource);
+ }
+
+ return ins;
+ }
+
+ public String getTrustStore() {
+ return trustStore;
+ }
+
+ /**
+ * The location of a keystore file (in <code>jks</code> format) containing one or more
+ * trusted certificates.
+ *
+ * @param trustStore If specified with a scheme, treat as a URL, otherwise treat as a classpath resource.
+ */
+ public void setTrustStore(String trustStore) {
+ this.trustStore = trustStore;
+ trustManager = null;
+ }
+
+ public String getTrustStorePassword() {
+ return trustStorePassword;
+ }
+
+ /**
+ * The password to match the trust store specified by {@link setTrustStore}.
+ *
+ * @param trustStorePassword The password used to unlock the keystore file.
+ */
+ public void setTrustStorePassword(String trustStorePassword) {
+ this.trustStorePassword = trustStorePassword;
+ }
+
+ public String getKeyStore() {
+ return keyStore;
+ }
+
+ /**
+ * The location of a keystore file (in <code>jks</code> format) containing a certificate
+ * and its private key.
+ *
+ * @param keyStore If specified with a scheme, treat as a URL, otherwise treat as a classpath resource.
+ */
+ public void setKeyStore(String keyStore) {
+ this.keyStore = keyStore;
+ keyManager = null;
+ }
+
+ public String getKeyStorePassword() {
+ return keyStorePassword;
+ }
+
+ /**
+ * The password to match the key store specified by {@link setKeyStore}.
+ *
+ * @param keyStorePassword The password, which is used both to unlock the keystore file
+ * and as the pass phrase for the private key stored in the keystore.
+ */
+ public void setKeyStorePassword(String keyStorePassword) {
+ this.keyStorePassword = keyStorePassword;
+ }
+
}
Added: activemq/trunk/activemq-core/src/test/java/org/apache/activemq/ActiveMQSslConnectionFactoryTest.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/test/java/org/apache/activemq/ActiveMQSslConnectionFactoryTest.java?rev=1074577&view=auto
==============================================================================
--- activemq/trunk/activemq-core/src/test/java/org/apache/activemq/ActiveMQSslConnectionFactoryTest.java (added)
+++ activemq/trunk/activemq-core/src/test/java/org/apache/activemq/ActiveMQSslConnectionFactoryTest.java Fri Feb 25 15:25:21 2011
@@ -0,0 +1,220 @@
+/**
+ * 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;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.KeyStore;
+
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.Session;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.apache.activemq.broker.BrokerRegistry;
+import org.apache.activemq.broker.BrokerService;
+import org.apache.activemq.broker.SslBrokerService;
+import org.apache.activemq.broker.TransportConnector;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class ActiveMQSslConnectionFactoryTest extends CombinationTestSupport {
+ private static final Log LOG = LogFactory.getLog(ActiveMQSslConnectionFactoryTest.class);
+
+ public static final String KEYSTORE_TYPE = "jks";
+ public static final String PASSWORD = "password";
+ public static final String SERVER_KEYSTORE = "src/test/resources/server.keystore";
+ public static final String TRUST_KEYSTORE = "src/test/resources/client.keystore";
+
+ private TransportConnector connector;
+ private ActiveMQConnection connection;
+ private BrokerService broker;
+
+ protected void tearDown() throws Exception {
+ // Try our best to close any previously opend connection.
+ try {
+ connection.close();
+ } catch (Throwable ignore) {
+ }
+ // Try our best to stop any previously started broker.
+ try {
+ broker.stop();
+ } catch (Throwable ignore) {
+ }
+ }
+
+ public void testCreateTcpConnectionUsingKnownPort() throws Exception {
+ // Control case: check that the factory can create an ordinary (non-ssl) connection.
+ broker = createBroker("tcp://localhost:61610?wireFormat.tcpNoDelayEnabled=true");
+
+ // This should create the connection.
+ ActiveMQSslConnectionFactory cf = new ActiveMQSslConnectionFactory("tcp://localhost:61610?wireFormat.tcpNoDelayEnabled=true");
+ connection = (ActiveMQConnection)cf.createConnection();
+ assertNotNull(connection);
+
+ brokerStop();
+ }
+
+ public void testCreateSslConnection() throws Exception {
+ // Create SSL/TLS connection with trusted cert from truststore.
+ String sslUri = "ssl://localhost:61611";
+ broker = createSslBroker(sslUri);
+ assertNotNull(broker);
+
+ // This should create the connection.
+ ActiveMQSslConnectionFactory cf = new ActiveMQSslConnectionFactory(sslUri);
+ cf.setTrustStore("server.keystore");
+ cf.setTrustStorePassword("password");
+ connection = (ActiveMQConnection)cf.createConnection();
+ LOG.info("Created client connection");
+ assertNotNull(connection);
+
+ brokerStop();
+ }
+
+ public void testNegativeCreateSslConnectionWithWrongPassword() throws Exception {
+ // Create SSL/TLS connection with trusted cert from truststore.
+ String sslUri = "ssl://localhost:61611";
+ broker = createSslBroker(sslUri);
+ assertNotNull(broker);
+
+ // This should FAIL to connect, due to wrong password.
+ ActiveMQSslConnectionFactory cf = new ActiveMQSslConnectionFactory(sslUri);
+ cf.setTrustStore("server.keystore");
+ cf.setTrustStorePassword("wrongPassword");
+ try {
+ connection = (ActiveMQConnection)cf.createConnection();
+ }
+ catch (javax.jms.JMSException ignore) {
+ // Expected exception
+ LOG.info("Expected java.io.Exception [" + ignore + "]");
+ }
+ assertNull(connection);
+
+ brokerStop();
+ }
+
+ public void testNegativeCreateSslConnectionWithWrongCert() throws Exception {
+ // Create SSL/TLS connection with trusted cert from truststore.
+ String sslUri = "ssl://localhost:61611";
+ broker = createSslBroker(sslUri);
+ assertNotNull(broker);
+
+ // This should FAIL to connect, due to wrong password.
+ ActiveMQSslConnectionFactory cf = new ActiveMQSslConnectionFactory(sslUri);
+ cf.setTrustStore("dummy.keystore");
+ cf.setTrustStorePassword("password");
+ try {
+ connection = (ActiveMQConnection)cf.createConnection();
+ }
+ catch (javax.jms.JMSException ignore) {
+ // Expected exception
+ LOG.info("Expected SSLHandshakeException [" + ignore + "]");
+ }
+ assertNull(connection);
+
+ brokerStop();
+ }
+
+ protected BrokerService createBroker(String uri) throws Exception {
+ // Start up a broker with a tcp connector.
+ BrokerService service = new BrokerService();
+ service.setPersistent(false);
+ connector = service.addConnector(uri);
+ service.start();
+
+ return service;
+ }
+
+ protected BrokerService createSslBroker(String uri) throws Exception {
+
+ // http://java.sun.com/javase/javaseforbusiness/docs/TLSReadme.html
+ // work around: javax.net.ssl.SSLHandshakeException: renegotiation is not allowed
+ //System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
+
+ SslBrokerService service = new SslBrokerService();
+ service.setPersistent(false);
+
+ KeyManager[] km = getKeyManager();
+ TrustManager[] tm = getTrustManager();
+ connector = service.addSslConnector(uri, km, tm, null);
+ service.start();
+
+ return service;
+ }
+
+ protected void brokerStop() throws Exception {
+ broker.stop();
+ }
+
+ public static TrustManager[] getTrustManager() throws Exception {
+ TrustManager[] trustStoreManagers = null;
+ KeyStore trustedCertStore = KeyStore.getInstance(ActiveMQSslConnectionFactoryTest.KEYSTORE_TYPE);
+
+ trustedCertStore.load(new FileInputStream(ActiveMQSslConnectionFactoryTest.TRUST_KEYSTORE), null);
+ TrustManagerFactory tmf =
+ TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+
+ tmf.init(trustedCertStore);
+ trustStoreManagers = tmf.getTrustManagers();
+ return trustStoreManagers;
+ }
+
+ public static KeyManager[] getKeyManager() throws Exception {
+ KeyManagerFactory kmf =
+ KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ KeyStore ks = KeyStore.getInstance(ActiveMQSslConnectionFactoryTest.KEYSTORE_TYPE);
+ KeyManager[] keystoreManagers = null;
+
+ byte[] sslCert = loadClientCredential(ActiveMQSslConnectionFactoryTest.SERVER_KEYSTORE);
+
+
+ if (sslCert != null && sslCert.length > 0) {
+ ByteArrayInputStream bin = new ByteArrayInputStream(sslCert);
+ ks.load(bin, ActiveMQSslConnectionFactoryTest.PASSWORD.toCharArray());
+ kmf.init(ks, ActiveMQSslConnectionFactoryTest.PASSWORD.toCharArray());
+ keystoreManagers = kmf.getKeyManagers();
+ }
+ return keystoreManagers;
+ }
+
+ private static byte[] loadClientCredential(String fileName) throws IOException {
+ if (fileName == null) {
+ return null;
+ }
+ FileInputStream in = new FileInputStream(fileName);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] buf = new byte[512];
+ int i = in.read(buf);
+ while (i > 0) {
+ out.write(buf, 0, i);
+ i = in.read(buf);
+ }
+ in.close();
+ return out.toByteArray();
+ }
+
+}
Propchange: activemq/trunk/activemq-core/src/test/java/org/apache/activemq/ActiveMQSslConnectionFactoryTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: activemq/trunk/activemq-core/src/test/java/org/apache/activemq/ActiveMQSslConnectionFactoryTest.java
------------------------------------------------------------------------------
svn:keywords = Rev Date