Skip to content

Commit

Permalink
PROTON-2727 Allow reading key and trust store files from class path
Browse files Browse the repository at this point in the history
Use prefix notation to indicate that a store is to be found on the class
path, prefix is 'classpath:' and add some tests for that case.
  • Loading branch information
tabish121 committed Mar 19, 2024
1 parent f166dcb commit 8222234
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
*/
package org.apache.qpid.protonj2.client.transport.netty4;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
Expand Down Expand Up @@ -56,6 +56,10 @@ public final class SslSupport {

private static final Logger LOG = LoggerFactory.getLogger(SslSupport.class);

public static final String FROM_CLASSPATH_PREFIX = "classpath:";
public static final String FROM_FILE_PREFIX = "file:";
public static final String FROM_FILE_URL_PREFIX = "file://";

/**
* Determines if Netty OpenSSL support is available and applicable based on the configuration
* in the given TransportOptions instance.
Expand Down Expand Up @@ -437,11 +441,35 @@ private static void validateAlias(KeyStore store, String alias) throws IllegalAr
}

private static KeyStore loadStore(String storePath, final String password, String storeType) throws Exception {
KeyStore store = KeyStore.getInstance(storeType);
try (InputStream in = new FileInputStream(new File(storePath));) {
final KeyStore store = KeyStore.getInstance(storeType);

try (InputStream in = openStoreAtLocation(storePath)) {
store.load(in, password != null ? password.toCharArray() : null);
} catch (Exception ex) {
LOG.trace("Caught Error loading store: {}", ex.getMessage(), ex);
throw ex;
}

return store;
}

private static InputStream openStoreAtLocation(final String storePath) throws IOException {
final InputStream stream;

if (storePath.startsWith(FROM_CLASSPATH_PREFIX)) {
stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(storePath.substring(FROM_CLASSPATH_PREFIX.length()));
} else if (storePath.startsWith(FROM_FILE_URL_PREFIX)) {
stream = new FileInputStream(storePath.substring(FROM_FILE_URL_PREFIX.length()));
} else if (storePath.startsWith(FROM_FILE_PREFIX)) {
stream = new FileInputStream(storePath.substring(FROM_FILE_PREFIX.length()));
} else {
stream = new FileInputStream(storePath);
}

if (stream == null) {
throw new IOException("Could no locate KeyStore at location: " + storePath);
}

return stream;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
*/
package org.apache.qpid.protonj2.client.transport.netty5;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
Expand Down Expand Up @@ -56,6 +56,10 @@ public final class SslSupport {

private static final Logger LOG = LoggerFactory.getLogger(SslSupport.class);

public static final String FROM_CLASSPATH_PREFIX = "classpath:";
public static final String FROM_FILE_PREFIX = "file:";
public static final String FROM_FILE_URL_PREFIX = "file://";

/**
* Determines if Netty OpenSSL support is available and applicable based on the configuration
* in the given TransportOptions instance.
Expand Down Expand Up @@ -438,10 +442,33 @@ private static void validateAlias(KeyStore store, String alias) throws IllegalAr

private static KeyStore loadStore(String storePath, final String password, String storeType) throws Exception {
KeyStore store = KeyStore.getInstance(storeType);
try (InputStream in = new FileInputStream(new File(storePath));) {
try (InputStream in = openStoreAtLocation(storePath)) {
store.load(in, password != null ? password.toCharArray() : null);
} catch (Exception ex) {
LOG.trace("Caught Error loading store: {}", ex.getMessage(), ex);
throw ex;
}

return store;
}

private static InputStream openStoreAtLocation(final String storePath) throws IOException {
final InputStream stream;

if (storePath.startsWith(FROM_CLASSPATH_PREFIX)) {
stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(storePath.substring(FROM_CLASSPATH_PREFIX.length()));
} else if (storePath.startsWith(FROM_FILE_URL_PREFIX)) {
stream = new FileInputStream(storePath.substring(FROM_FILE_URL_PREFIX.length()));
} else if (storePath.startsWith(FROM_FILE_PREFIX)) {
stream = new FileInputStream(storePath.substring(FROM_FILE_PREFIX.length()));
} else {
stream = new FileInputStream(storePath);
}

if (stream == null) {
throw new IOException("Could no locate KeyStore at location: " + storePath);
}

return stream;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public class SslConnectionTest extends ImperativeClientTestCase {
private static final String BROKER_PKCS12_TRUSTSTORE = "src/test/resources/broker-pkcs12.truststore";
private static final String CLIENT_MULTI_KEYSTORE = "src/test/resources/client-multiple-keys-jks.keystore";
private static final String CLIENT_JKS_TRUSTSTORE = "src/test/resources/client-jks.truststore";
private static final String CLIENT_JKS_TRUSTSTORE_CLASSPATH = "classpath:client-jks.truststore";
private static final String CLIENT_PKCS12_TRUSTSTORE = "src/test/resources/client-pkcs12.truststore";
private static final String OTHER_CA_TRUSTSTORE = "src/test/resources/other-ca-jks.truststore";
private static final String CLIENT_JKS_KEYSTORE = "src/test/resources/client-jks.keystore";
Expand Down Expand Up @@ -100,18 +101,31 @@ protected ConnectionOptions connectionOptions() {

@Test
public void testCreateAndCloseSslConnectionJDK() throws Exception {
testCreateAndCloseSslConnection(false);
testCreateAndCloseSslConnection(false, false);
}

@Test
public void testCreateAndCloseSslConnectionJDKTrustStoreOnClasspath() throws Exception {
testCreateAndCloseSslConnection(false, true);
}

@Test
public void testCreateAndCloseSslConnectionOpenSSL() throws Exception {
assumeTrue(OpenSsl.isAvailable());
assumeTrue(OpenSsl.supportsKeyManagerFactory());

testCreateAndCloseSslConnection(true);
testCreateAndCloseSslConnection(true, false);
}

private void testCreateAndCloseSslConnection(boolean openSSL) throws Exception {
@Test
public void testCreateAndCloseSslConnectionOpenSSLTrustStoreOnClasspath() throws Exception {
assumeTrue(OpenSsl.isAvailable());
assumeTrue(OpenSsl.supportsKeyManagerFactory());

testCreateAndCloseSslConnection(true, true);
}

private void testCreateAndCloseSslConnection(boolean openSSL, boolean storeFromClassPath) throws Exception {
ProtonTestServerOptions serverOptions = serverOptions();
serverOptions.setSecure(true);
serverOptions.setKeyStoreLocation(BROKER_JKS_KEYSTORE);
Expand All @@ -124,11 +138,12 @@ private void testCreateAndCloseSslConnection(boolean openSSL) throws Exception {
peer.expectClose().respond();
peer.start();

URI remoteURI = peer.getServerURI();
final URI remoteURI = peer.getServerURI();
final String storeLocation = storeFromClassPath ? CLIENT_JKS_TRUSTSTORE_CLASSPATH : CLIENT_JKS_TRUSTSTORE;

ConnectionOptions clientOptions = connectionOptions();
clientOptions.sslOptions()
.trustStoreLocation(CLIENT_JKS_TRUSTSTORE)
.trustStoreLocation(storeLocation)
.trustStorePassword(PASSWORD)
.allowNativeSSL(openSSL);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public class SslTransportTest extends TcpTransportTest {
public static final String CLIENT_MULTI_KEYSTORE = "src/test/resources/client-multiple-keys-jks.keystore";
public static final String CLIENT_TRUSTSTORE = "src/test/resources/client-jks.truststore";
public static final String OTHER_CA_TRUSTSTORE = "src/test/resources/other-ca-jks.truststore";
public static final String SERVER_CLASSPATH_KEYSTORE = "classpath:broker-jks.keystore";
public static final String SERVER_CLASSPATH_TRUSTSTORE = "classpath:broker-jks.truststore";

public static final String CLIENT_KEY_ALIAS = "client";
public static final String CLIENT_DN = "O=Client,CN=client";
Expand Down Expand Up @@ -210,6 +212,31 @@ public void testConnectToServerClientTrustsAll() throws Exception {
assertTrue(exceptions.isEmpty());
}

@Test
public void testConnectToServerWithServerClasspathStores() throws Exception {
try (NettyEchoServer server = createEchoServer()) {
server.start();

final int port = server.getServerPort();

Transport transport = createTransport(createTransportOptions(), createServerClasspathSSLOptions());
try {
transport.connect(HOSTNAME, port, testListener).awaitConnect();
LOG.info("Connection established to test server: {}:{}", HOSTNAME, port);
} catch (Exception e) {
fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e);
}

assertTrue(transport.isConnected());
assertTrue(transport.isSecure());

transport.close();
}

logTransportErrors();
assertTrue(exceptions.isEmpty());
}

@Test
public void testConnectWithNeedClientAuth() throws Exception {
try (NettyEchoServer server = createEchoServer(true)) {
Expand Down Expand Up @@ -386,4 +413,19 @@ protected SslOptions createServerSSLOptions() {

return options;
}

protected SslOptions createServerClasspathSSLOptions() {
SslOptions options = new SslOptions();

// Run the server in JDK mode for now to validate cross compatibility
options.sslEnabled(true);
options.keyStoreLocation(SERVER_CLASSPATH_KEYSTORE);
options.keyStorePassword(PASSWORD);
options.trustStoreLocation(SERVER_CLASSPATH_TRUSTSTORE);
options.trustStorePassword(PASSWORD);
options.storeType(KEYSTORE_TYPE);
options.verifyHost(false);

return options;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public class SslTransportTest extends TcpTransportTest {
public static final String CLIENT_MULTI_KEYSTORE = "src/test/resources/client-multiple-keys-jks.keystore";
public static final String CLIENT_TRUSTSTORE = "src/test/resources/client-jks.truststore";
public static final String OTHER_CA_TRUSTSTORE = "src/test/resources/other-ca-jks.truststore";
public static final String SERVER_CLASSPATH_KEYSTORE = "classpath:broker-jks.keystore";
public static final String SERVER_CLASSPATH_TRUSTSTORE = "classpath:broker-jks.truststore";

public static final String CLIENT_KEY_ALIAS = "client";
public static final String CLIENT_DN = "O=Client,CN=client";
Expand Down Expand Up @@ -210,6 +212,31 @@ public void testConnectToServerClientTrustsAll() throws Exception {
assertTrue(exceptions.isEmpty());
}

@Test
public void testConnectToServerWithServerClasspathStores() throws Exception {
try (NettyEchoServer server = createEchoServer()) {
server.start();

final int port = server.getServerPort();

Transport transport = createTransport(createTransportOptions(), createServerClasspathSSLOptions());
try {
transport.connect(HOSTNAME, port, testListener).awaitConnect();
LOG.info("Connection established to test server: {}:{}", HOSTNAME, port);
} catch (Exception e) {
fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e);
}

assertTrue(transport.isConnected());
assertTrue(transport.isSecure());

transport.close();
}

logTransportErrors();
assertTrue(exceptions.isEmpty());
}

@Test
public void testConnectWithNeedClientAuth() throws Exception {
try (NettyEchoServer server = createEchoServer(true)) {
Expand Down Expand Up @@ -390,4 +417,19 @@ protected SslOptions createServerSSLOptions() {

return options;
}

protected SslOptions createServerClasspathSSLOptions() {
SslOptions options = new SslOptions();

// Run the server in JDK mode for now to validate cross compatibility
options.sslEnabled(true);
options.keyStoreLocation(SERVER_CLASSPATH_KEYSTORE);
options.keyStorePassword(PASSWORD);
options.trustStoreLocation(SERVER_CLASSPATH_TRUSTSTORE);
options.trustStorePassword(PASSWORD);
options.storeType(KEYSTORE_TYPE);
options.verifyHost(false);

return options;
}
}

0 comments on commit 8222234

Please sign in to comment.