From 2d59a0d3dbec0029ccf3eefee80199a7ee1ca86d Mon Sep 17 00:00:00 2001 From: Christian Sailer <christian.sailer@r3.com> Date: Mon, 5 Mar 2018 10:19:04 +0000 Subject: [PATCH] ARTEMIS-1746: Fix client side OpenSSL --- .../remoting/impl/netty/NettyConnector.java | 2 +- .../core/remoting/impl/ssl/SSLSupport.java | 24 +- tests/integration-tests/pom.xml | 7 + ...CoreClientOverTwoWayOpenSSLServerTest.java | 346 +++++++++++++++++ .../ssl/CoreClientOverTwoWayOpenSSLTest.java | 351 ++++++++++++++++++ .../openssl-client-side-keystore.jceks | Bin 0 -> 683 bytes .../openssl-client-side-keystore.jks | Bin 0 -> 706 bytes .../openssl-client-side-truststore.jceks | Bin 0 -> 572 bytes .../openssl-client-side-truststore.jks | Bin 0 -> 572 bytes .../openssl-server-side-keystore.jceks | Bin 0 -> 684 bytes .../openssl-server-side-keystore.jks | Bin 0 -> 707 bytes .../openssl-server-side-truststore.jceks | Bin 0 -> 571 bytes .../openssl-server-side-truststore.jks | Bin 0 -> 571 bytes ...erified-openssl-client-side-keystore.jceks | Bin 0 -> 655 bytes .../verified-openssl-client-side-keystore.jks | Bin 0 -> 679 bytes ...ified-openssl-server-side-truststore.jceks | Bin 0 -> 543 bytes ...erified-openssl-server-side-truststore.jks | Bin 0 -> 544 bytes 17 files changed, 723 insertions(+), 7 deletions(-) create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverTwoWayOpenSSLServerTest.java create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverTwoWayOpenSSLTest.java create mode 100644 tests/unit-tests/src/test/resources/openssl-client-side-keystore.jceks create mode 100644 tests/unit-tests/src/test/resources/openssl-client-side-keystore.jks create mode 100644 tests/unit-tests/src/test/resources/openssl-client-side-truststore.jceks create mode 100644 tests/unit-tests/src/test/resources/openssl-client-side-truststore.jks create mode 100644 tests/unit-tests/src/test/resources/openssl-server-side-keystore.jceks create mode 100644 tests/unit-tests/src/test/resources/openssl-server-side-keystore.jks create mode 100644 tests/unit-tests/src/test/resources/openssl-server-side-truststore.jceks create mode 100644 tests/unit-tests/src/test/resources/openssl-server-side-truststore.jks create mode 100644 tests/unit-tests/src/test/resources/verified-openssl-client-side-keystore.jceks create mode 100644 tests/unit-tests/src/test/resources/verified-openssl-client-side-keystore.jks create mode 100644 tests/unit-tests/src/test/resources/verified-openssl-server-side-truststore.jceks create mode 100644 tests/unit-tests/src/test/resources/verified-openssl-server-side-truststore.jks diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java index 65a63e5337..ebb274a1d8 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java @@ -642,7 +642,7 @@ public class NettyConnector extends AbstractConnector { String realTrustStorePath, String realTrustStorePassword) throws Exception { - SslContext context = SSLSupport.createNettyContext(realKeyStoreProvider, realKeyStorePath, realKeyStorePassword, realTrustStoreProvider, realTrustStorePath, realTrustStorePassword, sslProvider); + SslContext context = SSLSupport.createNettyClientContext(realKeyStoreProvider, realKeyStorePath, realKeyStorePassword, realTrustStoreProvider, realTrustStorePath, realTrustStorePassword, sslProvider, trustAll); Subject subject = null; if (kerb5Config != null) { diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java index 297b294ec6..85c2c50609 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java @@ -29,7 +29,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.security.AccessController; import java.security.KeyStore; -import java.security.PrivateKey; import java.security.PrivilegedAction; import java.security.SecureRandom; import java.security.Security; @@ -39,7 +38,6 @@ import java.security.cert.CertificateFactory; import java.security.cert.CollectionCertStoreParameters; import java.security.cert.PKIXBuilderParameters; import java.security.cert.X509CertSelector; -import java.security.cert.X509Certificate; import java.util.Collection; import io.netty.handler.ssl.SslContext; @@ -112,12 +110,26 @@ public class SSLSupport { final String sslProvider) throws Exception { KeyStore keyStore = SSLSupport.loadKeystore(keystoreProvider, keystorePath, keystorePassword); - String alias = keyStore.aliases().nextElement(); - PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, keystorePassword.toCharArray()); - X509Certificate certificate = (X509Certificate) keyStore.getCertificate(alias); - return SslContextBuilder.forServer(privateKey, certificate).sslProvider(SslProvider.valueOf(sslProvider)).trustManager(SSLSupport.loadTrustManagerFactory(trustStoreProvider, trustStorePath, trustStorePassword, false, null)).build(); + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + keyManagerFactory.init(keyStore, keystorePassword.toCharArray()); + return SslContextBuilder.forServer(keyManagerFactory).sslProvider(SslProvider.valueOf(sslProvider)).trustManager(SSLSupport.loadTrustManagerFactory(trustStoreProvider, trustStorePath, trustStorePassword, false, null)).build(); } + public static SslContext createNettyClientContext(final String keystoreProvider, + final String keystorePath, + final String keystorePassword, + final String trustStoreProvider, + final String trustStorePath, + final String trustStorePassword, + final String sslProvider, + final boolean trustAll ) throws Exception { + KeyStore keyStore = SSLSupport.loadKeystore(keystoreProvider, keystorePath, keystorePassword); + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + keyManagerFactory.init(keyStore, keystorePassword.toCharArray()); + return SslContextBuilder.forClient().sslProvider(SslProvider.valueOf(sslProvider)).keyManager(keyManagerFactory).trustManager(SSLSupport.loadTrustManagerFactory(trustStoreProvider, trustStorePath, trustStorePassword, trustAll, null)).build(); + } + + public static String[] parseCommaSeparatedListIntoArray(String suites) { String[] cipherSuites = suites.split(","); for (int i = 0; i < cipherSuites.length; i++) { diff --git a/tests/integration-tests/pom.xml b/tests/integration-tests/pom.xml index 0558075876..b8ff029756 100644 --- a/tests/integration-tests/pom.xml +++ b/tests/integration-tests/pom.xml @@ -396,6 +396,13 @@ <groupId>org.jgroups</groupId> <artifactId>jgroups</artifactId> </dependency> + + <!-- openSSL test --> + <dependency> + <groupId>io.netty</groupId> + <artifactId>netty-tcnative-boringssl-static</artifactId> + <version>2.0.7.Final</version> + </dependency> </dependencies> <build> diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverTwoWayOpenSSLServerTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverTwoWayOpenSSLServerTest.java new file mode 100644 index 0000000000..dac87fcfd2 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverTwoWayOpenSSLServerTest.java @@ -0,0 +1,346 @@ +/* + * 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.artemis.tests.integration.ssl; + +import io.netty.handler.ssl.SslHandler; +import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException; +import org.apache.activemq.artemis.api.core.Interceptor; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.api.core.TransportConfiguration; +import org.apache.activemq.artemis.api.core.client.ActiveMQClient; +import org.apache.activemq.artemis.api.core.client.ClientConsumer; +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientProducer; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; +import org.apache.activemq.artemis.api.core.client.ServerLocator; +import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; +import org.apache.activemq.artemis.core.protocol.core.Packet; +import org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl; +import org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptor; +import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnection; +import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants; +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; +import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.apache.activemq.artemis.utils.RandomUtil; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import javax.net.ssl.SSLPeerUnverifiedException; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * Test connecting to a server running with OpenSSL TLS from a client that is running with JDK TLS + */ +@RunWith(value = Parameterized.class) +public class CoreClientOverTwoWayOpenSSLServerTest extends ActiveMQTestBase { + + @Parameterized.Parameters(name = "storeType={0}") + public static Collection getParameters() { + return Arrays.asList(new Object[][]{{"JCEKS"}, {"JKS"}}); + } + + public CoreClientOverTwoWayOpenSSLServerTest(String storeType) { + this.storeType = storeType; + SERVER_SIDE_KEYSTORE = "openssl-server-side-keystore." + storeType.toLowerCase(); + SERVER_SIDE_TRUSTSTORE = "openssl-server-side-truststore." + storeType.toLowerCase(); + CLIENT_SIDE_TRUSTSTORE = "openssl-client-side-truststore." + storeType.toLowerCase(); + CLIENT_SIDE_KEYSTORE = "openssl-client-side-keystore." + storeType.toLowerCase(); + } + + public static final SimpleString QUEUE = new SimpleString("QueueOverSSL"); + + /** + * These artifacts are required for testing 2-way SSL with open SSL - note the EC key and ECDSA signature to comply with what OpenSSL offers + * + * Commands to create the JKS artifacts: + * keytool -genkey -keystore openssl-client-side-keystore.jks -storepass secureexample -keypass secureexample -dname "CN=ActiveMQ Artemis Client, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" -keyalg EC -sigalg SHA256withECDSA + * keytool -export -keystore openssl-client-side-keystore.jks -file activemq-jks.cer -storepass secureexample + * keytool -import -keystore openssl-server-side-truststore.jks -file activemq-jks.cer -storepass secureexample -keypass secureexample -noprompt + * + * keytool -genkey -keystore openssl-server-side-keystore.jks -storepass secureexample -keypass secureexample -dname "CN=ActiveMQ Artemis Server, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" -keyalg EC -sigalg SHA256withECDSA + * keytool -export -keystore openssl-server-side-keystore.jks -file activemq-jks.cer -storepass secureexample + * keytool -import -keystore openssl-client-side-truststore.jks -file activemq-jks.cer -storepass secureexample -keypass secureexample -noprompt + * + * keytool -genkey -keystore verified-openssl-client-side-keystore.jks -storepass secureexample -keypass secureexample -dname "CN=localhost, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" -keyalg EC -sigalg SHA256withECDSA + * keytool -export -keystore verified-openssl-client-side-keystore.jks -file activemq-jks.cer -storepass secureexample + * keytool -import -keystore verified-openssl-server-side-truststore.jks -file activemq-jks.cer -storepass secureexample -keypass secureexample -noprompt + * + * Commands to create the JCEKS artifacts: + * keytool -genkey -keystore openssl-client-side-keystore.jceks -storetype JCEKS -storepass secureexample -keypass secureexample -dname "CN=ActiveMQ Artemis Client, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" -keyalg EC -sigalg SHA256withECDSA + * keytool -export -keystore openssl-client-side-keystore.jceks -file activemq-jceks.cer -storetype jceks -storepass secureexample + * keytool -import -keystore openssl-server-side-truststore.jceks -storetype JCEKS -file activemq-jceks.cer -storepass secureexample -keypass secureexample -noprompt + * + * keytool -genkey -keystore openssl-server-side-keystore.jceks -storetype JCEKS -storepass secureexample -keypass secureexample -dname "CN=ActiveMQ Artemis Server, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" -keyalg EC -sigalg SHA256withECDSA + * keytool -export -keystore openssl-server-side-keystore.jceks -file activemq-jceks.cer -storetype jceks -storepass secureexample + * keytool -import -keystore openssl-client-side-truststore.jceks -storetype JCEKS -file activemq-jceks.cer -storepass secureexample -keypass secureexample -noprompt + * + * keytool -genkey -keystore verified-openssl-client-side-keystore.jceks -storetype JCEKS -storepass secureexample -keypass secureexample -dname "CN=localhost, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" -keyalg EC -sigalg SHA256withECDSA + * keytool -export -keystore verified-openssl-client-side-keystore.jceks -file activemq-jceks.cer -storetype jceks -storepass secureexample + * keytool -import -keystore verified-openssl-server-side-truststore.jceks -storetype JCEKS -file activemq-jceks.cer -storepass secureexample -keypass secureexample -noprompt + * + */ + + private String storeType; + private String SERVER_SIDE_KEYSTORE; + private String SERVER_SIDE_TRUSTSTORE; + private String CLIENT_SIDE_TRUSTSTORE; + private String CLIENT_SIDE_KEYSTORE; + private final String PASSWORD = "secureexample"; + + private ActiveMQServer server; + + private TransportConfiguration tc; + + private class MyInterceptor implements Interceptor { + + @Override + public boolean intercept(final Packet packet, final RemotingConnection connection) throws ActiveMQException { + if (packet.getType() == PacketImpl.SESS_SEND) { + try { + if (connection.getTransportConnection() instanceof NettyConnection) { + System.out.println("Passed through...."); + NettyConnection nettyConnection = (NettyConnection) connection.getTransportConnection(); + SslHandler sslHandler = (SslHandler) nettyConnection.getChannel().pipeline().get("ssl"); + Assert.assertNotNull(sslHandler); + Assert.assertNotNull(sslHandler.engine().getSession()); + Assert.assertNotNull(sslHandler.engine().getSession().getPeerCertificateChain()); + } + } catch (SSLPeerUnverifiedException e) { + Assert.fail(e.getMessage()); + } + } + return true; + } + } + + @Test + public void testTwoWaySSL() throws Exception { + String text = RandomUtil.randomString(); + + tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + tc.getParams().put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, CLIENT_SIDE_TRUSTSTORE); + tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD); + tc.getParams().put(TransportConstants.KEYSTORE_PATH_PROP_NAME, CLIENT_SIDE_KEYSTORE); + tc.getParams().put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, PASSWORD); + //tc.getParams().put(TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"); + + server.getRemotingService().addIncomingInterceptor(new MyInterceptor()); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc)); + ClientSessionFactory sf = createSessionFactory(locator); + ClientSession session = sf.createSession(false, true, true); + session.createQueue(CoreClientOverTwoWayOpenSSLServerTest.QUEUE, CoreClientOverTwoWayOpenSSLServerTest.QUEUE, false); + ClientProducer producer = session.createProducer(CoreClientOverTwoWayOpenSSLServerTest.QUEUE); + + ClientMessage message = createTextMessage(session, text); + producer.send(message); + + ClientConsumer consumer = session.createConsumer(CoreClientOverTwoWayOpenSSLServerTest.QUEUE); + session.start(); + + ClientMessage m = consumer.receive(1000); + Assert.assertNotNull(m); + Assert.assertEquals(text, m.getBodyBuffer().readString()); + } + + @Test + public void testTwoWaySSLVerifyClientHost() throws Exception { + NettyAcceptor acceptor = (NettyAcceptor) server.getRemotingService().getAcceptor("nettySSL"); + acceptor.getConfiguration().put(TransportConstants.VERIFY_HOST_PROP_NAME, true); + acceptor.getConfiguration().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, "verified-" + SERVER_SIDE_TRUSTSTORE); + server.getRemotingService().stop(false); + server.getRemotingService().start(); + server.getRemotingService().startAcceptors(); + + String text = RandomUtil.randomString(); + + tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + tc.getParams().put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, CLIENT_SIDE_TRUSTSTORE); + tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD); + tc.getParams().put(TransportConstants.KEYSTORE_PATH_PROP_NAME, "verified-" + CLIENT_SIDE_KEYSTORE); + tc.getParams().put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, PASSWORD); + + server.getRemotingService().addIncomingInterceptor(new MyInterceptor()); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc)); + ClientSessionFactory sf = createSessionFactory(locator); + ClientSession session = sf.createSession(false, true, true); + session.createQueue(CoreClientOverTwoWayOpenSSLServerTest.QUEUE, CoreClientOverTwoWayOpenSSLServerTest.QUEUE, false); + ClientProducer producer = session.createProducer(CoreClientOverTwoWayOpenSSLServerTest.QUEUE); + + ClientMessage message = createTextMessage(session, text); + producer.send(message); + + ClientConsumer consumer = session.createConsumer(CoreClientOverTwoWayOpenSSLServerTest.QUEUE); + session.start(); + + ClientMessage m = consumer.receive(1000); + Assert.assertNotNull(m); + Assert.assertEquals(text, m.getBodyBuffer().readString()); + } + + @Test + public void testTwoWaySSLVerifyClientHostNegative() throws Exception { + NettyAcceptor acceptor = (NettyAcceptor) server.getRemotingService().getAcceptor("nettySSL"); + acceptor.getConfiguration().put(TransportConstants.VERIFY_HOST_PROP_NAME, true); + server.getRemotingService().stop(false); + server.getRemotingService().start(); + server.getRemotingService().startAcceptors(); + + tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + tc.getParams().put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, CLIENT_SIDE_TRUSTSTORE); + tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD); + tc.getParams().put(TransportConstants.KEYSTORE_PATH_PROP_NAME, CLIENT_SIDE_KEYSTORE); + tc.getParams().put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, PASSWORD); + + server.getRemotingService().addIncomingInterceptor(new MyInterceptor()); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc)); + try { + ClientSessionFactory sf = createSessionFactory(locator); + fail("Creating a session here should fail due to a certificate with a CN that doesn't match the host name."); + } catch (ActiveMQNotConnectedException se) { + // ignore + } + } + + @Test + public void testTwoWaySSLVerifyClientTrustAllTrue() throws Exception { + NettyAcceptor acceptor = (NettyAcceptor) server.getRemotingService().getAcceptor("nettySSL"); + acceptor.getConfiguration().put(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME, true); + server.getRemotingService().stop(false); + server.getRemotingService().start(); + server.getRemotingService().startAcceptors(); + + //Set trust all so this should work even with no trust store set + tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + tc.getParams().put(TransportConstants.TRUST_ALL_PROP_NAME, true); + tc.getParams().put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.KEYSTORE_PATH_PROP_NAME, CLIENT_SIDE_KEYSTORE); + tc.getParams().put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, PASSWORD); + + server.getRemotingService().addIncomingInterceptor(new MyInterceptor()); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc)); + ClientSessionFactory sf = createSessionFactory(locator); + sf.close(); + } + + @Test + public void testTwoWaySSLVerifyClientTrustAllTrueByURI() throws Exception { + NettyAcceptor acceptor = (NettyAcceptor) server.getRemotingService().getAcceptor("nettySSL"); + acceptor.getConfiguration().put(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME, true); + server.getRemotingService().stop(false); + server.getRemotingService().start(); + server.getRemotingService().startAcceptors(); + + //Set trust all so this should work even with no trust store set + StringBuilder uri = new StringBuilder("tcp://" + tc.getParams().get(TransportConstants.HOST_PROP_NAME).toString() + + ":" + tc.getParams().get(TransportConstants.PORT_PROP_NAME).toString()); + + uri.append("?").append(TransportConstants.SSL_ENABLED_PROP_NAME).append("=true"); + uri.append("&").append(TransportConstants.TRUST_ALL_PROP_NAME).append("=true"); + uri.append("&").append(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME).append("=").append(storeType); + uri.append("&").append(TransportConstants.KEYSTORE_PATH_PROP_NAME).append("=").append(CLIENT_SIDE_KEYSTORE); + uri.append("&").append(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME).append("=").append(PASSWORD); + + server.getRemotingService().addIncomingInterceptor(new MyInterceptor()); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocator(uri.toString())); + ClientSessionFactory sf = createSessionFactory(locator); + sf.close(); + } + + @Test + public void testTwoWaySSLVerifyClientTrustAllFalse() throws Exception { + NettyAcceptor acceptor = (NettyAcceptor) server.getRemotingService().getAcceptor("nettySSL"); + acceptor.getConfiguration().put(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME, true); + server.getRemotingService().stop(false); + server.getRemotingService().start(); + server.getRemotingService().startAcceptors(); + + //Trust all defaults to false so this should fail with no trust store set + tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + tc.getParams().put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.KEYSTORE_PATH_PROP_NAME, CLIENT_SIDE_KEYSTORE); + tc.getParams().put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, PASSWORD); + + server.getRemotingService().addIncomingInterceptor(new MyInterceptor()); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc)); + try { + ClientSessionFactory sf = createSessionFactory(locator); + fail("Creating a session here should fail due to no trust store being set"); + } catch (ActiveMQNotConnectedException se) { + // ignore + } + } + + @Test + public void testTwoWaySSLWithoutClientKeyStore() throws Exception { + tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + tc.getParams().put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, CLIENT_SIDE_TRUSTSTORE); + tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc)); + try { + createSessionFactory(locator); + Assert.fail(); + } catch (ActiveMQNotConnectedException se) { + //ok + } catch (ActiveMQException e) { + Assert.fail("Invalid Exception type:" + e.getType()); + } + } + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + Map<String, Object> params = new HashMap<>(); + params.put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + params.put(TransportConstants.SSL_PROVIDER, TransportConstants.OPENSSL_PROVIDER); + params.put(TransportConstants.KEYSTORE_PATH_PROP_NAME, SERVER_SIDE_KEYSTORE); + params.put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, PASSWORD); + params.put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, SERVER_SIDE_TRUSTSTORE); + params.put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD); + params.put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType); + params.put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType); + params.put(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME, true); + ConfigurationImpl config = createBasicConfig().addAcceptorConfiguration(new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, params, "nettySSL")); + server = createServer(false, config); + server.start(); + waitForServerToStart(server); + tc = new TransportConfiguration(NETTY_CONNECTOR_FACTORY); + } +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverTwoWayOpenSSLTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverTwoWayOpenSSLTest.java new file mode 100644 index 0000000000..cb8c73dd27 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverTwoWayOpenSSLTest.java @@ -0,0 +1,351 @@ +/* + * 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.artemis.tests.integration.ssl; + +import io.netty.handler.ssl.SslHandler; +import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException; +import org.apache.activemq.artemis.api.core.Interceptor; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.api.core.TransportConfiguration; +import org.apache.activemq.artemis.api.core.client.ActiveMQClient; +import org.apache.activemq.artemis.api.core.client.ClientConsumer; +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientProducer; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; +import org.apache.activemq.artemis.api.core.client.ServerLocator; +import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; +import org.apache.activemq.artemis.core.protocol.core.Packet; +import org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl; +import org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptor; +import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnection; +import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants; +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; +import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.apache.activemq.artemis.utils.RandomUtil; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import javax.net.ssl.SSLPeerUnverifiedException; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * Testing connection where client and server are running OpenSSL TLS + */ +@RunWith(value = Parameterized.class) +public class CoreClientOverTwoWayOpenSSLTest extends ActiveMQTestBase { + + @Parameterized.Parameters(name = "storeType={0}") + public static Collection getParameters() { + return Arrays.asList(new Object[][]{{"JCEKS"}, {"JKS"}}); + } + + public CoreClientOverTwoWayOpenSSLTest(String storeType) { + this.storeType = storeType; + SERVER_SIDE_KEYSTORE = "openssl-server-side-keystore." + storeType.toLowerCase(); + SERVER_SIDE_TRUSTSTORE = "openssl-server-side-truststore." + storeType.toLowerCase(); + CLIENT_SIDE_TRUSTSTORE = "openssl-client-side-truststore." + storeType.toLowerCase(); + CLIENT_SIDE_KEYSTORE = "openssl-client-side-keystore." + storeType.toLowerCase(); + } + + public static final SimpleString QUEUE = new SimpleString("QueueOverSSL"); + + /** + * These artifacts are required for testing 2-way SSL with open SSL - note the EC key and ECDSA signature to comply with what OpenSSL offers + * + * Commands to create the JKS artifacts: + * keytool -genkey -keystore openssl-client-side-keystore.jks -storepass secureexample -keypass secureexample -dname "CN=ActiveMQ Artemis Client, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" -keyalg EC -sigalg SHA256withECDSA + * keytool -export -keystore openssl-client-side-keystore.jks -file activemq-jks.cer -storepass secureexample + * keytool -import -keystore openssl-server-side-truststore.jks -file activemq-jks.cer -storepass secureexample -keypass secureexample -noprompt + * + * keytool -genkey -keystore openssl-server-side-keystore.jks -storepass secureexample -keypass secureexample -dname "CN=ActiveMQ Artemis Server, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" -keyalg EC -sigalg SHA256withECDSA + * keytool -export -keystore openssl-server-side-keystore.jks -file activemq-jks.cer -storepass secureexample + * keytool -import -keystore openssl-client-side-truststore.jks -file activemq-jks.cer -storepass secureexample -keypass secureexample -noprompt + * + * keytool -genkey -keystore verified-openssl-client-side-keystore.jks -storepass secureexample -keypass secureexample -dname "CN=localhost, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" -keyalg EC -sigalg SHA256withECDSA + * keytool -export -keystore verified-openssl-client-side-keystore.jks -file activemq-jks.cer -storepass secureexample + * keytool -import -keystore verified-openssl-server-side-truststore.jks -file activemq-jks.cer -storepass secureexample -keypass secureexample -noprompt + * + * Commands to create the JCEKS artifacts: + * keytool -genkey -keystore openssl-client-side-keystore.jceks -storetype JCEKS -storepass secureexample -keypass secureexample -dname "CN=ActiveMQ Artemis Client, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" -keyalg EC -sigalg SHA256withECDSA + * keytool -export -keystore openssl-client-side-keystore.jceks -file activemq-jceks.cer -storetype jceks -storepass secureexample + * keytool -import -keystore openssl-server-side-truststore.jceks -storetype JCEKS -file activemq-jceks.cer -storepass secureexample -keypass secureexample -noprompt + * + * keytool -genkey -keystore openssl-server-side-keystore.jceks -storetype JCEKS -storepass secureexample -keypass secureexample -dname "CN=ActiveMQ Artemis Server, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" -keyalg EC -sigalg SHA256withECDSA + * keytool -export -keystore openssl-server-side-keystore.jceks -file activemq-jceks.cer -storetype jceks -storepass secureexample + * keytool -import -keystore openssl-client-side-truststore.jceks -storetype JCEKS -file activemq-jceks.cer -storepass secureexample -keypass secureexample -noprompt + * + * keytool -genkey -keystore verified-openssl-client-side-keystore.jceks -storetype JCEKS -storepass secureexample -keypass secureexample -dname "CN=localhost, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" -keyalg EC -sigalg SHA256withECDSA + * keytool -export -keystore verified-openssl-client-side-keystore.jceks -file activemq-jceks.cer -storetype jceks -storepass secureexample + * keytool -import -keystore verified-openssl-server-side-truststore.jceks -storetype JCEKS -file activemq-jceks.cer -storepass secureexample -keypass secureexample -noprompt + * + */ + + private String storeType; + private String SERVER_SIDE_KEYSTORE; + private String SERVER_SIDE_TRUSTSTORE; + private String CLIENT_SIDE_TRUSTSTORE; + private String CLIENT_SIDE_KEYSTORE; + private final String PASSWORD = "secureexample"; + + private ActiveMQServer server; + + private TransportConfiguration tc; + + private class MyInterceptor implements Interceptor { + + @Override + public boolean intercept(final Packet packet, final RemotingConnection connection) throws ActiveMQException { + if (packet.getType() == PacketImpl.SESS_SEND) { + try { + if (connection.getTransportConnection() instanceof NettyConnection) { + System.out.println("Passed through...."); + NettyConnection nettyConnection = (NettyConnection) connection.getTransportConnection(); + SslHandler sslHandler = (SslHandler) nettyConnection.getChannel().pipeline().get("ssl"); + Assert.assertNotNull(sslHandler); + Assert.assertNotNull(sslHandler.engine().getSession()); + Assert.assertNotNull(sslHandler.engine().getSession().getPeerCertificateChain()); + } + } catch (SSLPeerUnverifiedException e) { + Assert.fail(e.getMessage()); + } + } + return true; + } + } + + @Test + public void testTwoWaySSL() throws Exception { + String text = RandomUtil.randomString(); + + tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + tc.getParams().put(TransportConstants.SSL_PROVIDER, TransportConstants.OPENSSL_PROVIDER); + tc.getParams().put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, CLIENT_SIDE_TRUSTSTORE); + tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD); + tc.getParams().put(TransportConstants.KEYSTORE_PATH_PROP_NAME, CLIENT_SIDE_KEYSTORE); + tc.getParams().put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, PASSWORD); + //tc.getParams().put(TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"); + + server.getRemotingService().addIncomingInterceptor(new MyInterceptor()); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc)); + ClientSessionFactory sf = createSessionFactory(locator); + ClientSession session = sf.createSession(false, true, true); + session.createQueue(CoreClientOverTwoWayOpenSSLTest.QUEUE, CoreClientOverTwoWayOpenSSLTest.QUEUE, false); + ClientProducer producer = session.createProducer(CoreClientOverTwoWayOpenSSLTest.QUEUE); + + ClientMessage message = createTextMessage(session, text); + producer.send(message); + + ClientConsumer consumer = session.createConsumer(CoreClientOverTwoWayOpenSSLTest.QUEUE); + session.start(); + + ClientMessage m = consumer.receive(1000); + Assert.assertNotNull(m); + Assert.assertEquals(text, m.getBodyBuffer().readString()); + } + + @Test + public void testTwoWaySSLVerifyClientHost() throws Exception { + NettyAcceptor acceptor = (NettyAcceptor) server.getRemotingService().getAcceptor("nettySSL"); + acceptor.getConfiguration().put(TransportConstants.VERIFY_HOST_PROP_NAME, true); + acceptor.getConfiguration().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, "verified-" + SERVER_SIDE_TRUSTSTORE); + server.getRemotingService().stop(false); + server.getRemotingService().start(); + server.getRemotingService().startAcceptors(); + + String text = RandomUtil.randomString(); + + tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + tc.getParams().put(TransportConstants.SSL_PROVIDER, TransportConstants.OPENSSL_PROVIDER); + tc.getParams().put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, CLIENT_SIDE_TRUSTSTORE); + tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD); + tc.getParams().put(TransportConstants.KEYSTORE_PATH_PROP_NAME, "verified-" + CLIENT_SIDE_KEYSTORE); + tc.getParams().put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, PASSWORD); + + server.getRemotingService().addIncomingInterceptor(new MyInterceptor()); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc)); + ClientSessionFactory sf = createSessionFactory(locator); + ClientSession session = sf.createSession(false, true, true); + session.createQueue(CoreClientOverTwoWayOpenSSLTest.QUEUE, CoreClientOverTwoWayOpenSSLTest.QUEUE, false); + ClientProducer producer = session.createProducer(CoreClientOverTwoWayOpenSSLTest.QUEUE); + + ClientMessage message = createTextMessage(session, text); + producer.send(message); + + ClientConsumer consumer = session.createConsumer(CoreClientOverTwoWayOpenSSLTest.QUEUE); + session.start(); + + ClientMessage m = consumer.receive(1000); + Assert.assertNotNull(m); + Assert.assertEquals(text, m.getBodyBuffer().readString()); + } + + @Test + public void testTwoWaySSLVerifyClientHostNegative() throws Exception { + NettyAcceptor acceptor = (NettyAcceptor) server.getRemotingService().getAcceptor("nettySSL"); + acceptor.getConfiguration().put(TransportConstants.VERIFY_HOST_PROP_NAME, true); + server.getRemotingService().stop(false); + server.getRemotingService().start(); + server.getRemotingService().startAcceptors(); + + tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + tc.getParams().put(TransportConstants.SSL_PROVIDER, TransportConstants.OPENSSL_PROVIDER); + tc.getParams().put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, CLIENT_SIDE_TRUSTSTORE); + tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD); + tc.getParams().put(TransportConstants.KEYSTORE_PATH_PROP_NAME, CLIENT_SIDE_KEYSTORE); + tc.getParams().put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, PASSWORD); + + server.getRemotingService().addIncomingInterceptor(new MyInterceptor()); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc)); + try { + ClientSessionFactory sf = createSessionFactory(locator); + fail("Creating a session here should fail due to a certificate with a CN that doesn't match the host name."); + } catch (ActiveMQNotConnectedException se) { + // ignore + } + } + + @Test + public void testTwoWaySSLVerifyClientTrustAllTrue() throws Exception { + NettyAcceptor acceptor = (NettyAcceptor) server.getRemotingService().getAcceptor("nettySSL"); + acceptor.getConfiguration().put(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME, true); + server.getRemotingService().stop(false); + server.getRemotingService().start(); + server.getRemotingService().startAcceptors(); + + //Set trust all so this should work even with no trust store set + tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + tc.getParams().put(TransportConstants.SSL_PROVIDER, TransportConstants.OPENSSL_PROVIDER); + tc.getParams().put(TransportConstants.TRUST_ALL_PROP_NAME, true); + tc.getParams().put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.KEYSTORE_PATH_PROP_NAME, CLIENT_SIDE_KEYSTORE); + tc.getParams().put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, PASSWORD); + + server.getRemotingService().addIncomingInterceptor(new MyInterceptor()); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc)); + ClientSessionFactory sf = createSessionFactory(locator); + sf.close(); + } + + @Test + public void testTwoWaySSLVerifyClientTrustAllTrueByURI() throws Exception { + NettyAcceptor acceptor = (NettyAcceptor) server.getRemotingService().getAcceptor("nettySSL"); + acceptor.getConfiguration().put(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME, true); + server.getRemotingService().stop(false); + server.getRemotingService().start(); + server.getRemotingService().startAcceptors(); + + //Set trust all so this should work even with no trust store set + StringBuilder uri = new StringBuilder("tcp://" + tc.getParams().get(TransportConstants.HOST_PROP_NAME).toString() + + ":" + tc.getParams().get(TransportConstants.PORT_PROP_NAME).toString()); + + uri.append("?").append(TransportConstants.SSL_ENABLED_PROP_NAME).append("=true"); + uri.append("&").append(TransportConstants.SSL_PROVIDER).append("=").append(TransportConstants.OPENSSL_PROVIDER); + uri.append("&").append(TransportConstants.TRUST_ALL_PROP_NAME).append("=true"); + uri.append("&").append(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME).append("=").append(storeType); + uri.append("&").append(TransportConstants.KEYSTORE_PATH_PROP_NAME).append("=").append(CLIENT_SIDE_KEYSTORE); + uri.append("&").append(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME).append("=").append(PASSWORD); + + server.getRemotingService().addIncomingInterceptor(new MyInterceptor()); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocator(uri.toString())); + ClientSessionFactory sf = createSessionFactory(locator); + sf.close(); + } + + @Test + public void testTwoWaySSLVerifyClientTrustAllFalse() throws Exception { + NettyAcceptor acceptor = (NettyAcceptor) server.getRemotingService().getAcceptor("nettySSL"); + acceptor.getConfiguration().put(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME, true); + server.getRemotingService().stop(false); + server.getRemotingService().start(); + server.getRemotingService().startAcceptors(); + + //Trust all defaults to false so this should fail with no trust store set + tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + tc.getParams().put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.KEYSTORE_PATH_PROP_NAME, CLIENT_SIDE_KEYSTORE); + tc.getParams().put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, PASSWORD); + + server.getRemotingService().addIncomingInterceptor(new MyInterceptor()); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc)); + try { + ClientSessionFactory sf = createSessionFactory(locator); + fail("Creating a session here should fail due to no trust store being set"); + } catch (ActiveMQNotConnectedException se) { + // ignore + } + } + + @Test + public void testTwoWaySSLWithoutClientKeyStore() throws Exception { + tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + tc.getParams().put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType); + tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, CLIENT_SIDE_TRUSTSTORE); + tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc)); + try { + createSessionFactory(locator); + Assert.fail(); + } catch (ActiveMQNotConnectedException se) { + //ok + } catch (ActiveMQException e) { + Assert.fail("Invalid Exception type:" + e.getType()); + } + } + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + Map<String, Object> params = new HashMap<>(); + params.put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + params.put(TransportConstants.SSL_PROVIDER, TransportConstants.OPENSSL_PROVIDER); + params.put(TransportConstants.KEYSTORE_PATH_PROP_NAME, SERVER_SIDE_KEYSTORE); + params.put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, PASSWORD); + params.put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, SERVER_SIDE_TRUSTSTORE); + params.put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD); + params.put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType); + params.put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType); + params.put(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME, true); + ConfigurationImpl config = createBasicConfig().addAcceptorConfiguration(new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, params, "nettySSL")); + server = createServer(false, config); + server.start(); + waitForServerToStart(server); + tc = new TransportConfiguration(NETTY_CONNECTOR_FACTORY); + } +} diff --git a/tests/unit-tests/src/test/resources/openssl-client-side-keystore.jceks b/tests/unit-tests/src/test/resources/openssl-client-side-keystore.jceks new file mode 100644 index 0000000000000000000000000000000000000000..787213710c2ab4f88b0e8ce1a7b6d8b5cbf36588 GIT binary patch literal 683 zcmX?i?%X*B1_mY|W&~r_+{*0KN+2&u`ON$0K%oqSGy^F%PHi?u7Dg>5VMYU97LGSJ zrgmLF|51R6QG~_g%kfI1!y6w@_sM%^{Aaqix9#ma(wi5r{XE6~Ci~VK7sXvqb?|K4 z&i%rB$^q7i?Z+z*Eq&|qe9n(z$Bf@hOT8yKto?PMOn7ni2Z;3%dZq@JK<ob+G%@}J z;<*c$nHZUvSS060L>ln0acH%9oU>(NW@2S9C^Y0T;9_GAWnmL$cJvK|@i<U;>=2%y z0Y6A3moSH8a!F=cDp0wA0Ut<+TbSLks3bKvv)E9<Kn^6rEG!OFqyUvtaL&n0%_}jG z6X!LwFfcYWF|af=H#Um`bIlBmpxl9QQlx<}8#_2um>8kK!_3Ie?8Lwlw8h@`%q^we z!6#16?yB<fd|axw>)P4c=hd8H5Ay!bZ{MlU#Ats<A-DD1v_reiUaC2;oGDqwmysH` zW>;!U{ievpiU#rqvcM3L<zo?J5qaHrzTnL3Wd^!_eyrQ;r{62-{QVs@MwmSq3|yHM z8Ezc=sq=o$r6VCtZmZrtHamP?Q_Ifv?92Y*DfXQIJx)(!QphxHIg>G6exXMGyM*)W s>?^EdwRXqV?x~J^|5=qS@z09g>$|4Psov3RWr+Kx;29&T^6Fzh0F-*!qW}N^ literal 0 HcmV?d00001 diff --git a/tests/unit-tests/src/test/resources/openssl-client-side-keystore.jks b/tests/unit-tests/src/test/resources/openssl-client-side-keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..fd644568e3a9430ea0d3d1d347c64171e8a1f8e2 GIT binary patch literal 706 zcmezO_TO6u1_mY|W&~r_+{*0KN+2&u`Lu2VP^jLZ)_{+VOPh_6g;9%1kdcv<fh9Zj zc7scg?v}=bLgF*cYPf=&&$qi97dLpc^L4H?e|6jL-saz1lfxDX=Py`&=Y!gb$SoVy zXKyjmPhYa7`RB5v**8?4ocguZ$1v?7`(wEoi{`GknivqbgKb%Ab=nT;>8x80Uh*+F z`uW_%-$bnh;;INeQv*w&tNt1^G5!SNxeJ(?7@3$@G>f0!H{fC8&}#EIXUoFO#L8e$ zXvky0#l{@U!Y0h@=o<**aiH+nAv{3?evnEoVGhUSlFYJHpmG7AZ-GMG!t9PkC8@cY z#fAz7av%|AVR4uu1*nvQb53SzUWtL6IIp3Ffw7^9fu*6Ti9r;YYi4K!<qm|CA`OJu z*ukN~#0U)@W=3{qCkB=q6$aJHI_ZX6QYK70{WvR$<HE|;twDA5{@s~>pJZz}82fH3 zG`h0qcWCz1GpChRvNhJ7nmy_EzF(O)`X6wgi%4CpXdrJO3k(riJ{B<+k?@wiTkVg} zbXVBRHto!{d6GI4v~y8ogxQ0^z?DhCd4|}h#jgsF+}$->;QYIDcW$X4INxnIP0s9B z|Bas?{_8U-GTh(hGpTuIhP30J1uT`1PB?GsYSsEOd20M){tIC<-Gru^ZLq9e^RV{V RlK&S!-N<Bomedt{9{|C}<eC5g literal 0 HcmV?d00001 diff --git a/tests/unit-tests/src/test/resources/openssl-client-side-truststore.jceks b/tests/unit-tests/src/test/resources/openssl-client-side-truststore.jceks new file mode 100644 index 0000000000000000000000000000000000000000..9881cebdbbaa4390203c6f85af66b19dd3a7408e GIT binary patch literal 572 zcmX?i?%X*B1_mY|W(3o$xs}<el|WvS@`ZIe46G4)rUsTk?mvSj#$P}@cL6gKBNG!# zW?j=w10FUGtu~Kywk*s{tPBQ)hCBvbY|No7Y{JZrzJV|v2MUiJ!V@&$2dU%|=5S0d z$t+6+Di<)|0|{{pvpW`*q~>N88!8ycfkc>v#bJsRpi&CKsYPX}MFw)>yoMGA#)c*a zmPUp~hEZUynV}JsI}lEaG!SNE2Zst1BQ$uJ8QGbg7+8XY=Ot9M{$$_1@Z{k<jqXzG z6F(<j>z$`)YQghk$&$^p3lmePZT%2YbJ6nnxkVD~g_12S-wWlnn{MrU>i+MjP{?9M z19<~kV2H@_v52vV%sTmG=LFqF3g7x(eY?ddJ7s!LZ3=3PFncl>xG^a*Y^%S%hl_FL z`!q!}fgS4~%I!F~Xv*G_o8o*27lcpZVPpp?d9}!NMo{Lq7M<I-kEP#M&y49`>bo=X kG=tysE&k#&WmC)lnARNkd=h=bd5Puv@JBz=e`V(b09bLT^#A|> literal 0 HcmV?d00001 diff --git a/tests/unit-tests/src/test/resources/openssl-client-side-truststore.jks b/tests/unit-tests/src/test/resources/openssl-client-side-truststore.jks new file mode 100644 index 0000000000000000000000000000000000000000..3ef44dc4bcdc151498040e11e92f8782c24af97d GIT binary patch literal 572 zcmezO_TO6u1_mY|W(3o$xs}<el|WvS@&yZV2G$5YQv*vN_n$!%<1ZkdyMURAk%@`L zI%nf610FUGtu~Kywk*s{tPBQ)hCBvbY|No7Y{JZrzJV|v2MUiJ!V@&$2dU%|=5S0d z$t+6+Di<)|0|{{pvpW`*q~>N88!8ycfkc>v#bJsRpi&CKsYPX}MFw)>yoMGA#)c*a zmPQ7qMp0m{nV}JsI}lEaG!SNE2Zst1BQ$uJ8QGbg7+CCFrffazzbn~<!F-i_PWD>k z;B=wRbDyLHKP3oG$h_k7(mIuO)yHS*Y|MdN_dawTe0@Q6m&?t0Co1#jAC<6f_`O)s zK;A$W7$UNKEMhDo6GYiVYCl{k{qQAemB%9cJ2NI9V?~V-W={qKHzq}fHSE<%z7g_g zJ%kN+3G`ewsw&^OfNxg2+s>OOo!NGISpbzxe`>3pDCn{`kcC@3w%7dcHipQQ+6m$u iXRjpvJ9D_}1J{e=$^nH_CE2zfVt=o>&TNs&GY$Z0x1e7D literal 0 HcmV?d00001 diff --git a/tests/unit-tests/src/test/resources/openssl-server-side-keystore.jceks b/tests/unit-tests/src/test/resources/openssl-server-side-keystore.jceks new file mode 100644 index 0000000000000000000000000000000000000000..d54310159c23255025632a90c7c0e4e5441d3368 GIT binary patch literal 684 zcmX?i?%X*B1_mY|W&~r_+{*0KN+2&u`9fj_P$<J7%|MEcQ=5&Eg;9%1n9+cjg+ueS z#ruOr4eOW~MOZw74o+kUtKJfv_MQ1QS55Pe`DurzWYt<c40<Id5H%sO-=He<gjGyy z<=;mhWuh`$t~mamzQe^c->Q5^VXyj+2gUywpJ{C=7fyp%AE9SzU<tJTpFtDjFCd<~ zfSHMriHRk%uIZ)$4;zP8o5wj@7G@?^27^LF9s@2m=1>+kVP;3)Kp2k$g~txz2^#Q& zRB{P(I3|~5mZbuf3mEW$gt&#-9g9j*b2Ez#6%6D+BFw_#FhvSbDTUzFqO#N?137VC zLkk0ALlXl_BSRy@C@|N|&<M&M2q#4v2(z(+LxqVE8a&L5?95IKEJ4Ea5-M7MvhQAa z@^GF;cd7M>pA)b3&QmnC;Q6s+$>!OGiK)}Jeu${KXnFkHB8m1w$rhIHh4R`>xAr}C z|94a<WU-=wyn!q*L}d9`#8^aToqV!$g6<-PZ+)-6-C~rTGQFoZ1vN&PJsAw#m=qbd z)nDJk#klf)nxdJ&j`a`ab{t$ZWpBw%alV5K!YA=CvICX8T4XvSD05qj&h6XB(r>G0 r#`G`s-I;ir!SDGNfAN{JsfqVaE<DseS54aI?cG(rp|=tzZ*Btsi+$3$ literal 0 HcmV?d00001 diff --git a/tests/unit-tests/src/test/resources/openssl-server-side-keystore.jks b/tests/unit-tests/src/test/resources/openssl-server-side-keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..0a26208761ee137395d3d5996a3630862fc7ee3e GIT binary patch literal 707 zcmezO_TO6u1_mY|W&~r_+{*0KN+2&u`TW~yK%shrS_3{dE^RhO7Dg>5K}JSa2A1sZ zuNK=mwYEB((=jpM^zp$ozio5hCOZWSHM^L8H&B1m&2~avC1$HwoAleM(|kVn8VB31 z+4)#B-1Wlp;M<qq2VHQESuy!nAwz_k{kDetGi2W^SiFU0&D!_X2~14_i~0Eje?{A| zPJF3boXEQY;;INeQv*w&tNs}@G5!MLxeJ(?7@3$@taCQLGT>q3&}#EIXUoFO#L8e$ zXvky0#l{@U!Y0h@=o<**aiH+nAv{3?evnEoVGhUSlFYJHpmG7AZ-GMG!t9PkC8@cY z#fAz7av%|AVR4uu1*nulaB5LmYLS7QIIp3Ffw7^9fu)gwsZkV|Yi4K!<qm|CA`OJu z*ukN~#0U)@W=3{qCk7TfmnmBh`|nCNVK86io|C=SI5=IX^V}yX!A}W-6Ed&(ytGbb zUG?#qIvaB!*S!y22VY-M-Q{v~-igZm`9~$J8-6cVG>|ut1%`+$ABz}^$OKXLklGIy zN<Vx_TII3G{?3fa$5>HggxQn9z>P_fVGVnAl5d3kSr1{uT>?E<jjGBwF5sKh?zZ#h zNoTfQUKT(l)1TUECkndk4P@aKkL@-8yNw|-rFMci$Jr}M|IQrl`Vbobt#9qAiH9c! P7O?G|mDns6_`V1L@TAz8 literal 0 HcmV?d00001 diff --git a/tests/unit-tests/src/test/resources/openssl-server-side-truststore.jceks b/tests/unit-tests/src/test/resources/openssl-server-side-truststore.jceks new file mode 100644 index 0000000000000000000000000000000000000000..9ebd0c75bcb1ad30ce2e6c33c42a0f592e1591fd GIT binary patch literal 571 zcmX?i?%X*B1_mY|W(3o$xs}<el|WvS@>!D_2G$5YQv*vN_pd<{<4+)-yMURAk%@^# za&APV0S_C8R-4B;TNY*}RtAGYLmmSzHs(+kHeqH*-#{3T1BJ&9;Rzb>gH&<}b2uiK zWR|4@l?xc~frPk)*&T~YQgbtl4HXRJKqAb-;xI)DP$>oHoXpg`5(7DLUPB85V?z@I zOG9&GvnVjv%+LtR9SA2y8VIwogF}Uh5gI(qjO@%#3@kxg>}}87QraDS;^geEDj(0s zrE0sbovnRd%^CI}@9+Hfo%&3S_IDI=ThC29wA<{Zngh$3l2v>esc~y|rMA>>id?K{ zAa5WG3=vsA7BLo)*L~*;&b(e`pzG(yy1jn-y^_w~-%(?P*@MBrl}VA|#<8C|@8?`P z64K<h>g{8*!{;@%>|D>j>@S{T&-vfu^fV@gOv9El8Pnw#YUIC5IKR%m!YWp4cU<kB j>d5z>RoN2%tPt0Kc5~&Ix~)ngKMuZ9u3IC>{-X&120*EZ literal 0 HcmV?d00001 diff --git a/tests/unit-tests/src/test/resources/openssl-server-side-truststore.jks b/tests/unit-tests/src/test/resources/openssl-server-side-truststore.jks new file mode 100644 index 0000000000000000000000000000000000000000..89217a3815f082a50a279169f414f7181e9edf64 GIT binary patch literal 571 zcmezO_TO6u1_mY|W(3o$xs}<el|WvS@)^Tu2G$5YQv*vN_pd<{<4+)-yMURAk%@^# zv-s(K10FUGtu~Kywk*s{tPBQ)hCBvbY|No7Y{JZrzJV|v2MUiJ!V@&$2dU%|=5S0d z$t+6+Di<)|0|{{pvpW`*q~>N88!8ycfkc>v#bJsRpi&CXIhm<>B?fZhyoMGA#)c*a zmWHM#22o(HnV}JsI}lEaG!SNE2Zst1BQ$uJ8QGbg7+7vp7*s3kq#JHYnK142<E$i( z3oBc<2G!O3cW3^6lC9-n?7OYd=*phoq1jW<oK{xJ)>wCH_N3eUer4Y1f53e%B6YE% zfxLk%FhpeeSj1RF!dv!kwLd=7U12ZVv@_S{N$O0{&P9z8W)B7fS0)AL8DgInzbZU( zch_uz^Y6~xxut&Ke7D^+IkR8=H-3Kjug|2&aDSW6q~@6!(vEu;uv9)e;k>D<RqM;- ksqv5bFNDo>6PkK&yUg@c_ay~II5qbDKR5T$ER{M_0P9Ap9RL6T literal 0 HcmV?d00001 diff --git a/tests/unit-tests/src/test/resources/verified-openssl-client-side-keystore.jceks b/tests/unit-tests/src/test/resources/verified-openssl-client-side-keystore.jceks new file mode 100644 index 0000000000000000000000000000000000000000..fc8c4cc4d1b2818156d1aba3be9d5e9407472d9c GIT binary patch literal 655 zcmX?i?%X*B1_mY|W&~r_+{*0KN+2&enR(e%piqWEnt>D>r#2fS3!@g3Frxu43&*b; z_k~|{{+DB76k+iY`+RDO_?vUG9InmBLo0Tk{idwE>6SqKn(JDg>2KWoj_=&M>U)X! zFY&aa6QX|>SbulCuB3m&sa|F8O_eig`>bqE`>x9A%L@Dpu|7i2)W8yG{X>H$#=Ah= zxPY07k%@^VTWs1l10FUGtu~Kywk*s{tPBRphCBvbY|No7Y{JZrzJV|v2MUiJ!V@&$ z2dU%|=5S0d$t+6+Di<)|0|{{pvpW`*q~>N88wwc+fJB&uIdk%p6LT{1i%SgT#CZ)Z z3``A84J=H}42`3}Tnj^ED3?Bth%^vpV+Z@3i4htE%#7^JP7ExML)DDqkA;_fyzLj? z!8=pPzKF%K<?$wudG1~<mI7-hhpg^+tM{smxi?Ov=EV6cY2sX8wcCtROxh-2Hd?V- zdCp=*19<~kps!{5Sj1RFjIIS1JlXVUY08X!68bBpZ3<J5HKB$&vj>BLE0Y3~?F$8q zr{~KyK91uFQ9cyrwtT<!&*dcs7n8G%WmYZHXHsOCJiFF?lkBR?hN7|I3*VcR7>i%} m@|`)2D@Dk2<Ng;qY6>6sd=)t_=Tz%<;Z+&St(cUq<5K{n^}~4p literal 0 HcmV?d00001 diff --git a/tests/unit-tests/src/test/resources/verified-openssl-client-side-keystore.jks b/tests/unit-tests/src/test/resources/verified-openssl-client-side-keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..d60a9e7babdd689ebf57bf11f9c45b46a5743685 GIT binary patch literal 679 zcmezO_TO6u1_mY|W&~r_+{*0KN+2&end#mGpisR*tpOh!mo^(C3!@g3AR{9y1538} zX$OZXAA@Ut-D2)EQ!rRsbe1nmIrw*4bU=-#QT12WRLRxza<fm`EIX&@{cRJQ_p+U< z;{@I|%~-UqsO^FO%qOmFDr}#+f3<yeJLY(^^LNbSrw3m5Pj1pap0{Js*P_pF^}{#q zE$4leH+i`%#8nY`rUsTkS3NRlV!Q{$jSHBW7@3$@@|^U~8St=iXtjBqvt?msVr4K$ zHsmqjVq*?vVH0L{^bLgZI8b=(5T2j`KS(8)Fo$DuNoH9pP`Lomw?H9oVRpx&lGNPH zVnZPV0gwo@FlSDFa$-(KesPI`oH(zcg@LJ|sey&5sgY3>m}_BZ4CT_t5s?PMZ0um4 zGciJgfSHk<*@=NAU+Y7_JDbAKhg?}Ex4aDc^XOb-V4mcB#~-^wRb<q(j_=Bx)12VO z{XMNkZr`S6P5F3Hwi-j<7dM<w|95<JIYnZzqJg}DEYR1od@N!tBImzZE>Te4zdDuY zX!MgkKWy}}?Ps8dI<qH(fg6(|!(*}IT0gH@uyH+hUt|5wZko(d&7|d9Rys7W1%$ul zo;44s<eO+%wMWs+i+7f9Q<mym9opE>vvQS8#*KRp7fx<JtmDC#J}YIm|HOvs)=N=K MBd?WL?`c&80GRC5fB*mh literal 0 HcmV?d00001 diff --git a/tests/unit-tests/src/test/resources/verified-openssl-server-side-truststore.jceks b/tests/unit-tests/src/test/resources/verified-openssl-server-side-truststore.jceks new file mode 100644 index 0000000000000000000000000000000000000000..c91e3f2e5aee481a6634ddfe94ea32e9f204d597 GIT binary patch literal 543 zcmX?i?%X*B1_mY|W(3o$xs}<el|Wu{G7G~=2G$5YQv*vN_n|=(<6R(bT)@o4$i&2w zEjI0&0S_C8R-4B;TNY*}RtAG)LmmSzHs(+kHeqH*-#{3T1BJ&9;Rzb>gH&<}b2uiK zWR|4@l?xc~frPk)*&T~YQgbtl4TTH@KqAb-oH_Z)i8&eh#U%!E;=G0y2BwCl1{S7f zhQ?7~u7#m7luI8+L>dUQv4efi#0U)nW=3{qCkB?sp=!qQ$HGfK-u8>{;GHRCU&P|r z@_3WSJa?}aOM$hMLsoaZ)q7RO+#4rSbK?A!G;yx4+HFQDCT){18?9KaJZG_@fxLk% z(ATnjEMhDoM%Mxho@{!wG-bv<3H_DQHifCjnovWX*@MBrl}Ulg_JxAQ)AMB;AII^8 zC?5)QTfX1==kgMRi^<u>GOHHpGbu7mo?Yv{Np{s`L($mqh3`#DjKwc~`Ociil_KQ1 easLY)wMnj$KV44yx7O^R;Q##M*;u>oSv>$W9hLh4 literal 0 HcmV?d00001 diff --git a/tests/unit-tests/src/test/resources/verified-openssl-server-side-truststore.jks b/tests/unit-tests/src/test/resources/verified-openssl-server-side-truststore.jks new file mode 100644 index 0000000000000000000000000000000000000000..22fda4bb410d18816de60add1eaa94f0f582908b GIT binary patch literal 544 zcmezO_TO6u1_mY|W(3o$xs}<el|Wu{GILo118anysevVs`^cb)@g5L2E?{P2WMX2; zbJ9O&z{AF&)#h=|mW7##mBAp{kjH?FjX9KsO_<ryHxS0-K;f}Nc!CD}AeCIg9FEB) znPsU!<pKtLAR%sHcE_TU)ZENsLm>kJkO;FdXHI@{VopYWafyMPIIp3FfvKUXfrY85 zkx>+wYhh>%<<iFykp{wS>|mcWF+ziYnUS5@iGd|w>qEdho5IhBTv;Z!ybSvD=v-r9 zp5%PTAG<<TWYn~d@5-FhoZ!a&J*`D<-==0w`FK&b8bjX~H=IxZcYJg?MPjj{fxLk% z(ATnjEMhDo=f7DlQBdB$I+f>W^pib5Z1l42XP|~UvnPXr8<Qf#W3l5}Kd)J^aXofl zWBtx<n#@tnq~%*yIyA5agumsUH4mudn`l_IN72lScb0Ecmg-v_+St#ta+OTRje8Ck fPHsP}<I!>5`OZbh*H8K`<=ZU(oLa5)LPZt;QM8*M literal 0 HcmV?d00001