Repository: qpid-broker-j Updated Branches: refs/heads/master b720d7d1e -> 20f47fec8
QPID-8083: [System Tests] [REST/HTTP] Add TlsClientAuthenticationTest Project: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/commit/20f47fec Tree: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/tree/20f47fec Diff: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/diff/20f47fec Branch: refs/heads/master Commit: 20f47fec86440686e7d345c6718618abfc8a09cf Parents: b720d7d Author: Keith Wall <kw...@apache.org> Authored: Fri Feb 23 17:15:51 2018 +0000 Committer: Keith Wall <kw...@apache.org> Committed: Fri Feb 23 17:18:36 2018 +0000 ---------------------------------------------------------------------- .../ManagedPeerCertificateTrustStore.java | 1 + .../apache/qpid/tests/http/HttpTestHelper.java | 87 +++++--- .../TlsClientAuthenticationTest.java | 210 +++++++++++++++++++ .../rest/BrokerRestHttpsClientCertAuthTest.java | 87 -------- 4 files changed, 272 insertions(+), 113 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/20f47fec/broker-core/src/main/java/org/apache/qpid/server/security/ManagedPeerCertificateTrustStore.java ---------------------------------------------------------------------- diff --git a/broker-core/src/main/java/org/apache/qpid/server/security/ManagedPeerCertificateTrustStore.java b/broker-core/src/main/java/org/apache/qpid/server/security/ManagedPeerCertificateTrustStore.java index 580696a..1010611 100644 --- a/broker-core/src/main/java/org/apache/qpid/server/security/ManagedPeerCertificateTrustStore.java +++ b/broker-core/src/main/java/org/apache/qpid/server/security/ManagedPeerCertificateTrustStore.java @@ -37,6 +37,7 @@ public interface ManagedPeerCertificateTrustStore<X extends ManagedPeerCertifica { String TYPE_NAME = "ManagedCertificateStore"; + String STORED_CERTIFICATES = "storedCertificates"; @Override http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/20f47fec/systests/qpid-systests-http-management/src/main/java/org/apache/qpid/tests/http/HttpTestHelper.java ---------------------------------------------------------------------- diff --git a/systests/qpid-systests-http-management/src/main/java/org/apache/qpid/tests/http/HttpTestHelper.java b/systests/qpid-systests-http-management/src/main/java/org/apache/qpid/tests/http/HttpTestHelper.java index 6561504..a2bb3b8 100644 --- a/systests/qpid-systests-http-management/src/main/java/org/apache/qpid/tests/http/HttpTestHelper.java +++ b/systests/qpid-systests-http-management/src/main/java/org/apache/qpid/tests/http/HttpTestHelper.java @@ -33,17 +33,20 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import javax.xml.bind.DatatypeConverter; @@ -54,6 +57,7 @@ import org.junit.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.qpid.server.transport.network.security.ssl.SSLUtil; import org.apache.qpid.tests.utils.BrokerAdmin; public class HttpTestHelper @@ -74,12 +78,14 @@ public class HttpTestHelper private final String _username; private final String _password; private final String _requestHostName; - private final int _connectTimeout = Integer.getInteger("qpid.resttest_connection_timeout", 30000); private String _acceptEncoding; private boolean _tls = false; + private KeyStore _keyStore; + private String _keyStorePassword; + public HttpTestHelper(final BrokerAdmin admin) { this(admin, null); @@ -138,32 +144,21 @@ public class HttpTestHelper try { SSLContext sslContext = SSLContext.getInstance("TLS"); - TrustManager[] trustAllCerts = new TrustManager[] { - new X509TrustManager() - { - public X509Certificate[] getAcceptedIssuers() - { - X509Certificate[] issuers = new X509Certificate[0]; - return issuers; - } - - @Override - public void checkClientTrusted(X509Certificate[] certs, String authType) - { - } - - @Override - public void checkServerTrusted(X509Certificate[] certs, String authType) - { - } - } - }; - - sslContext.init(null, trustAllCerts, null); + TrustManager[] trustAllCerts = new TrustManager[] {new TrustAllTrustManager()}; + + KeyManager[] keyManagers = null; + if (_keyStore != null) + { + char[] keyStoreCharPassword = _keyStorePassword == null ? null : _keyStorePassword.toCharArray(); + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(_keyStore, keyStoreCharPassword); + keyManagers = kmf.getKeyManagers(); + } + sslContext.init(keyManagers, trustAllCerts, null); httpsCon.setSSLSocketFactory(sslContext.getSocketFactory()); httpsCon.setHostnameVerifier((s, sslSession) -> true); } - catch (KeyManagementException | NoSuchAlgorithmException e) + catch (KeyStoreException | UnrecoverableKeyException | KeyManagementException | NoSuchAlgorithmException e) { throw new RuntimeException(e); } @@ -439,4 +434,44 @@ public class HttpTestHelper _acceptEncoding = acceptEncoding; } + public void setKeyStore(final String keystore, final String password) throws Exception + { + _keyStorePassword = password; + + if (keystore != null) + { + try + { + URL ks = new URL(keystore); + _keyStore = SSLUtil.getInitializedKeyStore(ks, password, KeyStore.getDefaultType()); + } + catch (MalformedURLException e) + { + _keyStore = SSLUtil.getInitializedKeyStore(keystore, password, KeyStore.getDefaultType()); + } + } + else + { + _keyStore = null; + } + } + + private static class TrustAllTrustManager implements X509TrustManager + { + public X509Certificate[] getAcceptedIssuers() + { + X509Certificate[] issuers = new X509Certificate[0]; + return issuers; + } + + @Override + public void checkClientTrusted(X509Certificate[] certs, String authType) + { + } + + @Override + public void checkServerTrusted(X509Certificate[] certs, String authType) + { + } + } } http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/20f47fec/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/authentication/TlsClientAuthenticationTest.java ---------------------------------------------------------------------- diff --git a/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/authentication/TlsClientAuthenticationTest.java b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/authentication/TlsClientAuthenticationTest.java new file mode 100644 index 0000000..7d14ff8 --- /dev/null +++ b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/authentication/TlsClientAuthenticationTest.java @@ -0,0 +1,210 @@ +/* + * + * 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.tests.http.authentication; + +import static javax.servlet.http.HttpServletResponse.SC_CREATED; +import static javax.servlet.http.HttpServletResponse.SC_OK; +import static org.apache.qpid.server.transport.network.security.ssl.SSLUtil.generateSelfSignedCertificate; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import java.io.ByteArrayOutputStream; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayDeque; +import java.util.Base64; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.Map; + +import javax.net.ssl.SSLHandshakeException; + +import com.fasterxml.jackson.core.type.TypeReference; +import org.junit.After; +import org.junit.Test; + +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Protocol; +import org.apache.qpid.server.model.Transport; +import org.apache.qpid.server.security.FileKeyStore; +import org.apache.qpid.server.security.ManagedPeerCertificateTrustStore; +import org.apache.qpid.server.security.auth.manager.ExternalAuthenticationManager; +import org.apache.qpid.server.transport.network.security.ssl.SSLUtil.KeyCertPair; +import org.apache.qpid.server.util.BaseAction; +import org.apache.qpid.server.util.DataUrlUtils; +import org.apache.qpid.tests.http.HttpTestBase; +import org.apache.qpid.tests.http.HttpTestHelper; + +public class TlsClientAuthenticationTest extends HttpTestBase +{ + + private Deque<BaseAction<Void, Exception>> _tearDownActions; + private int _clientAuthPort; + private String _keyStore; + + @After + public void tearDown() throws Exception + { + if (_tearDownActions != null) + { + Exception exception = null; + while(!_tearDownActions.isEmpty()) + { + try + { + _tearDownActions.removeLast().performAction(null); + } + catch (Exception e) + { + exception = e; + } + } + + if (exception != null) + { + throw exception; + } + } + } + + @Test + public void clientAuthenticationSuccess() throws Exception + { + configPortAndAuthProvider("CN=foo"); + + HttpTestHelper helper = new HttpTestHelper(getBrokerAdmin(), null, _clientAuthPort); + helper.setTls(true); + helper.setKeyStore(_keyStore, "password"); + + String userId = helper.getJson("broker/getUser", new TypeReference<String>() {}, SC_OK); + assertThat(userId, startsWith("foo@")); + } + + @Test + public void unrecognisedCertification() throws Exception + { + configPortAndAuthProvider("CN=foo"); + + String keyStore = createKeyStoreDataUrl(getKeyCertPair("CN=bar"), "password"); + + HttpTestHelper helper = new HttpTestHelper(getBrokerAdmin(), null, _clientAuthPort); + helper.setTls(true); + helper.setKeyStore(keyStore, "password"); + + try + { + helper.getJson("broker/getUser", new TypeReference<String>() {}, SC_OK); + fail("Exception not thrown"); + } + catch (SSLHandshakeException e) + { + // PASS + } + } + + private void configPortAndAuthProvider(final String x500Name) throws Exception + { + + final KeyCertPair keyCertPair = getKeyCertPair(x500Name); + final byte[] cert = keyCertPair.getCertificate().getEncoded(); + + _keyStore = createKeyStoreDataUrl(keyCertPair, "password"); + + + final Deque<BaseAction<Void,Exception>> deleteActions = new ArrayDeque<>(); + + final Map<String, Object> authAttr = new HashMap<>(); + authAttr.put(ExternalAuthenticationManager.TYPE, "External"); + authAttr.put(ExternalAuthenticationManager.ATTRIBUTE_USE_FULL_DN, false); + + getHelper().submitRequest("authenticationprovider/myexternal","PUT", authAttr, SC_CREATED); + + deleteActions.add(object -> getHelper().submitRequest("authenticationprovider/myexternal", "DELETE", SC_OK)); + + final Map<String, Object> keystoreAttr = new HashMap<>(); + keystoreAttr.put(FileKeyStore.TYPE, "FileKeyStore"); + keystoreAttr.put(FileKeyStore.STORE_URL, "classpath:java_broker_keystore.jks"); + keystoreAttr.put(FileKeyStore.PASSWORD, "password"); + + getHelper().submitRequest("keystore/mykeystore","PUT", keystoreAttr, SC_CREATED); + deleteActions.add(object -> getHelper().submitRequest("keystore/mykeystore", "DELETE", SC_OK)); + + final Map<String, Object> truststoreAttr = new HashMap<>(); + truststoreAttr.put(ManagedPeerCertificateTrustStore.TYPE, ManagedPeerCertificateTrustStore.TYPE_NAME); + truststoreAttr.put(ManagedPeerCertificateTrustStore.STORED_CERTIFICATES, Collections.singletonList(Base64.getEncoder().encodeToString(cert))); + + getHelper().submitRequest("truststore/mytruststore","PUT", truststoreAttr, SC_CREATED); + deleteActions.add(object -> getHelper().submitRequest("truststore/mytruststore", "DELETE", SC_OK)); + + final Map<String, Object> portAttr = new HashMap<>(); + portAttr.put(Port.TYPE, "HTTP"); + portAttr.put(Port.PORT, 0); + portAttr.put(Port.AUTHENTICATION_PROVIDER, "myexternal"); + portAttr.put(Port.PROTOCOLS, Collections.singleton(Protocol.HTTP)); + portAttr.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + portAttr.put(Port.NEED_CLIENT_AUTH, true); + portAttr.put(Port.KEY_STORE, "mykeystore"); + portAttr.put(Port.TRUST_STORES, Collections.singletonList("mytruststore")); + + getHelper().submitRequest("port/myport","PUT", portAttr, SC_CREATED); + deleteActions.add(object -> getHelper().submitRequest("port/myport", "DELETE", SC_OK)); + + Map<String, Object> clientAuthPort = getHelper().getJsonAsMap("port/myport"); + int boundPort = Integer.parseInt(String.valueOf(clientAuthPort.get("boundPort"))); + + assertThat(boundPort, is(greaterThan(0))); + + _tearDownActions = deleteActions; + _clientAuthPort = boundPort; + } + + private String createKeyStoreDataUrl(final KeyCertPair keyCertPair, final String password) throws Exception + { + final KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(null, null); + Certificate[] certChain = new Certificate[] {keyCertPair.getCertificate()}; + keyStore.setKeyEntry("key1", keyCertPair.getPrivateKey(), password.toCharArray(), certChain); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) + { + keyStore.store(bos, password.toCharArray()); + bos.toByteArray(); + return DataUrlUtils.getDataUrlForBytes(bos.toByteArray()); + } + } + + private KeyCertPair getKeyCertPair(final String x500Name) throws Exception + { + return generateSelfSignedCertificate("RSA", "SHA256WithRSA", + 2048, Instant.now().toEpochMilli(), + Duration.of(365, ChronoUnit.DAYS).getSeconds(), + x500Name, + Collections.emptySet(), + Collections.emptySet()); + } + +} http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/20f47fec/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestHttpsClientCertAuthTest.java ---------------------------------------------------------------------- diff --git a/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestHttpsClientCertAuthTest.java b/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestHttpsClientCertAuthTest.java deleted file mode 100644 index 2df866a..0000000 --- a/systests/src/test/java/org/apache/qpid/systest/rest/BrokerRestHttpsClientCertAuthTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * - * 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.systest.rest; - -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 static org.apache.qpid.test.utils.TestSSLConstants.CERT_ALIAS_APP1; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.apache.qpid.server.model.AuthenticationProvider; -import org.apache.qpid.server.model.Port; -import org.apache.qpid.server.model.Protocol; -import org.apache.qpid.server.model.Transport; -import org.apache.qpid.server.security.auth.manager.ExternalAuthenticationManager; -import org.apache.qpid.test.utils.TestBrokerConfiguration; - -public class BrokerRestHttpsClientCertAuthTest extends QpidRestTestCase -{ - - @Override - public void setUp() throws Exception - { - setSystemProperty("javax.net.debug", "ssl"); - super.setUp(); - } - - @Override - protected void customizeConfiguration() throws Exception - { - super.customizeConfiguration(); - - Map<String, Object> newAttributes = new HashMap<String, Object>(); - newAttributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.HTTP)); - newAttributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); - newAttributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); - newAttributes.put(Port.TRUST_STORES, Collections.singleton(TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE)); - newAttributes.put(Port.NEED_CLIENT_AUTH,"true"); - - - Map<String, Object> externalProviderAttributes = new HashMap<String, Object>(); - externalProviderAttributes.put(AuthenticationProvider.TYPE, ExternalAuthenticationManager.PROVIDER_TYPE); - externalProviderAttributes.put(AuthenticationProvider.NAME, EXTERNAL_AUTHENTICATION_PROVIDER); - getDefaultBrokerConfiguration().addObjectConfiguration(AuthenticationProvider.class, externalProviderAttributes); - - // set password authentication provider on http port for the tests - getDefaultBrokerConfiguration().setObjectAttribute(Port.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.AUTHENTICATION_PROVIDER, - EXTERNAL_AUTHENTICATION_PROVIDER); - - getDefaultBrokerConfiguration().setObjectAttributes(Port.class, TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, newAttributes); - } - - public void testGetWithHttps() throws Exception - { - _restTestHelper = new RestTestHelper(getDefaultBroker().getHttpsPort()); - _restTestHelper.setUseSslAuth(true); - _restTestHelper.setTruststore(TRUSTSTORE, TRUSTSTORE_PASSWORD); - _restTestHelper.setKeystore(KEYSTORE, KEYSTORE_PASSWORD); - _restTestHelper.setClientAuthAlias(CERT_ALIAS_APP1); - - Map<String, Object> saslData = getRestTestHelper().getJsonAsMap("broker"); - - Asserts.assertAttributesPresent(saslData, "modelVersion"); - } -} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org