ARTEMIS-656 support host verification for SSL cert

This commit is contained in:
jbertram 2016-08-11 14:32:56 -05:00 committed by Clebert Suconic
parent 113c2a3477
commit cfbe06f3bc
14 changed files with 212 additions and 6 deletions

View File

@ -18,6 +18,7 @@ package org.apache.activemq.artemis.core.remoting.impl.netty;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import java.io.IOException; import java.io.IOException;
import java.net.ConnectException; import java.net.ConnectException;
import java.net.Inet6Address; import java.net.Inet6Address;
@ -197,6 +198,8 @@ public class NettyConnector extends AbstractConnector {
private String enabledProtocols; private String enabledProtocols;
private boolean verifyHost;
private boolean tcpNoDelay; private boolean tcpNoDelay;
private int tcpSendBufferSize; private int tcpSendBufferSize;
@ -306,6 +309,8 @@ public class NettyConnector extends AbstractConnector {
enabledCipherSuites = ConfigurationHelper.getStringProperty(TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME, TransportConstants.DEFAULT_ENABLED_CIPHER_SUITES, configuration); enabledCipherSuites = ConfigurationHelper.getStringProperty(TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME, TransportConstants.DEFAULT_ENABLED_CIPHER_SUITES, configuration);
enabledProtocols = ConfigurationHelper.getStringProperty(TransportConstants.ENABLED_PROTOCOLS_PROP_NAME, TransportConstants.DEFAULT_ENABLED_PROTOCOLS, configuration); enabledProtocols = ConfigurationHelper.getStringProperty(TransportConstants.ENABLED_PROTOCOLS_PROP_NAME, TransportConstants.DEFAULT_ENABLED_PROTOCOLS, configuration);
verifyHost = ConfigurationHelper.getBooleanProperty(TransportConstants.VERIFY_HOST_PROP_NAME, TransportConstants.DEFAULT_VERIFY_HOST, configuration);
} }
else { else {
keyStoreProvider = TransportConstants.DEFAULT_KEYSTORE_PROVIDER; keyStoreProvider = TransportConstants.DEFAULT_KEYSTORE_PROVIDER;
@ -316,6 +321,7 @@ public class NettyConnector extends AbstractConnector {
trustStorePassword = TransportConstants.DEFAULT_TRUSTSTORE_PASSWORD; trustStorePassword = TransportConstants.DEFAULT_TRUSTSTORE_PASSWORD;
enabledCipherSuites = TransportConstants.DEFAULT_ENABLED_CIPHER_SUITES; enabledCipherSuites = TransportConstants.DEFAULT_ENABLED_CIPHER_SUITES;
enabledProtocols = TransportConstants.DEFAULT_ENABLED_PROTOCOLS; enabledProtocols = TransportConstants.DEFAULT_ENABLED_PROTOCOLS;
verifyHost = TransportConstants.DEFAULT_VERIFY_HOST;
} }
tcpNoDelay = ConfigurationHelper.getBooleanProperty(TransportConstants.TCP_NODELAY_PROPNAME, TransportConstants.DEFAULT_TCP_NODELAY, configuration); tcpNoDelay = ConfigurationHelper.getBooleanProperty(TransportConstants.TCP_NODELAY_PROPNAME, TransportConstants.DEFAULT_TCP_NODELAY, configuration);
@ -462,7 +468,13 @@ public class NettyConnector extends AbstractConnector {
public void initChannel(Channel channel) throws Exception { public void initChannel(Channel channel) throws Exception {
final ChannelPipeline pipeline = channel.pipeline(); final ChannelPipeline pipeline = channel.pipeline();
if (sslEnabled && !useServlet) { if (sslEnabled && !useServlet) {
SSLEngine engine = context.createSSLEngine(); SSLEngine engine;
if (verifyHost) {
engine = context.createSSLEngine(host, port);
}
else {
engine = context.createSSLEngine();
}
engine.setUseClientMode(true); engine.setUseClientMode(true);
@ -496,6 +508,12 @@ public class NettyConnector extends AbstractConnector {
engine.setEnabledProtocols(originalProtocols); engine.setEnabledProtocols(originalProtocols);
} }
if (verifyHost) {
SSLParameters sslParameters = engine.getSSLParameters();
sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
engine.setSSLParameters(sslParameters);
}
SslHandler handler = new SslHandler(engine); SslHandler handler = new SslHandler(engine);
pipeline.addLast(handler); pipeline.addLast(handler);

View File

@ -87,6 +87,8 @@ public class TransportConstants {
public static final String NEED_CLIENT_AUTH_PROP_NAME = "needClientAuth"; public static final String NEED_CLIENT_AUTH_PROP_NAME = "needClientAuth";
public static final String VERIFY_HOST_PROP_NAME = "verifyHost";
public static final String BACKLOG_PROP_NAME = "backlog"; public static final String BACKLOG_PROP_NAME = "backlog";
public static final String NETTY_VERSION; public static final String NETTY_VERSION;
@ -155,6 +157,8 @@ public class TransportConstants {
public static final boolean DEFAULT_NEED_CLIENT_AUTH = false; public static final boolean DEFAULT_NEED_CLIENT_AUTH = false;
public static final boolean DEFAULT_VERIFY_HOST = false;
public static final boolean DEFAULT_TCP_NODELAY = true; public static final boolean DEFAULT_TCP_NODELAY = true;
public static final int DEFAULT_TCP_SENDBUFFER_SIZE = 32768; public static final int DEFAULT_TCP_SENDBUFFER_SIZE = 32768;
@ -226,6 +230,7 @@ public class TransportConstants {
allowableAcceptorKeys.add(TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME); allowableAcceptorKeys.add(TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME);
allowableAcceptorKeys.add(TransportConstants.ENABLED_PROTOCOLS_PROP_NAME); allowableAcceptorKeys.add(TransportConstants.ENABLED_PROTOCOLS_PROP_NAME);
allowableAcceptorKeys.add(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME); allowableAcceptorKeys.add(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME);
allowableAcceptorKeys.add(TransportConstants.VERIFY_HOST_PROP_NAME);
allowableAcceptorKeys.add(TransportConstants.TCP_NODELAY_PROPNAME); allowableAcceptorKeys.add(TransportConstants.TCP_NODELAY_PROPNAME);
allowableAcceptorKeys.add(TransportConstants.TCP_SENDBUFFER_SIZE_PROPNAME); allowableAcceptorKeys.add(TransportConstants.TCP_SENDBUFFER_SIZE_PROPNAME);
allowableAcceptorKeys.add(TransportConstants.TCP_RECEIVEBUFFER_SIZE_PROPNAME); allowableAcceptorKeys.add(TransportConstants.TCP_RECEIVEBUFFER_SIZE_PROPNAME);
@ -270,6 +275,7 @@ public class TransportConstants {
allowableConnectorKeys.add(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME); allowableConnectorKeys.add(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME);
allowableConnectorKeys.add(TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME); allowableConnectorKeys.add(TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME);
allowableConnectorKeys.add(TransportConstants.ENABLED_PROTOCOLS_PROP_NAME); allowableConnectorKeys.add(TransportConstants.ENABLED_PROTOCOLS_PROP_NAME);
allowableConnectorKeys.add(TransportConstants.VERIFY_HOST_PROP_NAME);
allowableConnectorKeys.add(TransportConstants.TCP_NODELAY_PROPNAME); allowableConnectorKeys.add(TransportConstants.TCP_NODELAY_PROPNAME);
allowableConnectorKeys.add(TransportConstants.TCP_SENDBUFFER_SIZE_PROPNAME); allowableConnectorKeys.add(TransportConstants.TCP_SENDBUFFER_SIZE_PROPNAME);
allowableConnectorKeys.add(TransportConstants.TCP_RECEIVEBUFFER_SIZE_PROPNAME); allowableConnectorKeys.add(TransportConstants.TCP_RECEIVEBUFFER_SIZE_PROPNAME);

View File

@ -19,12 +19,15 @@ package org.apache.activemq.artemis.core.remoting.impl.netty;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLParameters;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -138,6 +141,8 @@ public class NettyAcceptor extends AbstractAcceptor {
private final boolean needClientAuth; private final boolean needClientAuth;
private final boolean verifyHost;
private final boolean tcpNoDelay; private final boolean tcpNoDelay;
private final int backlog; private final int backlog;
@ -224,6 +229,8 @@ public class NettyAcceptor extends AbstractAcceptor {
enabledProtocols = ConfigurationHelper.getStringProperty(TransportConstants.ENABLED_PROTOCOLS_PROP_NAME, TransportConstants.DEFAULT_ENABLED_PROTOCOLS, configuration); enabledProtocols = ConfigurationHelper.getStringProperty(TransportConstants.ENABLED_PROTOCOLS_PROP_NAME, TransportConstants.DEFAULT_ENABLED_PROTOCOLS, configuration);
needClientAuth = ConfigurationHelper.getBooleanProperty(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME, TransportConstants.DEFAULT_NEED_CLIENT_AUTH, configuration); needClientAuth = ConfigurationHelper.getBooleanProperty(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME, TransportConstants.DEFAULT_NEED_CLIENT_AUTH, configuration);
verifyHost = ConfigurationHelper.getBooleanProperty(TransportConstants.VERIFY_HOST_PROP_NAME, TransportConstants.DEFAULT_VERIFY_HOST, configuration);
} }
else { else {
keyStoreProvider = TransportConstants.DEFAULT_KEYSTORE_PROVIDER; keyStoreProvider = TransportConstants.DEFAULT_KEYSTORE_PROVIDER;
@ -235,6 +242,7 @@ public class NettyAcceptor extends AbstractAcceptor {
enabledCipherSuites = TransportConstants.DEFAULT_ENABLED_CIPHER_SUITES; enabledCipherSuites = TransportConstants.DEFAULT_ENABLED_CIPHER_SUITES;
enabledProtocols = TransportConstants.DEFAULT_ENABLED_PROTOCOLS; enabledProtocols = TransportConstants.DEFAULT_ENABLED_PROTOCOLS;
needClientAuth = TransportConstants.DEFAULT_NEED_CLIENT_AUTH; needClientAuth = TransportConstants.DEFAULT_NEED_CLIENT_AUTH;
verifyHost = TransportConstants.DEFAULT_VERIFY_HOST;
} }
tcpNoDelay = ConfigurationHelper.getBooleanProperty(TransportConstants.TCP_NODELAY_PROPNAME, TransportConstants.DEFAULT_TCP_NODELAY, configuration); tcpNoDelay = ConfigurationHelper.getBooleanProperty(TransportConstants.TCP_NODELAY_PROPNAME, TransportConstants.DEFAULT_TCP_NODELAY, configuration);
@ -391,7 +399,13 @@ public class NettyAcceptor extends AbstractAcceptor {
ise.initCause(e); ise.initCause(e);
throw ise; throw ise;
} }
SSLEngine engine = context.createSSLEngine(); SSLEngine engine;
if (verifyHost) {
engine = context.createSSLEngine(host, port);
}
else {
engine = context.createSSLEngine();
}
engine.setUseClientMode(false); engine.setUseClientMode(false);
@ -439,6 +453,13 @@ public class NettyAcceptor extends AbstractAcceptor {
} }
engine.setEnabledProtocols(set.toArray(new String[set.size()])); engine.setEnabledProtocols(set.toArray(new String[set.size()]));
if (verifyHost) {
SSLParameters sslParameters = engine.getSSLParameters();
sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
engine.setSSLParameters(sslParameters);
}
return new SslHandler(engine); return new SslHandler(engine);
} }

View File

@ -395,6 +395,18 @@ following additional properties:
connecting to this acceptor that 2-way SSL is required. Valid values connecting to this acceptor that 2-way SSL is required. Valid values
are `true` or `false`. Default is `false`. are `true` or `false`. Default is `false`.
- `verifyHost`
When used on an `acceptor` the `CN` of the connecting client's SSL certificate
will be compared to its hostname to verify they match. This is useful
only for 2-way SSL.
When used on a `connector` the `CN` of the server's SSL certificate will be
compared to its hostname to verify they match. This is useful for both 1-way
and 2-way SSL.
Valid values are `true` or `false`. Default is `false`.
## Configuring Netty HTTP ## Configuring Netty HTTP
Netty HTTP tunnels packets over the HTTP protocol. It can be useful in Netty HTTP tunnels packets over the HTTP protocol. It can be useful in

View File

@ -55,7 +55,10 @@ public class CoreClientOverOneWaySSLTest extends ActiveMQTestBase {
@Parameterized.Parameters(name = "storeType={0}") @Parameterized.Parameters(name = "storeType={0}")
public static Collection getParameters() { public static Collection getParameters() {
return Arrays.asList(new Object[][]{{"JCEKS"}, {"JKS"}}); return Arrays.asList(new Object[][]{
{"JCEKS"},
{"JKS"}
});
} }
public CoreClientOverOneWaySSLTest(String storeType) { public CoreClientOverOneWaySSLTest(String storeType) {
@ -78,6 +81,10 @@ public class CoreClientOverOneWaySSLTest extends ActiveMQTestBase {
* keytool -export -keystore other-server-side-keystore.jks -file activemq-jks.cer -storepass secureexample * keytool -export -keystore other-server-side-keystore.jks -file activemq-jks.cer -storepass secureexample
* keytool -import -keystore other-client-side-truststore.jks -file activemq-jks.cer -storepass secureexample -keypass secureexample -noprompt * keytool -import -keystore other-client-side-truststore.jks -file activemq-jks.cer -storepass secureexample -keypass secureexample -noprompt
* *
* keytool -genkey -keystore verified-server-side-keystore.jks -storepass secureexample -keypass secureexample -dname "CN=localhost, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" -keyalg RSA
* keytool -export -keystore verified-server-side-keystore.jks -file activemq-jks.cer -storepass secureexample
* keytool -import -keystore verified-client-side-truststore.jks -file activemq-jks.cer -storepass secureexample -keypass secureexample -noprompt
*
* Commands to create the JCEKS artifacts: * Commands to create the JCEKS artifacts:
* keytool -genkey -keystore 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" * keytool -genkey -keystore 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"
* keytool -export -keystore server-side-keystore.jceks -file activemq-jceks.cer -storetype jceks -storepass secureexample * keytool -export -keystore server-side-keystore.jceks -file activemq-jceks.cer -storetype jceks -storepass secureexample
@ -86,6 +93,10 @@ public class CoreClientOverOneWaySSLTest extends ActiveMQTestBase {
* keytool -genkey -keystore other-server-side-keystore.jceks -storetype JCEKS -storepass secureexample -keypass secureexample -dname "CN=Other ActiveMQ Artemis Server, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" * keytool -genkey -keystore other-server-side-keystore.jceks -storetype JCEKS -storepass secureexample -keypass secureexample -dname "CN=Other ActiveMQ Artemis Server, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ"
* keytool -export -keystore other-server-side-keystore.jceks -file activemq-jceks.cer -storetype jceks -storepass secureexample * keytool -export -keystore other-server-side-keystore.jceks -file activemq-jceks.cer -storetype jceks -storepass secureexample
* keytool -import -keystore other-client-side-truststore.jceks -storetype JCEKS -file activemq-jceks.cer -storepass secureexample -keypass secureexample -noprompt * keytool -import -keystore other-client-side-truststore.jceks -storetype JCEKS -file activemq-jceks.cer -storepass secureexample -keypass secureexample -noprompt
*
* keytool -genkey -keystore verified-server-side-keystore.jceks -storetype JCEKS -storepass secureexample -keypass secureexample -dname "CN=localhost, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ"
* keytool -export -keystore verified-server-side-keystore.jceks -file activemq-jceks.cer -storetype jceks -storepass secureexample
* keytool -import -keystore verified-client-side-truststore.jceks -storetype JCEKS -file activemq-jceks.cer -storepass secureexample -keypass secureexample -noprompt
*/ */
private String storeType; private String storeType;
private String SERVER_SIDE_KEYSTORE; private String SERVER_SIDE_KEYSTORE;
@ -123,6 +134,56 @@ public class CoreClientOverOneWaySSLTest extends ActiveMQTestBase {
Assert.assertEquals(text, m.getBodyBuffer().readString()); Assert.assertEquals(text, m.getBodyBuffer().readString());
} }
@Test
public void testOneWaySSLVerifyHost() throws Exception {
createCustomSslServer(null, null, true);
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.TRUSTSTORE_PATH_PROP_NAME, "verified-" + CLIENT_SIDE_TRUSTSTORE);
tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD);
tc.getParams().put(TransportConstants.VERIFY_HOST_PROP_NAME, true);
ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc));
ClientSessionFactory sf = addSessionFactory(createSessionFactory(locator));
ClientSession session = addClientSession(sf.createSession(false, true, true));
session.createQueue(CoreClientOverOneWaySSLTest.QUEUE, CoreClientOverOneWaySSLTest.QUEUE, false);
ClientProducer producer = addClientProducer(session.createProducer(CoreClientOverOneWaySSLTest.QUEUE));
ClientMessage message = createTextMessage(session, text);
producer.send(message);
ClientConsumer consumer = addClientConsumer(session.createConsumer(CoreClientOverOneWaySSLTest.QUEUE));
session.start();
Message m = consumer.receive(1000);
Assert.assertNotNull(m);
Assert.assertEquals(text, m.getBodyBuffer().readString());
}
@Test
public void testOneWaySSLVerifyHostNegative() throws Exception {
createCustomSslServer(null, null, false);
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.TRUSTSTORE_PATH_PROP_NAME, CLIENT_SIDE_TRUSTSTORE);
tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD);
tc.getParams().put(TransportConstants.VERIFY_HOST_PROP_NAME, true);
ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc));
try {
ClientSessionFactory sf = addSessionFactory(createSessionFactory(locator));
fail("Creating a session here should fail due to a certificate with a CN that doesn't match the host name.");
}
catch (Exception e) {
// ignore
}
}
@Test @Test
public void testOneWaySSLReloaded() throws Exception { public void testOneWaySSLReloaded() throws Exception {
createCustomSslServer(); createCustomSslServer();
@ -589,11 +650,22 @@ public class CoreClientOverOneWaySSLTest extends ActiveMQTestBase {
} }
private void createCustomSslServer(String cipherSuites, String protocols) throws Exception { private void createCustomSslServer(String cipherSuites, String protocols) throws Exception {
createCustomSslServer(cipherSuites, protocols, false);
}
private void createCustomSslServer(String cipherSuites, String protocols, boolean useVerifiedKeystore) throws Exception {
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.put(TransportConstants.SSL_ENABLED_PROP_NAME, true); params.put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
params.put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType); params.put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType);
if (useVerifiedKeystore) {
params.put(TransportConstants.KEYSTORE_PATH_PROP_NAME, "verified-" + SERVER_SIDE_KEYSTORE);
}
else {
params.put(TransportConstants.KEYSTORE_PATH_PROP_NAME, SERVER_SIDE_KEYSTORE); params.put(TransportConstants.KEYSTORE_PATH_PROP_NAME, SERVER_SIDE_KEYSTORE);
}
params.put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, PASSWORD); params.put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, PASSWORD);
params.put(TransportConstants.HOST_PROP_NAME, "localhost");
if (cipherSuites != null) { if (cipherSuites != null) {
params.put(TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME, cipherSuites); params.put(TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME, cipherSuites);

View File

@ -36,6 +36,7 @@ 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.ClientSessionFactory;
import org.apache.activemq.artemis.api.core.client.ActiveMQClient; import org.apache.activemq.artemis.api.core.client.ActiveMQClient;
import org.apache.activemq.artemis.api.core.client.ServerLocator; import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptor;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.apache.activemq.artemis.utils.RandomUtil; import org.apache.activemq.artemis.utils.RandomUtil;
import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl;
@ -56,7 +57,9 @@ public class CoreClientOverTwoWaySSLTest extends ActiveMQTestBase {
@Parameterized.Parameters(name = "storeType={0}") @Parameterized.Parameters(name = "storeType={0}")
public static Collection getParameters() { public static Collection getParameters() {
return Arrays.asList(new Object[][]{{"JCEKS"}, {"JKS"}}); return Arrays.asList(new Object[][]{
{"JCEKS"},
{"JKS"}});
} }
public CoreClientOverTwoWaySSLTest(String storeType) { public CoreClientOverTwoWaySSLTest(String storeType) {
@ -77,10 +80,18 @@ public class CoreClientOverTwoWaySSLTest extends ActiveMQTestBase {
* keytool -export -keystore client-side-keystore.jks -file activemq-jks.cer -storepass secureexample * keytool -export -keystore client-side-keystore.jks -file activemq-jks.cer -storepass secureexample
* keytool -import -keystore server-side-truststore.jks -file activemq-jks.cer -storepass secureexample -keypass secureexample -noprompt * keytool -import -keystore server-side-truststore.jks -file activemq-jks.cer -storepass secureexample -keypass secureexample -noprompt
* *
* keytool -genkey -keystore verified-client-side-keystore.jks -storepass secureexample -keypass secureexample -dname "CN=localhost, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" -keyalg RSA
* keytool -export -keystore verified-client-side-keystore.jks -file activemq-jks.cer -storepass secureexample
* keytool -import -keystore verified-server-side-truststore.jks -file activemq-jks.cer -storepass secureexample -keypass secureexample -noprompt
*
* Commands to create the JCEKS artifacts: * Commands to create the JCEKS artifacts:
* keytool -genkey -keystore 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 RSA * keytool -genkey -keystore 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 RSA
* keytool -export -keystore client-side-keystore.jceks -file activemq-jceks.cer -storetype jceks -storepass secureexample * keytool -export -keystore client-side-keystore.jceks -file activemq-jceks.cer -storetype jceks -storepass secureexample
* keytool -import -keystore server-side-truststore.jceks -storetype JCEKS -file activemq-jceks.cer -storepass secureexample -keypass secureexample -noprompt * keytool -import -keystore server-side-truststore.jceks -storetype JCEKS -file activemq-jceks.cer -storepass secureexample -keypass secureexample -noprompt
*
* keytool -genkey -keystore verified-client-side-keystore.jceks -storetype JCEKS -storepass secureexample -keypass secureexample -dname "CN=localhost, OU=Artemis, O=ActiveMQ, L=AMQ, S=AMQ, C=AMQ" -keyalg RSA
* keytool -export -keystore verified-client-side-keystore.jceks -file activemq-jceks.cer -storetype jceks -storepass secureexample
* keytool -import -keystore verified-server-side-truststore.jceks -storetype JCEKS -file activemq-jceks.cer -storepass secureexample -keypass secureexample -noprompt
*/ */
private String storeType; private String storeType;
@ -148,6 +159,72 @@ public class CoreClientOverTwoWaySSLTest extends ActiveMQTestBase {
Assert.assertEquals(text, m.getBodyBuffer().readString()); 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(CoreClientOverTwoWaySSLTest.QUEUE, CoreClientOverTwoWaySSLTest.QUEUE, false);
ClientProducer producer = session.createProducer(CoreClientOverTwoWaySSLTest.QUEUE);
ClientMessage message = createTextMessage(session, text);
producer.send(message);
ClientConsumer consumer = session.createConsumer(CoreClientOverTwoWaySSLTest.QUEUE);
session.start();
Message 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 (Exception e) {
// ignore
}
}
@Test @Test
public void testTwoWaySSLWithoutClientKeyStore() throws Exception { public void testTwoWaySSLWithoutClientKeyStore() throws Exception {
tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true); tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
@ -183,7 +260,7 @@ public class CoreClientOverTwoWaySSLTest extends ActiveMQTestBase {
params.put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType); params.put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType);
params.put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType); params.put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType);
params.put(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME, true); params.put(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME, true);
ConfigurationImpl config = createBasicConfig().addAcceptorConfiguration(new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, params)); ConfigurationImpl config = createBasicConfig().addAcceptorConfiguration(new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, params, "nettySSL"));
server = createServer(false, config); server = createServer(false, config);
server.start(); server.start();
waitForServerToStart(server); waitForServerToStart(server);