diff options
Diffstat (limited to 'qpid/java/systests/src/test/java/org/apache/qpid/client/ssl/SSLTest.java')
-rw-r--r-- | qpid/java/systests/src/test/java/org/apache/qpid/client/ssl/SSLTest.java | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/client/ssl/SSLTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/client/ssl/SSLTest.java new file mode 100644 index 0000000000..eb61e5a084 --- /dev/null +++ b/qpid/java/systests/src/test/java/org/apache/qpid/client/ssl/SSLTest.java @@ -0,0 +1,485 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.ssl; + +import static org.apache.qpid.test.utils.TestSSLConstants.KEYSTORE; +import static org.apache.qpid.test.utils.TestSSLConstants.KEYSTORE_PASSWORD; +import static org.apache.qpid.test.utils.TestSSLConstants.TRUSTSTORE; +import static org.apache.qpid.test.utils.TestSSLConstants.TRUSTSTORE_PASSWORD; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Session; + +import org.apache.qpid.client.AMQConnectionURL; +import org.apache.qpid.client.AMQTestConnection_0_10; +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Transport; +import org.apache.qpid.test.utils.QpidBrokerTestCase; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SSLTest extends QpidBrokerTestCase +{ + private static final Logger LOGGER = LoggerFactory.getLogger(SSLTest.class); + + private static final String CERT_ALIAS_APP1 = "app1"; + private static final String CERT_ALIAS_APP2 = "app2"; + + @Override + protected void setUp() throws Exception + { + setSystemProperty("javax.net.debug", "ssl"); + + setSslStoreSystemProperties(); + + //We dont call super.setUp, the tests start the broker after deciding + //whether to run and then configuring it appropriately + } + + public void testCreateSSLConnectionUsingConnectionURLParams() throws Exception + { + if (shouldPerformTest()) + { + clearSslStoreSystemProperties(); + + //Start the broker (NEEDing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, true, false, false); + super.setUp(); + + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:%s" + + "?ssl='true'" + + "&key_store='%s'&key_store_password='%s'" + + "&trust_store='%s'&trust_store_password='%s'" + + "'"; + + url = String.format(url,QpidBrokerTestCase.DEFAULT_SSL_PORT, + KEYSTORE,KEYSTORE_PASSWORD,TRUSTSTORE,TRUSTSTORE_PASSWORD); + + Connection con = getConnection(new AMQConnectionURL(url)); + assertNotNull("connection should be successful", con); + Session ssn = con.createSession(false,Session.AUTO_ACKNOWLEDGE); + assertNotNull("create session should be successful", ssn); + } + } + + public void testHostVerificationIsOnByDefault() throws Exception + { + if (shouldPerformTest()) + { + clearSslStoreSystemProperties(); + + //Start the broker (NEEDing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, true, false, false); + super.setUp(); + + String url = "amqp://guest:guest@test/?brokerlist='tcp://127.0.0.1:%s" + + "?ssl='true'" + + "&key_store='%s'&key_store_password='%s'" + + "&trust_store='%s'&trust_store_password='%s'" + + "'"; + + url = String.format(url,QpidBrokerTestCase.DEFAULT_SSL_PORT, + KEYSTORE,KEYSTORE_PASSWORD,TRUSTSTORE,TRUSTSTORE_PASSWORD); + + try + { + getConnection(new AMQConnectionURL(url)); + } + catch(JMSException e) + { + assertTrue("Unexpected exception message", e.getMessage().contains("SSL hostname verification failed")); + } + + url = "amqp://guest:guest@test/?brokerlist='tcp://127.0.0.1:%s" + + "?ssl='true'&ssl_verify_hostname='false'" + + "&key_store='%s'&key_store_password='%s'" + + "&trust_store='%s'&trust_store_password='%s'" + + "'"; + url = String.format(url,QpidBrokerTestCase.DEFAULT_SSL_PORT, + KEYSTORE,KEYSTORE_PASSWORD,TRUSTSTORE,TRUSTSTORE_PASSWORD); + + Connection con = getConnection(new AMQConnectionURL(url)); + assertNotNull("connection should be successful", con); + Session ssn = con.createSession(false,Session.AUTO_ACKNOWLEDGE); + assertNotNull("create session should be successful", ssn); + } + } + + /** + * Create an SSL connection using the SSL system properties for the trust and key store, but using + * the {@link ConnectionURL} ssl='true' option to indicate use of SSL at a Connection level, + * without specifying anything at the {@link ConnectionURL#OPTIONS_BROKERLIST} level. + */ + public void testSslConnectionOption() throws Exception + { + if (shouldPerformTest()) + { + //Start the broker (NEEDing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, true, false, false); + super.setUp(); + + //Create URL enabling SSL at the connection rather than brokerlist level + String url = "amqp://guest:guest@test/?ssl='true'&brokerlist='tcp://localhost:%s'"; + url = String.format(url,QpidBrokerTestCase.DEFAULT_SSL_PORT); + + Connection con = getConnection(new AMQConnectionURL(url)); + assertNotNull("connection should be successful", con); + Session ssn = con.createSession(false,Session.AUTO_ACKNOWLEDGE); + assertNotNull("create session should be successful", ssn); + } + } + + /** + * Create an SSL connection using the SSL system properties for the trust and key store, but using + * the {@link ConnectionURL} ssl='true' option to indicate use of SSL at a Connection level, + * overriding the false setting at the {@link ConnectionURL#OPTIONS_BROKERLIST} level. + */ + public void testSslConnectionOptionOverridesBrokerlistOption() throws Exception + { + if (shouldPerformTest()) + { + //Start the broker (NEEDing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, true, false, false); + super.setUp(); + + //Create URL enabling SSL at the connection, overriding the false at the brokerlist level + String url = "amqp://guest:guest@test/?ssl='true'&brokerlist='tcp://localhost:%s?ssl='false''"; + url = String.format(url,QpidBrokerTestCase.DEFAULT_SSL_PORT); + + Connection con = getConnection(new AMQConnectionURL(url)); + assertNotNull("connection should be successful", con); + Session ssn = con.createSession(false,Session.AUTO_ACKNOWLEDGE); + assertNotNull("create session should be successful", ssn); + } + } + + public void testCreateSSLConnectionUsingSystemProperties() throws Exception + { + if (shouldPerformTest()) + { + //Start the broker (NEEDing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, true, false, false); + super.setUp(); + + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:%s?ssl='true''"; + + url = String.format(url,QpidBrokerTestCase.DEFAULT_SSL_PORT); + + Connection con = getConnection(new AMQConnectionURL(url)); + assertNotNull("connection should be successful", con); + Session ssn = con.createSession(false,Session.AUTO_ACKNOWLEDGE); + assertNotNull("create session should be successful", ssn); + } + } + + public void testMultipleCertsInSingleStore() throws Exception + { + if (shouldPerformTest()) + { + //Start the broker (NEEDing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, true, false, false); + super.setUp(); + + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:" + + QpidBrokerTestCase.DEFAULT_SSL_PORT + + "?ssl='true'&ssl_cert_alias='" + CERT_ALIAS_APP1 + "''"; + + AMQTestConnection_0_10 con = new AMQTestConnection_0_10(url); + org.apache.qpid.transport.Connection transportCon = con.getConnection(); + String userID = transportCon.getSecurityLayer().getUserID(); + assertEquals("The correct certificate was not choosen","app1@acme.org",userID); + con.close(); + + url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:" + + QpidBrokerTestCase.DEFAULT_SSL_PORT + + "?ssl='true'&ssl_cert_alias='" + CERT_ALIAS_APP2 + "''"; + + con = new AMQTestConnection_0_10(url); + transportCon = con.getConnection(); + userID = transportCon.getSecurityLayer().getUserID(); + assertEquals("The correct certificate was not choosen","app2@acme.org",userID); + con.close(); + } + } + + public void testVerifyHostNameWithIncorrectHostname() throws Exception + { + if (shouldPerformTest()) + { + //Start the broker (WANTing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, false, true, false); + super.setUp(); + + String url = "amqp://guest:guest@test/?brokerlist='tcp://127.0.0.1:" + + QpidBrokerTestCase.DEFAULT_SSL_PORT + + "?ssl='true''"; + + try + { + getConnection(new AMQConnectionURL(url)); + fail("Hostname verification failed. No exception was thrown"); + } + catch (Exception e) + { + verifyExceptionCausesContains(e, "SSL hostname verification failed"); + } + } + } + + private void verifyExceptionCausesContains(Exception e, String expectedString) + { + LOGGER.debug("verifying that the following exception contains " + expectedString, e); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(bout)); + String strace = bout.toString(); + assertTrue("Correct exception not thrown", strace.contains(expectedString)); + } + + public void testVerifyLocalHost() throws Exception + { + if (shouldPerformTest()) + { + //Start the broker (WANTing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, false, true, false); + super.setUp(); + + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:" + + QpidBrokerTestCase.DEFAULT_SSL_PORT + + "?ssl='true''"; + + Connection con = getConnection(new AMQConnectionURL(url)); + assertNotNull("connection should have been created", con); + } + } + + public void testVerifyLocalHostLocalDomain() throws Exception + { + if (shouldPerformTest()) + { + //Start the broker (WANTing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, false, true, false); + super.setUp(); + + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost.localdomain:" + + QpidBrokerTestCase.DEFAULT_SSL_PORT + + "?ssl='true''"; + + Connection con = getConnection(new AMQConnectionURL(url)); + assertNotNull("connection should have been created", con); + } + } + + public void testCreateSSLConnectionUsingConnectionURLParamsTrustStoreOnly() throws Exception + { + if (shouldPerformTest()) + { + clearSslStoreSystemProperties(); + + //Start the broker (WANTing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, false, true, false); + super.setUp(); + + + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:%s" + + "?ssl='true'" + + "&trust_store='%s'&trust_store_password='%s'" + + "'"; + + url = String.format(url,QpidBrokerTestCase.DEFAULT_SSL_PORT, TRUSTSTORE,TRUSTSTORE_PASSWORD); + + Connection con = getConnection(new AMQConnectionURL(url)); + assertNotNull("connection should be successful", con); + Session ssn = con.createSession(false,Session.AUTO_ACKNOWLEDGE); + assertNotNull("create session should be successful", ssn); + } + } + + /** + * Verifies that when the broker is configured to NEED client certificates, + * a client which doesn't supply one fails to connect. + */ + public void testClientCertMissingWhilstNeeding() throws Exception + { + missingClientCertWhileNeedingOrWantingTestImpl(true, false, false); + } + + /** + * Verifies that when the broker is configured to WANT client certificates, + * a client which doesn't supply one succeeds in connecting. + */ + public void testClientCertMissingWhilstWanting() throws Exception + { + missingClientCertWhileNeedingOrWantingTestImpl(false, true, true); + } + + /** + * Verifies that when the broker is configured to WANT and NEED client certificates + * that a client which doesn't supply one fails to connect. + */ + public void testClientCertMissingWhilstWantingAndNeeding() throws Exception + { + missingClientCertWhileNeedingOrWantingTestImpl(true, true, false); + } + + private void missingClientCertWhileNeedingOrWantingTestImpl(boolean needClientCerts, + boolean wantClientCerts, boolean shouldSucceed) throws Exception + { + if (shouldPerformTest()) + { + clearSslStoreSystemProperties(); + + //Start the broker + configureJavaBrokerIfNecessary(true, true, needClientCerts, wantClientCerts, false); + super.setUp(); + + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:%s" + + "?ssl='true'&trust_store='%s'&trust_store_password='%s''"; + + url = String.format(url,QpidBrokerTestCase.DEFAULT_SSL_PORT,TRUSTSTORE,TRUSTSTORE_PASSWORD); + try + { + Connection con = getConnection(new AMQConnectionURL(url)); + if(!shouldSucceed) + { + fail("Connection succeeded, expected exception was not thrown"); + } + else + { + //Use the connection to verify it works + con.createSession(true, Session.SESSION_TRANSACTED); + } + } + catch(JMSException e) + { + if(shouldSucceed) + { + _logger.error("Caught unexpected exception",e); + fail("Connection failed, unexpected exception thrown"); + } + else + { + //expected + verifyExceptionCausesContains(e, "Caused by: javax.net.ssl.SSLException:"); + } + } + } + } + + /** + * Test running TLS and unencrypted on the same port works and both TLS and non-TLS connections can be established + * + */ + public void testCreateSSLandTCPonSamePort() throws Exception + { + if (shouldPerformTest()) + { + clearSslStoreSystemProperties(); + + //Start the broker (NEEDing client certificate authentication) + configureJavaBrokerIfNecessary(true, false, false, false, true); + super.setUp(); + + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:%s" + + "?ssl='true'&ssl_verify_hostname='true'" + + "&key_store='%s'&key_store_password='%s'" + + "&trust_store='%s'&trust_store_password='%s'" + + "'"; + + url = String.format(url,QpidBrokerTestCase.DEFAULT_SSL_PORT, + KEYSTORE,KEYSTORE_PASSWORD,TRUSTSTORE,TRUSTSTORE_PASSWORD); + + Connection con = getConnection(new AMQConnectionURL(url)); + assertNotNull("connection should be successful", con); + Session ssn = con.createSession(false,Session.AUTO_ACKNOWLEDGE); + assertNotNull("create session should be successful", ssn); + + url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:%s'"; + + url = String.format(url,QpidBrokerTestCase.DEFAULT_SSL_PORT); + + con = getConnection(new AMQConnectionURL(url)); + assertNotNull("connection should be successful", con); + ssn = con.createSession(false,Session.AUTO_ACKNOWLEDGE); + assertNotNull("create session should be successful", ssn); + + } + } + + + private boolean shouldPerformTest() + { + // We run the SSL tests on all the Java broker profiles + if(isJavaBroker()) + { + setTestClientSystemProperty(PROFILE_USE_SSL, "true"); + } + + return Boolean.getBoolean(PROFILE_USE_SSL); + } + + private void configureJavaBrokerIfNecessary(boolean sslEnabled, + boolean sslOnly, + boolean needClientAuth, + boolean wantClientAuth, + boolean samePort) throws Exception + { + if(isJavaBroker()) + { + Map<String, Object> sslPortAttributes = new HashMap<String, Object>(); + sslPortAttributes.put(Port.TRANSPORTS, samePort ? Arrays.asList(Transport.SSL, Transport.TCP) + : Collections.singleton(Transport.SSL)); + sslPortAttributes.put(Port.PORT, DEFAULT_SSL_PORT); + sslPortAttributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + sslPortAttributes.put(Port.NEED_CLIENT_AUTH, needClientAuth); + sslPortAttributes.put(Port.WANT_CLIENT_AUTH, wantClientAuth); + sslPortAttributes.put(Port.NAME, TestBrokerConfiguration.ENTRY_NAME_SSL_PORT); + sslPortAttributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); + sslPortAttributes.put(Port.TRUST_STORES, Collections.singleton(TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE)); + getBrokerConfiguration().addObjectConfiguration(Port.class,sslPortAttributes); + } + } + + private void setSslStoreSystemProperties() + { + setSystemProperty("javax.net.ssl.keyStore", KEYSTORE); + setSystemProperty("javax.net.ssl.keyStorePassword", KEYSTORE_PASSWORD); + setSystemProperty("javax.net.ssl.trustStore", TRUSTSTORE); + setSystemProperty("javax.net.ssl.trustStorePassword", TRUSTSTORE_PASSWORD); + } + + private void clearSslStoreSystemProperties() + { + setSystemProperty("javax.net.ssl.keyStore", null); + setSystemProperty("javax.net.ssl.keyStorePassword", null); + setSystemProperty("javax.net.ssl.trustStore", null); + setSystemProperty("javax.net.ssl.trustStorePassword", null); + } +} |