You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by or...@apache.org on 2019/05/10 20:07:09 UTC
[qpid-jms-amqp-0-x] 01/02: QPID-8255: [JMS AMQ 0-x] Stop using
non-ASCII characters in internal passwords
This is an automated email from the ASF dual-hosted git repository.
orudyy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-jms-amqp-0-x.git
commit 5ec0a566a465f7e249d750157b68c99b7789c490
Author: Alex Rudyy <or...@apache.org>
AuthorDate: Fri May 10 20:50:55 2019 +0100
QPID-8255: [JMS AMQ 0-x] Stop using non-ASCII characters in internal passwords
---
.../apache/qpid/transport/ConnectionSettings.java | 23 +-
.../main/java/org/apache/qpid/util/Strings.java | 17 +
.../systest/core/AbstractSpawnQpidBrokerAdmin.java | 35 ++-
.../org/apache/qpid/systest/core/BrokerAdmin.java | 3 +
.../systest/core/brokerj/AmqpManagementFacade.java | 3 +-
.../src/main/resources/tls/broker_keystore.jks | Bin 0 -> 4425 bytes
.../apache/qpid/systest/connection/TlsTest.java | 345 +++++++++++++++++++++
7 files changed, 399 insertions(+), 27 deletions(-)
diff --git a/client/src/main/java/org/apache/qpid/transport/ConnectionSettings.java b/client/src/main/java/org/apache/qpid/transport/ConnectionSettings.java
index b8e9737..16021e4 100644
--- a/client/src/main/java/org/apache/qpid/transport/ConnectionSettings.java
+++ b/client/src/main/java/org/apache/qpid/transport/ConnectionSettings.java
@@ -20,32 +20,29 @@
*/
package org.apache.qpid.transport;
-import static org.apache.qpid.transport.LegacyClientProperties.AMQJ_HEARTBEAT_DELAY;
-import static org.apache.qpid.transport.LegacyClientProperties.AMQJ_HEARTBEAT_TIMEOUT_FACTOR;
-import static org.apache.qpid.transport.LegacyClientProperties.IDLE_TIMEOUT_PROP_NAME;
import static org.apache.qpid.configuration.ClientProperties.QPID_HEARTBEAT_INTERVAL;
import static org.apache.qpid.configuration.ClientProperties.QPID_HEARTBEAT_INTERVAL_010_DEFAULT;
import static org.apache.qpid.configuration.ClientProperties.QPID_HEARTBEAT_TIMEOUT_FACTOR;
import static org.apache.qpid.configuration.ClientProperties.QPID_HEARTBEAT_TIMEOUT_FACTOR_DEFAULT;
-import static org.apache.qpid.transport.LegacyClientProperties.AMQJ_TCP_NODELAY_PROP_NAME;
import static org.apache.qpid.configuration.ClientProperties.QPID_SSL_KEY_MANAGER_FACTORY_ALGORITHM_PROP_NAME;
-import static org.apache.qpid.transport.LegacyClientProperties.QPID_SSL_KEY_STORE_CERT_TYPE_PROP_NAME;
import static org.apache.qpid.configuration.ClientProperties.QPID_SSL_TRUST_MANAGER_FACTORY_ALGORITHM_PROP_NAME;
-import static org.apache.qpid.transport.LegacyClientProperties.QPID_SSL_TRUST_STORE_CERT_TYPE_PROP_NAME;
import static org.apache.qpid.configuration.ClientProperties.QPID_TCP_NODELAY_PROP_NAME;
import static org.apache.qpid.configuration.ClientProperties.RECEIVE_BUFFER_SIZE_PROP_NAME;
import static org.apache.qpid.configuration.ClientProperties.SEND_BUFFER_SIZE_PROP_NAME;
+import static org.apache.qpid.transport.LegacyClientProperties.AMQJ_HEARTBEAT_DELAY;
+import static org.apache.qpid.transport.LegacyClientProperties.AMQJ_HEARTBEAT_TIMEOUT_FACTOR;
+import static org.apache.qpid.transport.LegacyClientProperties.AMQJ_TCP_NODELAY_PROP_NAME;
+import static org.apache.qpid.transport.LegacyClientProperties.IDLE_TIMEOUT_PROP_NAME;
import static org.apache.qpid.transport.LegacyClientProperties.LEGACY_RECEIVE_BUFFER_SIZE_PROP_NAME;
import static org.apache.qpid.transport.LegacyClientProperties.LEGACY_SEND_BUFFER_SIZE_PROP_NAME;
+import static org.apache.qpid.transport.LegacyClientProperties.QPID_SSL_KEY_STORE_CERT_TYPE_PROP_NAME;
+import static org.apache.qpid.transport.LegacyClientProperties.QPID_SSL_TRUST_STORE_CERT_TYPE_PROP_NAME;
import java.io.FileInputStream;
import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
-import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@@ -62,6 +59,7 @@ import org.apache.qpid.configuration.QpidProperty;
import org.apache.qpid.ssl.SSLContextFactory;
import org.apache.qpid.transport.network.security.ssl.QpidClientX509KeyManager;
import org.apache.qpid.transport.network.security.ssl.SSLUtil;
+import org.apache.qpid.util.Strings;
/**
@@ -74,8 +72,6 @@ public class ConnectionSettings
{
public static final String WILDCARD_ADDRESS = "*";
- private static final SecureRandom RANDOM = new SecureRandom();
-
private String _transport = "tcp";
private String host = "localhost";
private String vhost;
@@ -658,10 +654,7 @@ public class ConnectionSettings
java.security.KeyStore inMemoryKeyStore =
java.security.KeyStore.getInstance(java.security.KeyStore.getDefaultType());
- byte[] bytes = new byte[64];
- char[] chars = new char[64];
- RANDOM.nextBytes(bytes);
- StandardCharsets.US_ASCII.decode(ByteBuffer.wrap(bytes)).get(chars);
+ char[] chars = Strings.randomAlphaNumericString(64).toCharArray();
inMemoryKeyStore.load(null, chars);
inMemoryKeyStore.setKeyEntry("1", privateKey, chars, certs);
diff --git a/client/src/main/java/org/apache/qpid/util/Strings.java b/client/src/main/java/org/apache/qpid/util/Strings.java
index e2cd921..a721844 100644
--- a/client/src/main/java/org/apache/qpid/util/Strings.java
+++ b/client/src/main/java/org/apache/qpid/util/Strings.java
@@ -29,6 +29,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.Random;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Matcher;
@@ -41,10 +42,26 @@ import java.util.regex.Pattern;
public final class Strings
{
+ private static final String NUMBERS = "0123456789";
+ private static final String LETTERS = "abcdefghijklmnopqrstuvwxwy";
+ private static final String OTHERS = "_-";
+ private static final char[] CHARACTERS = (NUMBERS + LETTERS + LETTERS.toUpperCase() + OTHERS).toCharArray();
+ private static final Random RANDOM = new Random();
+
private Strings()
{
}
+ public static String randomAlphaNumericString(int maxLength)
+ {
+ char[] result = new char[maxLength];
+ for (int i = 0; i < maxLength; i++)
+ {
+ result[i] = CHARACTERS[RANDOM.nextInt(CHARACTERS.length)];
+ }
+ return new String(result);
+ }
+
private static final byte[] EMPTY = new byte[0];
private static final ThreadLocal<char[]> charbuf = new ThreadLocal<char[]>()
diff --git a/systests/src/main/java/org/apache/qpid/systest/core/AbstractSpawnQpidBrokerAdmin.java b/systests/src/main/java/org/apache/qpid/systest/core/AbstractSpawnQpidBrokerAdmin.java
index 45b5024..2f66f98 100644
--- a/systests/src/main/java/org/apache/qpid/systest/core/AbstractSpawnQpidBrokerAdmin.java
+++ b/systests/src/main/java/org/apache/qpid/systest/core/AbstractSpawnQpidBrokerAdmin.java
@@ -144,14 +144,7 @@ public abstract class AbstractSpawnQpidBrokerAdmin implements BrokerAdmin
switch (portType)
{
case AMQP:
- for (ListeningPort p : _ports)
- {
- if (p.getTransport().contains("TCP"))
- {
- port = p.getPort();
- break;
- }
- }
+ port = getPort("TCP");
break;
default:
throw new IllegalArgumentException(String.format("Unknown port type '%s'", portType));
@@ -163,6 +156,20 @@ public abstract class AbstractSpawnQpidBrokerAdmin implements BrokerAdmin
return new InetSocketAddress(port);
}
+ private Integer getPort(final String transport)
+ {
+ Integer port = null;
+ for (ListeningPort p : _ports)
+ {
+ if (p.getTransport().contains(transport))
+ {
+ port = p.getPort();
+ break;
+ }
+ }
+ return port;
+ }
+
@Override
public Connection getConnection() throws JMSException
{
@@ -386,7 +393,15 @@ public abstract class AbstractSpawnQpidBrokerAdmin implements BrokerAdmin
@Override
public Connection getConnection(final String virtualHostName,
- final Map<String, String> options) throws JMSException
+ final Map<String, String> options) throws JMSException
+ {
+ return getConnection(virtualHostName, options, getBrokerAddress(PortType.AMQP).getPort());
+ }
+
+ @Override
+ public Connection getConnection(final String virtualHostName,
+ final Map<String, String> options,
+ final int port) throws JMSException
{
final Hashtable<Object, Object> initialContextEnvironment = new Hashtable<>();
initialContextEnvironment.put(Context.INITIAL_CONTEXT_FACTORY,
@@ -396,7 +411,7 @@ public abstract class AbstractSpawnQpidBrokerAdmin implements BrokerAdmin
StringBuilder url = new StringBuilder(String.format(urlTemplate,
"spawn_broker_admin",
virtualHostName,
- getBrokerAddress(PortType.AMQP).getPort()));
+ port));
if (options != null)
{
for (Map.Entry<String, String> option : options.entrySet())
diff --git a/systests/src/main/java/org/apache/qpid/systest/core/BrokerAdmin.java b/systests/src/main/java/org/apache/qpid/systest/core/BrokerAdmin.java
index f4ec92a..7edbfb0 100644
--- a/systests/src/main/java/org/apache/qpid/systest/core/BrokerAdmin.java
+++ b/systests/src/main/java/org/apache/qpid/systest/core/BrokerAdmin.java
@@ -49,6 +49,9 @@ public interface BrokerAdmin
Connection getConnection(Map<String, String> options) throws JMSException;
Connection getConnection(String virtualHostName,
Map<String, String> options) throws JMSException;
+ Connection getConnection(String virtualHostName,
+ Map<String, String> options,
+ int port) throws JMSException;
enum PortType
{
diff --git a/systests/src/main/java/org/apache/qpid/systest/core/brokerj/AmqpManagementFacade.java b/systests/src/main/java/org/apache/qpid/systest/core/brokerj/AmqpManagementFacade.java
index 1331c89..e31d91c 100644
--- a/systests/src/main/java/org/apache/qpid/systest/core/brokerj/AmqpManagementFacade.java
+++ b/systests/src/main/java/org/apache/qpid/systest/core/brokerj/AmqpManagementFacade.java
@@ -348,8 +348,7 @@ public class AmqpManagementFacade
}
}
- @SuppressWarnings(value = {"unused", "unchecked"})
- List<Map<String, Object>> managementQueryObjects(final String type, final Session session)
+ public List<Map<String, Object>> managementQueryObjects(final String type, final Session session)
throws JMSException
{
Destination replyToDestination = session.createQueue(AMQP_0_X_REPLY_TO_DESTINATION);
diff --git a/systests/src/main/resources/tls/broker_keystore.jks b/systests/src/main/resources/tls/broker_keystore.jks
new file mode 100644
index 0000000..b45991f
Binary files /dev/null and b/systests/src/main/resources/tls/broker_keystore.jks differ
diff --git a/systests/src/test/java/org/apache/qpid/systest/connection/TlsTest.java b/systests/src/test/java/org/apache/qpid/systest/connection/TlsTest.java
new file mode 100644
index 0000000..dcfcffa
--- /dev/null
+++ b/systests/src/test/java/org/apache/qpid/systest/connection/TlsTest.java
@@ -0,0 +1,345 @@
+/*
+ * 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.connection;
+
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeThat;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.Key;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Session;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.systest.core.BrokerAdmin;
+import org.apache.qpid.systest.core.JmsTestBase;
+import org.apache.qpid.systest.core.brokerj.AmqpManagementFacade;
+
+public class TlsTest extends JmsTestBase
+{
+ private static final String KEY_STORE = "/tls/client_keystore.jks";
+ private static final String BROKER_KEY_STORE = "/tls/broker_keystore.jks";
+ private static final String STORE_PASSWORD = "password";
+
+ private List<File> _files;
+ private File _brokerKeyStore;
+ private File _clientCertificatePath;
+ private File _clientPrivateKeyPath;
+ private File _brokerCertificatePath;
+ private String _portName;
+ private String _keyStoreName;
+ private String _trustStoreName;
+
+ @Before
+ public void setUp() throws Exception
+ {
+ assumeThat("Broker-j specific functionality is used by the test suite",
+ getBrokerAdmin().getBrokerType(),
+ is(equalTo(BrokerAdmin.BrokerType.BROKERJ)));
+
+ _files = new ArrayList<>();
+
+ _brokerKeyStore = copyResource(BROKER_KEY_STORE);
+ _files.add(_brokerKeyStore);
+ final File[] clientResources = extractResourcesFromTestKeyStore(KEY_STORE, "app1");
+ _files.addAll(Arrays.asList(clientResources));
+ _clientCertificatePath = clientResources[1];
+ _clientPrivateKeyPath = clientResources[0];
+ final File[] brokerResources = extractResourcesFromTestKeyStore(BROKER_KEY_STORE, "java-broker");
+ _files.addAll(Arrays.asList(brokerResources));
+ _brokerCertificatePath = brokerResources[1];
+ _portName = getTestName() + "Port";
+ _keyStoreName = _portName + "KeyStore";
+ _trustStoreName = _portName + "TrustStore";
+ }
+
+
+ @After
+ public void tearDown()
+ {
+ if (_files != null)
+ {
+ _files.forEach(f -> f.delete());
+ }
+ }
+
+ @Test
+ public void testCreateSSLWithCertFileAndPrivateKey() throws Exception
+ {
+ final int port = createTlsPort(true, false);
+
+ final Map<String, String> options = new HashMap<>();
+ options.put("client_cert_path", encodePathOption(_clientCertificatePath.getCanonicalPath()));
+ options.put("client_cert_priv_key_path", encodePathOption(_clientPrivateKeyPath.getCanonicalPath()));
+ options.put("trusted_certs_path", encodePathOption(_brokerCertificatePath.getCanonicalPath()));
+ options.put("ssl", "true");
+ final Connection connection =
+ getBrokerAdmin().getConnection(getBrokerAdmin().getVirtualHostName(), options, port);
+ try
+ {
+ assertConnection(connection);
+ }
+ finally
+ {
+ connection.close();
+ }
+ }
+
+ private File copyResource(final String resource) throws IOException
+ {
+ Path brokerKeyStore = Files.createTempFile("key_store", ".jks");
+ try (final InputStream in = getClass().getResourceAsStream(resource))
+ {
+ Files.copy(in, brokerKeyStore, REPLACE_EXISTING);
+ }
+ return brokerKeyStore.toFile();
+ }
+
+
+ public int createTlsPort(final boolean needClientAuth,
+ final boolean wantClientAuth) throws Exception
+ {
+ final AmqpManagementFacade managementFacade = new AmqpManagementFacade();
+ Connection connection = getBrokerManagementConnection();
+ try
+ {
+ connection.start();
+
+ final String authenticationProviderName = getAuthenticationProviderName(connection, managementFacade);
+ createKeyStore(connection, managementFacade);
+ createTrustStore(connection, managementFacade);
+ createPort(connection,
+ managementFacade,
+ needClientAuth,
+ wantClientAuth,
+ authenticationProviderName);
+ return getBoundPort(connection, managementFacade);
+ }
+ finally
+ {
+ connection.close();
+ }
+ }
+
+ private void createKeyStore(final Connection connection,
+ final AmqpManagementFacade managementFacade) throws JMSException
+ {
+ final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ try
+ {
+ final Map<String, Object> keyStoreAttributes = new HashMap<>();
+ keyStoreAttributes.put("storeUrl", _brokerKeyStore.getAbsolutePath());
+ keyStoreAttributes.put("password", STORE_PASSWORD);
+ keyStoreAttributes.put("keyStoreType", "pkcs12");
+ managementFacade.createEntityUsingAmqpManagement(_keyStoreName,
+ "org.apache.qpid.server.security.FileKeyStore",
+ keyStoreAttributes,
+ session);
+ }
+ finally
+ {
+ session.close();
+ }
+ }
+
+ private void createTrustStore(final Connection connection,
+ final AmqpManagementFacade managementFacade) throws JMSException
+ {
+ final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ try
+ {
+ final Map<String, Object> trustStoreAttributes = new HashMap<>();
+ trustStoreAttributes.put("certificatesUrl", _clientCertificatePath.getAbsolutePath());
+ managementFacade.createEntityUsingAmqpManagement(_trustStoreName,
+ "org.apache.qpid.server.security.NonJavaTrustStore",
+ trustStoreAttributes,
+ session);
+ }
+ finally
+ {
+ session.close();
+ }
+ }
+
+ private void createPort(final Connection connection,
+ final AmqpManagementFacade managementFacade,
+ final boolean needClientAuth,
+ final boolean wantClientAuth,
+ final String authenticationProvider)
+ throws JMSException
+ {
+ final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ try
+ {
+ final Map<String, Object> sslPortAttributes = new HashMap<>();
+ sslPortAttributes.put("transports", "[\"SSL\"]");
+ sslPortAttributes.put("port", 0);
+ sslPortAttributes.put("authenticationProvider", authenticationProvider);
+ sslPortAttributes.put("needClientAuth", needClientAuth);
+ sslPortAttributes.put("wantClientAuth", wantClientAuth);
+ sslPortAttributes.put("name", _portName);
+ sslPortAttributes.put("keyStore", _keyStoreName);
+ sslPortAttributes.put("trustStores", "[\"" + _trustStoreName + "\"]");
+
+ managementFacade.createEntityUsingAmqpManagement(_portName,
+ "org.apache.qpid.AmqpPort",
+ sslPortAttributes,
+ session);
+ }
+ finally
+ {
+ session.close();
+ }
+ }
+
+ private int getBoundPort(final Connection connection,
+ final AmqpManagementFacade managementFacade) throws JMSException
+ {
+ final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ try
+ {
+ final Map<String, Object> portEffectiveAttributes =
+ managementFacade.readEntityUsingAmqpManagement(_portName,
+ "org.apache.qpid.AmqpPort",
+ false,
+ session);
+ if (portEffectiveAttributes.containsKey("boundPort"))
+ {
+ return (int) portEffectiveAttributes.get("boundPort");
+ }
+ throw new RuntimeException("Bound port is not found");
+ }
+ finally
+ {
+ session.close();
+ }
+ }
+
+ private String getAuthenticationProviderName(final Connection connection,
+ final AmqpManagementFacade managementFacade) throws JMSException
+ {
+ String authenticationProvider = null;
+ final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ try
+ {
+ final List<Map<String, Object>> ports =
+ managementFacade.managementQueryObjects("org.apache.qpid.AmqpPort", session);
+ for (Map<String, Object> port : ports)
+ {
+ final String name = String.valueOf(port.get("name"));
+
+ final Session s = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ try
+ {
+ Map<String, Object> attributes = managementFacade.readEntityUsingAmqpManagement(name,
+ "org.apache.qpid.AmqpPort",
+ false,
+ s);
+ if (attributes.get("boundPort")
+ .equals(getBrokerAdmin().getBrokerAddress(BrokerAdmin.PortType.AMQP).getPort()))
+ {
+ authenticationProvider = String.valueOf(attributes.get("authenticationProvider"));
+ break;
+ }
+ }
+ finally
+ {
+ s.close();
+ }
+ }
+ }
+ finally
+ {
+ session.close();
+ }
+ return authenticationProvider;
+ }
+
+ private File[] extractResourcesFromTestKeyStore(final String keyStore, final String alias) throws Exception
+ {
+ final java.security.KeyStore ks = java.security.KeyStore.getInstance("pkcs12");
+ try (final InputStream in = getClass().getResourceAsStream(keyStore))
+ {
+ ks.load(in, STORE_PASSWORD.toCharArray());
+ }
+
+ final File privateKeyFile = Files.createTempFile(getTestName(), ".private-key.der").toFile();
+ try (FileOutputStream kos = new FileOutputStream(privateKeyFile))
+ {
+ Key pvt = ks.getKey(alias, STORE_PASSWORD.toCharArray());
+ kos.write(pvt.getEncoded());
+ }
+
+ final File certificateFile = Files.createTempFile(getTestName(), ".certificate.der").toFile();
+ try (FileOutputStream cos = new FileOutputStream(certificateFile))
+ {
+ Certificate[] chain = ks.getCertificateChain(alias);
+ for (Certificate pub : chain)
+ {
+ cos.write(pub.getEncoded());
+ }
+ cos.flush();
+ }
+
+ return new File[]{privateKeyFile, certificateFile};
+ }
+
+ private String encodePathOption(final String canonicalPath)
+ {
+ try
+ {
+ return URLEncoder.encode(canonicalPath, StandardCharsets.UTF_8.name()).replace("+", "%20");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void assertConnection(final Connection connection) throws JMSException
+ {
+ assertNotNull("connection should be successful", connection);
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ assertNotNull("create session should be successful", session);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org