diff --git a/src/main/java/com/rabbitmq/client/ConnectionFactory.java b/src/main/java/com/rabbitmq/client/ConnectionFactory.java index 279a015af7..e9af65a6b5 100644 --- a/src/main/java/com/rabbitmq/client/ConnectionFactory.java +++ b/src/main/java/com/rabbitmq/client/ConnectionFactory.java @@ -15,8 +15,6 @@ package com.rabbitmq.client; -import static java.util.concurrent.TimeUnit.*; - import com.rabbitmq.client.impl.AMQConnection; import com.rabbitmq.client.impl.ConnectionParams; import com.rabbitmq.client.impl.CredentialsProvider; @@ -32,6 +30,10 @@ import com.rabbitmq.client.impl.recovery.RetryHandler; import com.rabbitmq.client.impl.recovery.TopologyRecoveryFilter; +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; @@ -50,10 +52,8 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeoutException; import java.util.function.Predicate; -import javax.net.SocketFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; + +import static java.util.concurrent.TimeUnit.MINUTES; /** * Convenience factory class to facilitate opening a {@link Connection} to a RabbitMQ node. @@ -132,7 +132,7 @@ public class ConnectionFactory implements Cloneable { // connections uses, see rabbitmq/rabbitmq-java-client#86 private ExecutorService shutdownExecutor; private ScheduledExecutorService heartbeatExecutor; - private SocketConfigurator socketConf = new DefaultSocketConfigurator(); + private SocketConfigurator socketConf = SocketConfigurators.defaultConfigurator(); private ExceptionHandler exceptionHandler = new DefaultExceptionHandler(); private CredentialsProvider credentialsProvider = new DefaultCredentialsProvider(DEFAULT_USER, DEFAULT_PASS); @@ -729,6 +729,44 @@ public void useSslProtocol(SSLContext context) { setSocketFactory(context.getSocketFactory()); } + /** + * Enable server hostname verification for TLS connections. + *

+ * This enables hostname verification regardless of the IO mode + * used (blocking or non-blocking IO). + *

+ * This can be called typically after setting the {@link SSLContext} + * with one of the useSslProtocol methods. + * + * @see NioParams#enableHostnameVerification() + * @see NioParams#setSslEngineConfigurator(SslEngineConfigurator) + * @see SslEngineConfigurators#ENABLE_HOSTNAME_VERIFICATION + * @see SocketConfigurators#ENABLE_HOSTNAME_VERIFICATION + * @see ConnectionFactory#useSslProtocol(String) + * @see ConnectionFactory#useSslProtocol(SSLContext) + * @see ConnectionFactory#useSslProtocol() + * @see ConnectionFactory#useSslProtocol(String, TrustManager) + */ + public void enableHostnameVerification() { + enableHostnameVerificationForNio(); + enableHostnameVerificationForBlockingIo(); + } + + protected void enableHostnameVerificationForNio() { + if (this.nioParams == null) { + this.nioParams = new NioParams(); + } + this.nioParams = this.nioParams.enableHostnameVerification(); + } + + protected void enableHostnameVerificationForBlockingIo() { + if (this.socketConf == null) { + this.socketConf = SocketConfigurators.builder().defaultConfigurator().enableHostnameVerification().build(); + } else { + this.socketConf = this.socketConf.andThen(SocketConfigurators.enableHostnameVerification()); + } + } + public static String computeDefaultTlsProcotol(String[] supportedProtocols) { if(supportedProtocols != null) { for (String supportedProtocol : supportedProtocols) { diff --git a/src/main/java/com/rabbitmq/client/SocketChannelConfigurator.java b/src/main/java/com/rabbitmq/client/SocketChannelConfigurator.java index 5aded698f9..ceb3a95a88 100644 --- a/src/main/java/com/rabbitmq/client/SocketChannelConfigurator.java +++ b/src/main/java/com/rabbitmq/client/SocketChannelConfigurator.java @@ -17,7 +17,9 @@ import java.io.IOException; import java.nio.channels.SocketChannel; +import java.util.Objects; +@FunctionalInterface public interface SocketChannelConfigurator { /** @@ -26,4 +28,18 @@ public interface SocketChannelConfigurator { */ void configure(SocketChannel socketChannel) throws IOException; + /** + * Returns a composed configurator that performs, in sequence, this + * operation followed by the {@code after} operation. + * + * @param after the operation to perform after this operation + * @return a composed configurator that performs in sequence this + * operation followed by the {@code after} operation + * @throws NullPointerException if {@code after} is null + */ + default SocketChannelConfigurator andThen(SocketChannelConfigurator after) { + Objects.requireNonNull(after); + return t -> { configure(t); after.configure(t); }; + } + } diff --git a/src/main/java/com/rabbitmq/client/SocketChannelConfigurators.java b/src/main/java/com/rabbitmq/client/SocketChannelConfigurators.java new file mode 100644 index 0000000000..d6af09d7de --- /dev/null +++ b/src/main/java/com/rabbitmq/client/SocketChannelConfigurators.java @@ -0,0 +1,111 @@ +// Copyright (c) 2018 Pivotal Software, Inc. All rights reserved. +// +// This software, the RabbitMQ Java client library, is triple-licensed under the +// Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2 +// ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see +// LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, +// please see LICENSE-APACHE2. +// +// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, +// either express or implied. See the LICENSE file for specific language governing +// rights and limitations of this software. +// +// If you have any questions regarding licensing, please contact us at +// info@rabbitmq.com. + +package com.rabbitmq.client; + +/** + * Ready-to-use instances and builder for {@link SocketChannelConfigurator}. + *

+ * Note {@link SocketChannelConfigurator}s can be combined with + * {@link SocketChannelConfigurator#andThen(SocketChannelConfigurator)}. + * + * @since 5.5.0 + */ +public abstract class SocketChannelConfigurators { + + /** + * Disable Nagle's algorithm. + */ + public static final SocketChannelConfigurator DISABLE_NAGLE_ALGORITHM = + socketChannel -> SocketConfigurators.DISABLE_NAGLE_ALGORITHM.configure(socketChannel.socket()); + + /** + * Default {@link SocketChannelConfigurator} that disables Nagle's algorithm. + */ + public static final SocketChannelConfigurator DEFAULT = DISABLE_NAGLE_ALGORITHM; + + /** + * The default {@link SocketChannelConfigurator} that disables Nagle's algorithm. + * + * @return + */ + public static SocketChannelConfigurator defaultConfigurator() { + return DEFAULT; + } + + /** + * {@link SocketChannelConfigurator} that disables Nagle's algorithm. + * + * @return + */ + public static SocketChannelConfigurator disableNagleAlgorithm() { + return DISABLE_NAGLE_ALGORITHM; + } + + /** + * Builder to configure and creates a {@link SocketChannelConfigurator} instance. + * + * @return + */ + public static SocketChannelConfigurators.Builder builder() { + return new SocketChannelConfigurators.Builder(); + } + + public static class Builder { + + private SocketChannelConfigurator configurator = channel -> { + }; + + /** + * Set default configuration. + * + * @return + */ + public Builder defaultConfigurator() { + configurator = configurator.andThen(DEFAULT); + return this; + } + + /** + * Disable Nagle's Algorithm. + * + * @return + */ + public Builder disableNagleAlgorithm() { + configurator = configurator.andThen(DISABLE_NAGLE_ALGORITHM); + return this; + } + + /** + * Add an extra configuration step. + * + * @param extraConfiguration + * @return + */ + public Builder add(SocketChannelConfigurator extraConfiguration) { + configurator = configurator.andThen(extraConfiguration); + return this; + } + + /** + * Return the configured {@link SocketConfigurator}. + * + * @return + */ + public SocketChannelConfigurator build() { + return configurator; + } + } +} diff --git a/src/main/java/com/rabbitmq/client/SocketConfigurator.java b/src/main/java/com/rabbitmq/client/SocketConfigurator.java index 8896baf3e5..30c9ed4bab 100644 --- a/src/main/java/com/rabbitmq/client/SocketConfigurator.java +++ b/src/main/java/com/rabbitmq/client/SocketConfigurator.java @@ -17,11 +17,31 @@ import java.io.IOException; import java.net.Socket; +import java.util.Objects; +@FunctionalInterface public interface SocketConfigurator { + /** * Provides a hook to insert custom configuration of the sockets * used to connect to an AMQP server before they connect. */ void configure(Socket socket) throws IOException; + + /** + * Returns a composed configurator that performs, in sequence, this + * operation followed by the {@code after} operation. + * + * @param after the operation to perform after this operation + * @return a composed configurator that performs in sequence this + * operation followed by the {@code after} operation + * @throws NullPointerException if {@code after} is null + */ + default SocketConfigurator andThen(SocketConfigurator after) { + Objects.requireNonNull(after); + return t -> { + configure(t); + after.configure(t); + }; + } } diff --git a/src/main/java/com/rabbitmq/client/SocketConfigurators.java b/src/main/java/com/rabbitmq/client/SocketConfigurators.java new file mode 100644 index 0000000000..127bddeb34 --- /dev/null +++ b/src/main/java/com/rabbitmq/client/SocketConfigurators.java @@ -0,0 +1,153 @@ +// Copyright (c) 2018 Pivotal Software, Inc. All rights reserved. +// +// This software, the RabbitMQ Java client library, is triple-licensed under the +// Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2 +// ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see +// LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, +// please see LICENSE-APACHE2. +// +// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, +// either express or implied. See the LICENSE file for specific language governing +// rights and limitations of this software. +// +// If you have any questions regarding licensing, please contact us at +// info@rabbitmq.com. + +package com.rabbitmq.client; + +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocket; + +/** + * Ready-to-use instances and builder for {@link SocketConfigurator}. + *

+ * Note {@link SocketConfigurator}s can be combined with + * {@link SocketConfigurator#andThen(SocketConfigurator)}. + * + * @since 5.5.0 + */ +public abstract class SocketConfigurators { + + /** + * Disable Nagle's algorithm. + */ + public static final SocketConfigurator DISABLE_NAGLE_ALGORITHM = socket -> socket.setTcpNoDelay(true); + + /** + * Default {@link SocketConfigurator} that disables Nagle's algorithm. + */ + public static final SocketConfigurator DEFAULT = DISABLE_NAGLE_ALGORITHM; + + /** + * Enable server hostname validation for TLS connections. + */ + public static final SocketConfigurator ENABLE_HOSTNAME_VERIFICATION = socket -> { + if (socket instanceof SSLSocket) { + SSLSocket sslSocket = (SSLSocket) socket; + SSLParameters sslParameters = enableHostnameVerification(sslSocket.getSSLParameters()); + sslSocket.setSSLParameters(sslParameters); + } + }; + + static final SSLParameters enableHostnameVerification(SSLParameters sslParameters) { + if (sslParameters == null) { + sslParameters = new SSLParameters(); + } + // It says HTTPS but works also for any TCP connection. + // It checks SAN (Subject Alternative Name) as well as CN. + sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); + return sslParameters; + } + + /** + * The default {@link SocketConfigurator} that disables Nagle's algorithm. + * + * @return + */ + public static SocketConfigurator defaultConfigurator() { + return DEFAULT; + } + + /** + * {@link SocketConfigurator} that disables Nagle's algorithm. + * + * @return + */ + public static SocketConfigurator disableNagleAlgorithm() { + return DISABLE_NAGLE_ALGORITHM; + } + + /** + * {@link SocketConfigurator} that enable server hostname verification for TLS connections. + * + * @return + */ + public static SocketConfigurator enableHostnameVerification() { + return ENABLE_HOSTNAME_VERIFICATION; + } + + /** + * Builder to configure and creates a {@link SocketConfigurator} instance. + * + * @return + */ + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private SocketConfigurator configurator = socket -> { + }; + + /** + * Set default configuration. + * + * @return + */ + public Builder defaultConfigurator() { + configurator = configurator.andThen(DEFAULT); + return this; + } + + /** + * Disable Nagle's Algorithm. + * + * @return + */ + public Builder disableNagleAlgorithm() { + configurator = configurator.andThen(DISABLE_NAGLE_ALGORITHM); + return this; + } + + /** + * Enable server hostname verification for TLS connections. + * + * @return + */ + public Builder enableHostnameVerification() { + configurator = configurator.andThen(ENABLE_HOSTNAME_VERIFICATION); + return this; + } + + /** + * Add an extra configuration step. + * + * @param extraConfiguration + * @return + */ + public Builder add(SocketConfigurator extraConfiguration) { + configurator = configurator.andThen(extraConfiguration); + return this; + } + + /** + * Return the configured {@link SocketConfigurator}. + * + * @return + */ + public SocketConfigurator build() { + return configurator; + } + } +} diff --git a/src/main/java/com/rabbitmq/client/SslEngineConfigurator.java b/src/main/java/com/rabbitmq/client/SslEngineConfigurator.java index 7986d4b9d2..01b22c4c05 100644 --- a/src/main/java/com/rabbitmq/client/SslEngineConfigurator.java +++ b/src/main/java/com/rabbitmq/client/SslEngineConfigurator.java @@ -17,7 +17,9 @@ import javax.net.ssl.SSLEngine; import java.io.IOException; +import java.util.Objects; +@FunctionalInterface public interface SslEngineConfigurator { /** @@ -27,4 +29,18 @@ public interface SslEngineConfigurator { */ void configure(SSLEngine sslEngine) throws IOException; + /** + * Returns a composed configurator that performs, in sequence, this + * operation followed by the {@code after} operation. + * + * @param after the operation to perform after this operation + * @return a composed configurator that performs in sequence this + * operation followed by the {@code after} operation + * @throws NullPointerException if {@code after} is null + */ + default SslEngineConfigurator andThen(SslEngineConfigurator after) { + Objects.requireNonNull(after); + return t -> { configure(t); after.configure(t); }; + } + } diff --git a/src/main/java/com/rabbitmq/client/SslEngineConfigurators.java b/src/main/java/com/rabbitmq/client/SslEngineConfigurators.java new file mode 100644 index 0000000000..d84dfa304f --- /dev/null +++ b/src/main/java/com/rabbitmq/client/SslEngineConfigurators.java @@ -0,0 +1,116 @@ +// Copyright (c) 2018 Pivotal Software, Inc. All rights reserved. +// +// This software, the RabbitMQ Java client library, is triple-licensed under the +// Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2 +// ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see +// LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, +// please see LICENSE-APACHE2. +// +// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, +// either express or implied. See the LICENSE file for specific language governing +// rights and limitations of this software. +// +// If you have any questions regarding licensing, please contact us at +// info@rabbitmq.com. + +package com.rabbitmq.client; + +import javax.net.ssl.SSLParameters; + +/** + * Ready-to-use instances and builder for {@link SslEngineConfigurator}s. + *

+ * Note {@link SslEngineConfigurator}s can be combined with + * {@link SslEngineConfigurator#andThen(SslEngineConfigurator)}. + * + * @since 5.5.0 + */ +public abstract class SslEngineConfigurators { + + /** + * Default {@link SslEngineConfigurator}, does nothing. + */ + public static SslEngineConfigurator DEFAULT = sslEngine -> { + }; + + /** + * {@link SslEngineConfigurator} that enables server hostname verification. + */ + public static SslEngineConfigurator ENABLE_HOSTNAME_VERIFICATION = sslEngine -> { + SSLParameters sslParameters = SocketConfigurators.enableHostnameVerification(sslEngine.getSSLParameters()); + sslEngine.setSSLParameters(sslParameters); + }; + + /** + * Default {@link SslEngineConfigurator}, does nothing. + * + * @return + */ + public static SslEngineConfigurator defaultConfigurator() { + return DEFAULT; + } + + /** + * {@link SslEngineConfigurator} that enables server hostname verification. + * + * @return + */ + public static SslEngineConfigurator enableHostnameVerification() { + return ENABLE_HOSTNAME_VERIFICATION; + } + + /** + * Builder to configure and creates a {@link SslEngineConfigurator} instance. + * + * @return + */ + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private SslEngineConfigurator configurator = channel -> { + }; + + /** + * Set default configuration (no op). + * + * @return + */ + public Builder defaultConfigurator() { + configurator = configurator.andThen(DEFAULT); + return this; + } + + /** + * Enables server hostname verification. + * + * @return + */ + public Builder enableHostnameVerification() { + configurator = configurator.andThen(ENABLE_HOSTNAME_VERIFICATION); + return this; + } + + /** + * Add extra configuration step. + * + * @param extraConfiguration + * @return + */ + public Builder add(SslEngineConfigurator extraConfiguration) { + configurator = configurator.andThen(extraConfiguration); + return this; + } + + /** + * Return the configured {@link SslEngineConfigurator}. + * + * @return + */ + public SslEngineConfigurator build() { + return configurator; + } + } +} diff --git a/src/main/java/com/rabbitmq/client/impl/nio/NioParams.java b/src/main/java/com/rabbitmq/client/impl/nio/NioParams.java index 9f9da61795..80b8624447 100644 --- a/src/main/java/com/rabbitmq/client/impl/nio/NioParams.java +++ b/src/main/java/com/rabbitmq/client/impl/nio/NioParams.java @@ -15,14 +15,16 @@ package com.rabbitmq.client.impl.nio; -import com.rabbitmq.client.DefaultSocketChannelConfigurator; import com.rabbitmq.client.SocketChannelConfigurator; +import com.rabbitmq.client.SocketChannelConfigurators; import com.rabbitmq.client.SslEngineConfigurator; import javax.net.ssl.SSLEngine; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadFactory; +import static com.rabbitmq.client.SslEngineConfigurators.ENABLE_HOSTNAME_VERIFICATION; + /** * Parameters used to configure the NIO mode of a {@link com.rabbitmq.client.ConnectionFactory}. * @@ -68,7 +70,7 @@ public class NioParams { /** * the hook to configure the socket channel before it's open */ - private SocketChannelConfigurator socketChannelConfigurator = new DefaultSocketChannelConfigurator(); + private SocketChannelConfigurator socketChannelConfigurator = SocketChannelConfigurators.defaultConfigurator(); /** * the hook to configure the SSL engine before the connection is open @@ -94,10 +96,27 @@ public NioParams(NioParams nioParams) { setWriteQueueCapacity(nioParams.getWriteQueueCapacity()); setNioExecutor(nioParams.getNioExecutor()); setThreadFactory(nioParams.getThreadFactory()); + setSocketChannelConfigurator(nioParams.getSocketChannelConfigurator()); setSslEngineConfigurator(nioParams.getSslEngineConfigurator()); setConnectionShutdownExecutor(nioParams.getConnectionShutdownExecutor()); } + /** + * Enable server hostname verification for TLS connections. + * + * @return this {@link NioParams} instance + * @see NioParams#setSslEngineConfigurator(SslEngineConfigurator) + * @see com.rabbitmq.client.SslEngineConfigurators#ENABLE_HOSTNAME_VERIFICATION + */ + public NioParams enableHostnameVerification() { + if (this.sslEngineConfigurator == null) { + this.sslEngineConfigurator = ENABLE_HOSTNAME_VERIFICATION; + } else { + this.sslEngineConfigurator = this.sslEngineConfigurator.andThen(ENABLE_HOSTNAME_VERIFICATION); + } + return this; + } + public int getReadByteBufferSize() { return readByteBufferSize; } diff --git a/src/test/java/com/rabbitmq/client/test/ChannelRpcTimeoutIntegrationTest.java b/src/test/java/com/rabbitmq/client/test/ChannelRpcTimeoutIntegrationTest.java index 40d6a0ab5a..f880f617da 100644 --- a/src/test/java/com/rabbitmq/client/test/ChannelRpcTimeoutIntegrationTest.java +++ b/src/test/java/com/rabbitmq/client/test/ChannelRpcTimeoutIntegrationTest.java @@ -84,7 +84,7 @@ public void tearDown() throws Exception { private FrameHandler createFrameHandler() throws IOException { SocketFrameHandlerFactory socketFrameHandlerFactory = new SocketFrameHandlerFactory(ConnectionFactory.DEFAULT_CONNECTION_TIMEOUT, - SocketFactory.getDefault(), new DefaultSocketConfigurator(), false, null); + SocketFactory.getDefault(), SocketConfigurators.defaultConfigurator(), false, null); return socketFrameHandlerFactory.create(new Address("localhost"), null); } diff --git a/src/test/java/com/rabbitmq/client/test/functional/UnexpectedFrames.java b/src/test/java/com/rabbitmq/client/test/functional/UnexpectedFrames.java index 0ab8013969..698d8a8952 100644 --- a/src/test/java/com/rabbitmq/client/test/functional/UnexpectedFrames.java +++ b/src/test/java/com/rabbitmq/client/test/functional/UnexpectedFrames.java @@ -18,6 +18,7 @@ import com.rabbitmq.client.AMQP; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.DefaultSocketConfigurator; +import com.rabbitmq.client.SocketConfigurators; import com.rabbitmq.client.impl.*; import com.rabbitmq.client.impl.recovery.AutorecoveringConnection; import com.rabbitmq.client.test.BrokerTestCase; @@ -85,7 +86,7 @@ public ConfusedConnectionFactory() { private static class ConfusedFrameHandlerFactory extends SocketFrameHandlerFactory { private ConfusedFrameHandlerFactory() { - super(1000, SocketFactory.getDefault(), new DefaultSocketConfigurator(), false); + super(1000, SocketFactory.getDefault(), SocketConfigurators.defaultConfigurator(), false); } @Override public FrameHandler create(Socket sock) throws IOException { diff --git a/src/test/java/com/rabbitmq/client/test/ssl/HostnameVerification.java b/src/test/java/com/rabbitmq/client/test/ssl/HostnameVerification.java new file mode 100644 index 0000000000..85f6f6735e --- /dev/null +++ b/src/test/java/com/rabbitmq/client/test/ssl/HostnameVerification.java @@ -0,0 +1,104 @@ +// Copyright (c) 2007-Present Pivotal Software, Inc. All rights reserved. +// +// This software, the RabbitMQ Java client library, is triple-licensed under the +// Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2 +// ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see +// LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, +// please see LICENSE-APACHE2. +// +// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, +// either express or implied. See the LICENSE file for specific language governing +// rights and limitations of this software. +// +// If you have any questions regarding licensing, please contact us at +// info@rabbitmq.com. + +package com.rabbitmq.client.test.ssl; + +import com.rabbitmq.client.Address; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.test.TestUtils; +import org.junit.Test; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.TrustManagerFactory; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.concurrent.TimeoutException; + +import static java.util.Collections.singletonList; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +public class HostnameVerification extends UnverifiedConnection { + + public void openConnection() + throws IOException, TimeoutException { + try { + String keystorePath = System.getProperty("test-keystore.ca"); + assertNotNull(keystorePath); + String keystorePasswd = System.getProperty("test-keystore.password"); + assertNotNull(keystorePasswd); + char[] keystorePassword = keystorePasswd.toCharArray(); + + KeyStore tks = KeyStore.getInstance("JKS"); + tks.load(new FileInputStream(keystorePath), keystorePassword); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(tks); + + String p12Path = System.getProperty("test-client-cert.path"); + assertNotNull(p12Path); + String p12Passwd = System.getProperty("test-client-cert.password"); + assertNotNull(p12Passwd); + KeyStore ks = KeyStore.getInstance("PKCS12"); + char[] p12Password = p12Passwd.toCharArray(); + ks.load(new FileInputStream(p12Path), p12Password); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, p12Password); + + SSLContext c = getSSLContext(); + c.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + c.init(null, tmf.getTrustManagers(), null); + + connectionFactory = TestUtils.connectionFactory(); + connectionFactory.useSslProtocol(c); + connectionFactory.enableHostnameVerification(); + } catch (NoSuchAlgorithmException ex) { + throw new IOException(ex.toString()); + } catch (KeyManagementException ex) { + throw new IOException(ex.toString()); + } catch (KeyStoreException ex) { + throw new IOException(ex.toString()); + } catch (CertificateException ex) { + throw new IOException(ex.toString()); + } catch (UnrecoverableKeyException ex) { + throw new IOException(ex.toString()); + } + + try { + connection = connectionFactory.newConnection( + () -> singletonList(new Address("127.0.0.1", ConnectionFactory.DEFAULT_AMQP_OVER_SSL_PORT))); + fail("The server certificate isn't issued for 127.0.0.1, the TLS handshake should have failed"); + } catch (SSLHandshakeException ignored) { + } catch (IOException e) { + fail(); + } + } + + public void openChannel() { + } + + @Test + public void sSL() { + } +} diff --git a/src/test/java/com/rabbitmq/client/test/ssl/NioTlsUnverifiedConnection.java b/src/test/java/com/rabbitmq/client/test/ssl/NioTlsUnverifiedConnection.java index 7312658894..07ac30a901 100644 --- a/src/test/java/com/rabbitmq/client/test/ssl/NioTlsUnverifiedConnection.java +++ b/src/test/java/com/rabbitmq/client/test/ssl/NioTlsUnverifiedConnection.java @@ -82,12 +82,7 @@ public void connectionGetConsume() throws Exception { connectionFactory.useSslProtocol(); NioParams nioParams = new NioParams(); final AtomicBoolean sslEngineHasBeenCalled = new AtomicBoolean(false); - nioParams.setSslEngineConfigurator(new SslEngineConfigurator() { - @Override - public void configure(SSLEngine sslEngine) throws IOException { - sslEngineHasBeenCalled.set(true); - } - }); + nioParams.setSslEngineConfigurator(sslEngine -> sslEngineHasBeenCalled.set(true)); connectionFactory.setNioParams(nioParams); diff --git a/src/test/java/com/rabbitmq/client/test/ssl/SSLTests.java b/src/test/java/com/rabbitmq/client/test/ssl/SSLTests.java index 21946066e6..679468a59f 100644 --- a/src/test/java/com/rabbitmq/client/test/ssl/SSLTests.java +++ b/src/test/java/com/rabbitmq/client/test/ssl/SSLTests.java @@ -32,7 +32,8 @@ VerifiedConnection.class, BadVerifiedConnection.class, ConnectionFactoryDefaultTlsVersion.class, - NioTlsUnverifiedConnection.class + NioTlsUnverifiedConnection.class, + HostnameVerification.class }) public class SSLTests { diff --git a/src/test/java/com/rabbitmq/client/test/ssl/UnverifiedConnection.java b/src/test/java/com/rabbitmq/client/test/ssl/UnverifiedConnection.java index 319ef82de9..6d65599b4c 100644 --- a/src/test/java/com/rabbitmq/client/test/ssl/UnverifiedConnection.java +++ b/src/test/java/com/rabbitmq/client/test/ssl/UnverifiedConnection.java @@ -54,7 +54,7 @@ public void openConnection() } } if(connection == null) { - fail("Couldn't open TLS connection after 3 attemps"); + fail("Couldn't open TLS connection after 3 attempts"); } } diff --git a/src/test/java/com/rabbitmq/client/test/ssl/VerifiedConnection.java b/src/test/java/com/rabbitmq/client/test/ssl/VerifiedConnection.java index 3083e6e2f2..e662113085 100644 --- a/src/test/java/com/rabbitmq/client/test/ssl/VerifiedConnection.java +++ b/src/test/java/com/rabbitmq/client/test/ssl/VerifiedConnection.java @@ -96,7 +96,7 @@ public void openConnection() } } if(connection == null) { - fail("Couldn't open TLS connection after 3 attemps"); + fail("Couldn't open TLS connection after 3 attempts"); } } }