diff --git a/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java b/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
index a066652d6548e86823ccf270aba8aa59dba9b73c..e768c7f95eb4cd803c869a755bf44665d3c0a429 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
@@ -32,10 +32,11 @@ import org.apache.rocketmq.common.MQVersion;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.remoting.common.RemotingUtil;
-import org.apache.rocketmq.remoting.common.SslMode;
+import org.apache.rocketmq.remoting.common.TlsMode;
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
import org.apache.rocketmq.remoting.netty.NettyServerConfig;
import org.apache.rocketmq.remoting.netty.NettySystemConfig;
+import org.apache.rocketmq.remoting.netty.TlsSystemConfig;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.store.config.BrokerRole;
@@ -43,6 +44,8 @@ import org.apache.rocketmq.store.config.MessageStoreConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_ENABLE;
+
public class BrokerStartup {
public static Properties properties = null;
public static CommandLine commandLine = null;
@@ -98,7 +101,9 @@ public class BrokerStartup {
final BrokerConfig brokerConfig = new BrokerConfig();
final NettyServerConfig nettyServerConfig = new NettyServerConfig();
final NettyClientConfig nettyClientConfig = new NettyClientConfig();
- nettyClientConfig.setUseTLS(NettySystemConfig.sslMode == SslMode.ENFORCING);
+
+ nettyClientConfig.setUseTLS(Boolean.parseBoolean(System.getProperty(TLS_ENABLE,
+ String.valueOf(TlsSystemConfig.tlsMode == TlsMode.ENFORCING))));
nettyServerConfig.setListenPort(10911);
final MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
diff --git a/client/src/main/java/org/apache/rocketmq/client/ClientConfig.java b/client/src/main/java/org/apache/rocketmq/client/ClientConfig.java
index 8f255f012b13e5a67be48b368ff53bb144e50d81..a9eabfe6313015ba28e96f214f958eaa406ad229 100644
--- a/client/src/main/java/org/apache/rocketmq/client/ClientConfig.java
+++ b/client/src/main/java/org/apache/rocketmq/client/ClientConfig.java
@@ -19,6 +19,7 @@ package org.apache.rocketmq.client;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.remoting.common.RemotingUtil;
+import org.apache.rocketmq.remoting.netty.TlsSystemConfig;
/**
* Client Common configuration
@@ -45,7 +46,7 @@ public class ClientConfig {
private String unitName;
private boolean vipChannelEnabled = Boolean.parseBoolean(System.getProperty(SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY, "true"));
- private boolean useTLS;
+ private boolean useTLS = TlsSystemConfig.tlsEnable;
public String buildMQClientId() {
StringBuilder sb = new StringBuilder();
diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/common/SslMode.java b/remoting/src/main/java/org/apache/rocketmq/remoting/common/TlsMode.java
similarity index 87%
rename from remoting/src/main/java/org/apache/rocketmq/remoting/common/SslMode.java
rename to remoting/src/main/java/org/apache/rocketmq/remoting/common/TlsMode.java
index cb1e85a2b88bdbf8f8736230394d217cdaedf4d7..996ef0dd262a313f0f16be488299bceebd383ab9 100644
--- a/remoting/src/main/java/org/apache/rocketmq/remoting/common/SslMode.java
+++ b/remoting/src/main/java/org/apache/rocketmq/remoting/common/TlsMode.java
@@ -25,7 +25,7 @@ package org.apache.rocketmq.remoting.common;
*
enforcing: SSL is required, aka, non SSL connection will be rejected.
*
*/
-public enum SslMode {
+public enum TlsMode {
DISABLED("disabled"),
PERMISSIVE("permissive"),
@@ -33,14 +33,14 @@ public enum SslMode {
private String name;
- SslMode(String name) {
+ TlsMode(String name) {
this.name = name;
}
- public static SslMode parse(String mode) {
- for (SslMode sslMode: SslMode.values()) {
- if (sslMode.name.equals(mode)) {
- return sslMode;
+ public static TlsMode parse(String mode) {
+ for (TlsMode tlsMode : TlsMode.values()) {
+ if (tlsMode.name.equals(mode)) {
+ return tlsMode;
}
}
diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingClient.java b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingClient.java
index 6dc0457e4145c912eab6377ba4095824c24d0f2d..dcc80cba050a2c0d200c900dac65ccba27bb7771 100644
--- a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingClient.java
+++ b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingClient.java
@@ -34,6 +34,7 @@ import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
+import java.io.IOException;
import java.net.SocketAddress;
import java.security.cert.CertificateException;
import java.util.Collections;
@@ -52,7 +53,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
-import javax.net.ssl.SSLException;
import org.apache.rocketmq.remoting.ChannelEventListener;
import org.apache.rocketmq.remoting.InvokeCallback;
import org.apache.rocketmq.remoting.RPCHook;
@@ -131,9 +131,9 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
if (nettyClientConfig.isUseTLS()) {
try {
- sslContext = SslHelper.buildSslContext(true);
+ sslContext = TlsHelper.buildSslContext(true);
log.info("SSL enabled for client");
- } catch (SSLException e) {
+ } catch (IOException e) {
log.error("Failed to create SSLContext", e);
} catch (CertificateException e) {
log.error("Failed to create SSLContext", e);
diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingServer.java b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingServer.java
index ec1927a699f4406c07a765c5656a6231e0551827..cd6ed4704f48a36413f425a4f54b2561ba8ef01d 100644
--- a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingServer.java
+++ b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingServer.java
@@ -37,6 +37,7 @@ import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
+import java.io.IOException;
import java.net.InetSocketAddress;
import java.security.cert.CertificateException;
import java.util.NoSuchElementException;
@@ -46,7 +47,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
-import javax.net.ssl.SSLException;
import org.apache.rocketmq.remoting.ChannelEventListener;
import org.apache.rocketmq.remoting.InvokeCallback;
import org.apache.rocketmq.remoting.RPCHook;
@@ -54,7 +54,7 @@ import org.apache.rocketmq.remoting.RemotingServer;
import org.apache.rocketmq.remoting.common.Pair;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.common.RemotingUtil;
-import org.apache.rocketmq.remoting.common.SslMode;
+import org.apache.rocketmq.remoting.common.TlsMode;
import org.apache.rocketmq.remoting.exception.RemotingSendRequestException;
import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.remoting.exception.RemotingTooMuchRequestException;
@@ -139,16 +139,16 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
});
}
- SslMode sslMode = NettySystemConfig.sslMode;
- log.info("Server is running in TLS {} mode", sslMode.getName());
+ TlsMode tlsMode = TlsSystemConfig.tlsMode;
+ log.info("Server is running in TLS {} mode", tlsMode.getName());
- if (sslMode != SslMode.DISABLED) {
+ if (tlsMode != TlsMode.DISABLED) {
try {
- sslContext = SslHelper.buildSslContext(false);
+ sslContext = TlsHelper.buildSslContext(false);
log.info("SSLContext created for server");
} catch (CertificateException e) {
log.error("Failed to create SSLContext for server", e);
- } catch (SSLException e) {
+ } catch (IOException e) {
log.error("Failed to create SSLContext for server", e);
}
}
@@ -189,7 +189,7 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast(defaultEventExecutorGroup, HANDSHAKE_HANDLER_NAME,
- new HandshakeHandler(NettySystemConfig.sslMode))
+ new HandshakeHandler(TlsSystemConfig.tlsMode))
.addLast(defaultEventExecutorGroup,
new NettyEncoder(),
new NettyDecoder(),
@@ -326,12 +326,12 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
class HandshakeHandler extends SimpleChannelInboundHandler {
- private final SslMode sslMode;
+ private final TlsMode tlsMode;
private static final byte HANDSHAKE_MAGIC_CODE = 0x16;
- HandshakeHandler(SslMode sslMode) {
- this.sslMode = sslMode;
+ HandshakeHandler(TlsMode tlsMode) {
+ this.tlsMode = tlsMode;
}
@Override
@@ -344,7 +344,7 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
byte b = msg.getByte(0);
if (b == HANDSHAKE_MAGIC_CODE) {
- switch (sslMode) {
+ switch (tlsMode) {
case DISABLED:
ctx.close();
log.warn("Clients intend to establish a SSL connection while this server is running in SSL disabled mode");
@@ -366,7 +366,7 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
log.warn("Unknown TLS mode");
break;
}
- } else if (sslMode == SslMode.ENFORCING) {
+ } else if (tlsMode == TlsMode.ENFORCING) {
ctx.close();
log.warn("Clients intend to establish an insecure connection while this server is running in SSL enforcing mode");
}
diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettySystemConfig.java b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettySystemConfig.java
index b9c1f3fa3b2c0ce12286bb5030f4c7b7bad0a0e3..6357c03ba8b4203a83db3df16601d7358cf77728 100644
--- a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettySystemConfig.java
+++ b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettySystemConfig.java
@@ -17,8 +17,6 @@
package org.apache.rocketmq.remoting.netty;
-import org.apache.rocketmq.remoting.common.SslMode;
-
public class NettySystemConfig {
public static final String COM_ROCKETMQ_REMOTING_NETTY_POOLED_BYTE_BUF_ALLOCATOR_ENABLE =
"com.rocketmq.remoting.nettyPooledByteBufAllocatorEnable";
@@ -31,12 +29,6 @@ public class NettySystemConfig {
public static final String COM_ROCKETMQ_REMOTING_CLIENT_ONEWAY_SEMAPHORE_VALUE =
"com.rocketmq.remoting.clientOnewaySemaphoreValue";
- public static final String ORG_APACHE_ROCKETMQ_REMOTING_SSL_MODE = //
- "org.apache.rocketmq.remoting.ssl.mode";
-
- public static final String ORG_APACHE_ROCKETMQ_REMOTING_SSL_CONFIG_FILE = //
- "org.apache.rocketmq.remoting.ssl.config.file";
-
public static final boolean NETTY_POOLED_BYTE_BUF_ALLOCATOR_ENABLE = //
Boolean.parseBoolean(System.getProperty(COM_ROCKETMQ_REMOTING_NETTY_POOLED_BYTE_BUF_ALLOCATOR_ENABLE, "false"));
public static final int CLIENT_ASYNC_SEMAPHORE_VALUE = //
@@ -47,18 +39,4 @@ public class NettySystemConfig {
Integer.parseInt(System.getProperty(COM_ROCKETMQ_REMOTING_SOCKET_SNDBUF_SIZE, "65535"));
public static int socketRcvbufSize =
Integer.parseInt(System.getProperty(COM_ROCKETMQ_REMOTING_SOCKET_RCVBUF_SIZE, "65535"));
-
- /**
- * For server, three SSL modes are supported: disabled, permissive and enforcing.
- *
- * - disabled: SSL is not supported; any incoming SSL handshake will be rejected, causing connection closed.
- * - permissive: SSL is optional, aka, server in this mode can serve client connections with or without SSL;
- * - enforcing: SSL is required, aka, non SSL connection will be rejected.
- *
- */
- public static SslMode sslMode = //
- SslMode.parse(System.getProperty(ORG_APACHE_ROCKETMQ_REMOTING_SSL_MODE, "permissive"));
-
- public static String sslConfigFile = //
- System.getProperty(ORG_APACHE_ROCKETMQ_REMOTING_SSL_CONFIG_FILE, "/etc/rocketmq/ssl.properties");
}
diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/SslHelper.java b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/SslHelper.java
deleted file mode 100644
index ebadd9682525bcb897a8b2f8fd243b6833d608c9..0000000000000000000000000000000000000000
--- a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/SslHelper.java
+++ /dev/null
@@ -1,133 +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.rocketmq.remoting.netty;
-
-import io.netty.handler.ssl.ClientAuth;
-import io.netty.handler.ssl.OpenSsl;
-import io.netty.handler.ssl.SslContext;
-import io.netty.handler.ssl.SslContextBuilder;
-import io.netty.handler.ssl.SslProvider;
-import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
-import io.netty.handler.ssl.util.SelfSignedCertificate;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.cert.CertificateException;
-import java.util.Properties;
-import javax.net.ssl.SSLException;
-import org.apache.rocketmq.remoting.common.RemotingHelper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class SslHelper {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(RemotingHelper.ROCKETMQ_REMOTING);
-
- public static SslContext buildSslContext(boolean forClient) throws SSLException, CertificateException {
-
- File configFile = new File(NettySystemConfig.sslConfigFile);
- boolean testMode = !(configFile.exists() && configFile.isFile() && configFile.canRead());
- Properties properties = null;
-
- if (!testMode) {
- properties = new Properties();
- InputStream inputStream = null;
- try {
- inputStream = new FileInputStream(configFile);
- properties.load(inputStream);
- } catch (FileNotFoundException ignore) {
- } catch (IOException ignore) {
- } finally {
- if (null != inputStream) {
- try {
- inputStream.close();
- } catch (IOException ignore) {
- }
- }
- }
- }
-
- SslProvider provider = null;
- if (OpenSsl.isAvailable()) {
- provider = SslProvider.OPENSSL;
- LOGGER.info("Using OpenSSL provider");
- } else {
- provider = SslProvider.JDK;
- LOGGER.info("Using JDK SSL provider");
- }
-
- if (forClient) {
- if (testMode) {
- return SslContextBuilder
- .forClient()
- .sslProvider(SslProvider.JDK)
- .trustManager(InsecureTrustManagerFactory.INSTANCE)
- .build();
- } else {
- SslContextBuilder sslContextBuilder = SslContextBuilder.forClient().sslProvider(SslProvider.JDK);
-
- if ("false".equals(properties.getProperty("client.auth.server"))) {
- sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
- } else {
- if (properties.containsKey("client.trustManager")) {
- sslContextBuilder.trustManager(new File(properties.getProperty("client.trustManager")));
- }
- }
-
- return sslContextBuilder.keyManager(
- properties.containsKey("client.keyCertChainFile") ? new File(properties.getProperty("client.keyCertChainFile")) : null,
- properties.containsKey("client.keyFile") ? new File(properties.getProperty("client.keyFile")) : null,
- properties.containsKey("client.password") ? properties.getProperty("client.password") : null)
- .build();
- }
- } else {
-
- if (testMode) {
- SelfSignedCertificate selfSignedCertificate = new SelfSignedCertificate();
- return SslContextBuilder
- .forServer(selfSignedCertificate.certificate(), selfSignedCertificate.privateKey())
- .sslProvider(SslProvider.JDK)
- .clientAuth(ClientAuth.OPTIONAL)
- .build();
- } else {
- return SslContextBuilder.forServer(
- properties.containsKey("server.keyCertChainFile") ? new File(properties.getProperty("server.keyCertChainFile")) : null,
- properties.containsKey("server.keyFile") ? new File(properties.getProperty("server.keyFile")) : null,
- properties.containsKey("server.password") ? properties.getProperty("server.password") : null)
- .sslProvider(provider)
- .trustManager(new File(properties.getProperty("server.trustManager")))
- .clientAuth(parseClientAuthMode(properties.getProperty("server.auth.client")))
- .build();
- }
- }
- }
-
- private static ClientAuth parseClientAuthMode(String authMode) {
- if (null == authMode || authMode.trim().isEmpty()) {
- return ClientAuth.NONE;
- }
-
- if ("optional".equalsIgnoreCase(authMode)) {
- return ClientAuth.OPTIONAL;
- }
-
- return ClientAuth.REQUIRE;
- }
-}
diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/TlsHelper.java b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/TlsHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..3a74b4b67bc815c14b0b45dd67b38d5ba0d71c1d
--- /dev/null
+++ b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/TlsHelper.java
@@ -0,0 +1,234 @@
+/*
+ * 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.rocketmq.remoting.netty;
+
+import io.netty.handler.ssl.ClientAuth;
+import io.netty.handler.ssl.OpenSsl;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.SslProvider;
+import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
+import io.netty.handler.ssl.util.SelfSignedCertificate;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.CertificateException;
+import java.util.Properties;
+import org.apache.rocketmq.remoting.common.RemotingHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_CLIENT_AUTHSERVER;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_CLIENT_CERTPATH;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_CLIENT_KEYPASSWORD;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_CLIENT_KEYPATH;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_CLIENT_TRUSTCERTPATH;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_SERVER_AUTHCLIENT;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_SERVER_CERTPATH;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_SERVER_KEYPASSWORD;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_SERVER_KEYPATH;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_SERVER_NEED_CLIENT_AUTH;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_SERVER_TRUSTCERTPATH;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_TEST_MODE_ENABLE;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsClientAuthServer;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsClientCertPath;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsClientKeyPassword;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsClientKeyPath;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsClientTrustCertPath;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsServerAuthClient;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsServerCertPath;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsServerKeyPassword;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsServerKeyPath;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsServerNeedClientAuth;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsServerTrustCertPath;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsTestModeEnable;
+
+public class TlsHelper {
+
+ public interface DecryptionStrategy {
+ /**
+ * Decrypt the target encrpted private key file.
+ *
+ * @param privateKeyEncryptPath A pathname string
+ * @param forClient tells whether it's a client-side key file
+ * @return An input stream for a decrypted key file
+ * @throws IOException if an I/O error has occurred
+ */
+ InputStream decryptPrivateKey(String privateKeyEncryptPath, boolean forClient) throws IOException;
+ }
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(RemotingHelper.ROCKETMQ_REMOTING);
+
+ private static DecryptionStrategy decryptionStrategy = new DecryptionStrategy() {
+ @Override
+ public InputStream decryptPrivateKey(final String privateKeyEncryptPath,
+ final boolean forClient) throws IOException {
+ return new FileInputStream(privateKeyEncryptPath);
+ }
+ };
+
+
+ public static void registerDecryptionStrategy(final DecryptionStrategy decryptionStrategy) {
+ TlsHelper.decryptionStrategy = decryptionStrategy;
+ }
+
+ public static SslContext buildSslContext(boolean forClient) throws IOException, CertificateException {
+ File configFile = new File(TlsSystemConfig.tlsConfigFile);
+ extractTlsConfigFromFile(configFile);
+ logTheFinalUsedTlsConfig();
+
+ SslProvider provider;
+ if (OpenSsl.isAvailable()) {
+ provider = SslProvider.OPENSSL;
+ LOGGER.info("Using OpenSSL provider");
+ } else {
+ provider = SslProvider.JDK;
+ LOGGER.info("Using JDK SSL provider");
+ }
+
+ if (forClient) {
+ if (tlsTestModeEnable) {
+ return SslContextBuilder
+ .forClient()
+ .sslProvider(SslProvider.JDK)
+ .trustManager(InsecureTrustManagerFactory.INSTANCE)
+ .build();
+ } else {
+ SslContextBuilder sslContextBuilder = SslContextBuilder.forClient().sslProvider(SslProvider.JDK);
+
+
+ if (!tlsClientAuthServer) {
+ sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
+ } else {
+ if (!isNullOrEmpty(tlsClientTrustCertPath)) {
+ sslContextBuilder.trustManager(new File(tlsClientTrustCertPath));
+ }
+ }
+
+ return sslContextBuilder.keyManager(
+ !isNullOrEmpty(tlsClientCertPath) ? new FileInputStream(tlsClientCertPath) : null,
+ !isNullOrEmpty(tlsClientKeyPath) ? decryptionStrategy.decryptPrivateKey(tlsClientKeyPath, true) : null,
+ !isNullOrEmpty(tlsClientKeyPassword) ? tlsClientKeyPassword : null)
+ .build();
+ }
+ } else {
+
+ if (tlsTestModeEnable) {
+ SelfSignedCertificate selfSignedCertificate = new SelfSignedCertificate();
+ return SslContextBuilder
+ .forServer(selfSignedCertificate.certificate(), selfSignedCertificate.privateKey())
+ .sslProvider(SslProvider.JDK)
+ .clientAuth(ClientAuth.OPTIONAL)
+ .build();
+ } else {
+ SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(
+ !isNullOrEmpty(tlsServerCertPath) ? new FileInputStream(tlsServerCertPath) : null,
+ !isNullOrEmpty(tlsServerKeyPath) ? decryptionStrategy.decryptPrivateKey(tlsServerKeyPath, false) : null,
+ !isNullOrEmpty(tlsServerKeyPassword) ? tlsServerKeyPassword : null)
+ .sslProvider(provider);
+
+ if (!tlsServerAuthClient) {
+ sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
+ } else {
+ if (!isNullOrEmpty(tlsServerTrustCertPath)) {
+ sslContextBuilder.trustManager(new File(tlsServerTrustCertPath));
+ }
+ }
+
+ sslContextBuilder.clientAuth(parseClientAuthMode(tlsServerNeedClientAuth));
+ return sslContextBuilder.build();
+ }
+ }
+ }
+
+ private static void extractTlsConfigFromFile(final File configFile) {
+ if (!(configFile.exists() && configFile.isFile() && configFile.canRead())) {
+ LOGGER.info("Tls config file doesn't exist, skip it");
+ return;
+ }
+
+ Properties properties;
+ properties = new Properties();
+ InputStream inputStream = null;
+ try {
+ inputStream = new FileInputStream(configFile);
+ properties.load(inputStream);
+ } catch (IOException ignore) {
+ } finally {
+ if (null != inputStream) {
+ try {
+ inputStream.close();
+ } catch (IOException ignore) {
+ }
+ }
+ }
+
+ tlsTestModeEnable = Boolean.parseBoolean(properties.getProperty(TLS_TEST_MODE_ENABLE, String.valueOf(tlsTestModeEnable)));
+ tlsServerNeedClientAuth = properties.getProperty(TLS_SERVER_NEED_CLIENT_AUTH, tlsServerNeedClientAuth);
+ tlsServerKeyPath = properties.getProperty(TLS_SERVER_KEYPATH, tlsServerKeyPath);
+ tlsServerKeyPassword = properties.getProperty(TLS_SERVER_KEYPASSWORD, tlsServerKeyPassword);
+ tlsServerCertPath = properties.getProperty(TLS_SERVER_CERTPATH, tlsServerCertPath);
+ tlsServerAuthClient = Boolean.parseBoolean(properties.getProperty(TLS_SERVER_AUTHCLIENT, String.valueOf(tlsServerAuthClient)));
+ tlsServerTrustCertPath = properties.getProperty(TLS_SERVER_TRUSTCERTPATH, tlsServerTrustCertPath);
+
+ tlsClientKeyPath = properties.getProperty(TLS_CLIENT_KEYPATH, tlsClientKeyPath);
+ tlsClientKeyPassword = properties.getProperty(TLS_CLIENT_KEYPASSWORD, tlsClientKeyPassword);
+ tlsClientCertPath = properties.getProperty(TLS_CLIENT_CERTPATH, tlsClientCertPath);
+ tlsClientAuthServer = Boolean.parseBoolean(properties.getProperty(TLS_CLIENT_AUTHSERVER, String.valueOf(tlsClientAuthServer)));
+ tlsClientTrustCertPath = properties.getProperty(TLS_CLIENT_TRUSTCERTPATH, tlsClientTrustCertPath);
+ }
+
+ private static void logTheFinalUsedTlsConfig() {
+ LOGGER.info("Log the final used tls related configuration");
+ LOGGER.info("{} = {}", TLS_TEST_MODE_ENABLE, tlsTestModeEnable);
+ LOGGER.info("{} = {}", TLS_SERVER_NEED_CLIENT_AUTH, tlsServerNeedClientAuth);
+ LOGGER.info("{} = {}", TLS_SERVER_KEYPATH, tlsServerKeyPath);
+ LOGGER.info("{} = {}", TLS_SERVER_KEYPASSWORD, tlsServerKeyPassword);
+ LOGGER.info("{} = {}", TLS_SERVER_CERTPATH, tlsServerCertPath);
+ LOGGER.info("{} = {}", TLS_SERVER_AUTHCLIENT, tlsServerAuthClient);
+ LOGGER.info("{} = {}", TLS_SERVER_TRUSTCERTPATH, tlsServerTrustCertPath);
+
+ LOGGER.info("{} = {}", TLS_CLIENT_KEYPATH, tlsClientKeyPath);
+ LOGGER.info("{} = {}", TLS_CLIENT_KEYPASSWORD, tlsClientKeyPassword);
+ LOGGER.info("{} = {}", TLS_CLIENT_CERTPATH, tlsClientCertPath);
+ LOGGER.info("{} = {}", TLS_CLIENT_AUTHSERVER, tlsClientAuthServer);
+ LOGGER.info("{} = {}", TLS_CLIENT_TRUSTCERTPATH, tlsClientTrustCertPath);
+ }
+
+ private static ClientAuth parseClientAuthMode(String authMode) {
+ if (null == authMode || authMode.trim().isEmpty()) {
+ return ClientAuth.NONE;
+ }
+
+ for (ClientAuth clientAuth : ClientAuth.values()) {
+ if (clientAuth.name().equals(authMode.toUpperCase())) {
+ return clientAuth;
+ }
+ }
+
+ return ClientAuth.NONE;
+ }
+
+ /**
+ * Determine if a string is {@code null} or {@link String#isEmpty()} returns {@code true}.
+ */
+ private static boolean isNullOrEmpty(String s) {
+ return s == null || s.isEmpty();
+ }
+}
diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/TlsSystemConfig.java b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/TlsSystemConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..403bd6c9a83718be9b82d9ed4c3bdb51aabb86c6
--- /dev/null
+++ b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/TlsSystemConfig.java
@@ -0,0 +1,125 @@
+/*
+ * 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.rocketmq.remoting.netty;
+
+import io.netty.handler.ssl.SslContext;
+import org.apache.rocketmq.remoting.common.TlsMode;
+
+public class TlsSystemConfig {
+ public static final String TLS_SERVER_MODE = "tls.server.mode";
+ public static final String TLS_ENABLE = "tls.enable";
+ public static final String TLS_CONFIG_FILE = "tls.config.file";
+ public static final String TLS_TEST_MODE_ENABLE = "tls.test.mode.enable";
+
+ public static final String TLS_SERVER_NEED_CLIENT_AUTH = "tls.server.need.client.auth";
+ public static final String TLS_SERVER_KEYPATH = "tls.server.keyPath";
+ public static final String TLS_SERVER_KEYPASSWORD = "tls.server.keyPassword";
+ public static final String TLS_SERVER_CERTPATH = "tls.server.certPath";
+ public static final String TLS_SERVER_AUTHCLIENT = "tls.server.authClient";
+ public static final String TLS_SERVER_TRUSTCERTPATH = "tls.server.trustCertPath";
+
+ public static final String TLS_CLIENT_KEYPATH = "tls.client.keyPath";
+ public static final String TLS_CLIENT_KEYPASSWORD = "tls.client.keyPassword";
+ public static final String TLS_CLIENT_CERTPATH = "tls.client.certPath";
+ public static final String TLS_CLIENT_AUTHSERVER = "tls.client.authServer";
+ public static final String TLS_CLIENT_TRUSTCERTPATH = "tls.client.trustCertPath";
+
+
+ /**
+ * To determine whether use SSL in client-side, include SDK client and BrokerOuterAPI
+ */
+ public static boolean tlsEnable = Boolean.parseBoolean(System.getProperty(TLS_ENABLE, "false"));
+
+ /**
+ * To determine whether use test mode when initialize TLS context
+ */
+ public static boolean tlsTestModeEnable = Boolean.parseBoolean(System.getProperty(TLS_TEST_MODE_ENABLE, "true"));
+
+ /**
+ * Indicates the state of the {@link javax.net.ssl.SSLEngine} with respect to client authentication.
+ * This configuration item really only applies when building the server-side {@link SslContext},
+ * and can be set to none, require or optional.
+ */
+ public static String tlsServerNeedClientAuth = System.getProperty(TLS_SERVER_NEED_CLIENT_AUTH, "none");
+ /**
+ * The store path of server-side private key
+ */
+ public static String tlsServerKeyPath = System.getProperty(TLS_SERVER_KEYPATH, null);
+
+ /**
+ * The password of the server-side private key
+ */
+ public static String tlsServerKeyPassword = System.getProperty(TLS_SERVER_KEYPASSWORD, null);
+
+ /**
+ * The store path of server-side X.509 certificate chain in PEM format
+ */
+ public static String tlsServerCertPath = System.getProperty(TLS_SERVER_CERTPATH, null);
+
+ /**
+ * To determine whether verify the client endpoint's certificate strictly
+ */
+ public static boolean tlsServerAuthClient = Boolean.parseBoolean(System.getProperty(TLS_SERVER_AUTHCLIENT, "false"));
+
+ /**
+ * The store path of trusted certificates for verifying the client endpoint's certificate
+ */
+ public static String tlsServerTrustCertPath = System.getProperty(TLS_SERVER_TRUSTCERTPATH, null);
+
+ /**
+ * The store path of client-side private key
+ */
+ public static String tlsClientKeyPath = System.getProperty(TLS_CLIENT_KEYPATH, null);
+
+ /**
+ * The password of the client-side private key
+ */
+ public static String tlsClientKeyPassword = System.getProperty(TLS_CLIENT_KEYPASSWORD, null);
+
+ /**
+ * The store path of client-side X.509 certificate chain in PEM format
+ */
+ public static String tlsClientCertPath = System.getProperty(TLS_CLIENT_CERTPATH, null);
+
+ /**
+ * To determine whether verify the server endpoint's certificate strictly
+ */
+ public static boolean tlsClientAuthServer = Boolean.parseBoolean(System.getProperty(TLS_CLIENT_AUTHSERVER, "false"));
+
+ /**
+ * The store path of trusted certificates for verifying the server endpoint's certificate
+ */
+ public static String tlsClientTrustCertPath = System.getProperty(TLS_CLIENT_TRUSTCERTPATH, null);
+
+ /**
+ * For server, three SSL modes are supported: disabled, permissive and enforcing.
+ * For client, use {@link TlsSystemConfig#tlsEnable} to determine whether use SSL.
+ *
+ * - disabled: SSL is not supported; any incoming SSL handshake will be rejected, causing connection closed.
+ * - permissive: SSL is optional, aka, server in this mode can serve client connections with or without SSL;
+ * - enforcing: SSL is required, aka, non SSL connection will be rejected.
+ *
+ */
+ public static TlsMode tlsMode = TlsMode.parse(System.getProperty(TLS_SERVER_MODE, "permissive"));
+
+ /**
+ * A config file to store the above TLS related configurations,
+ * except {@link TlsSystemConfig#tlsMode} and {@link TlsSystemConfig#tlsEnable}
+ */
+ public static String tlsConfigFile = System.getProperty(TLS_CONFIG_FILE, "/etc/rocketmq/tls.properties");
+}
diff --git a/remoting/src/test/java/org/apache/rocketmq/remoting/RemotingServerTest.java b/remoting/src/test/java/org/apache/rocketmq/remoting/RemotingServerTest.java
index 31c26473e57f09af58a8f6db785f98192d52cc7a..0ecfaaa5aa007c661637b80e14a3a00ffb10aa06 100644
--- a/remoting/src/test/java/org/apache/rocketmq/remoting/RemotingServerTest.java
+++ b/remoting/src/test/java/org/apache/rocketmq/remoting/RemotingServerTest.java
@@ -67,8 +67,11 @@ public class RemotingServerTest {
}
public static RemotingClient createRemotingClient() {
- NettyClientConfig config = new NettyClientConfig();
- RemotingClient client = new NettyRemotingClient(config);
+ return createRemotingClient(new NettyClientConfig());
+ }
+
+ public static RemotingClient createRemotingClient(NettyClientConfig nettyClientConfig) {
+ RemotingClient client = new NettyRemotingClient(nettyClientConfig);
client.start();
return client;
}
diff --git a/remoting/src/test/java/org/apache/rocketmq/remoting/TlsTest.java b/remoting/src/test/java/org/apache/rocketmq/remoting/TlsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e516dd74bda5488afc01197eac0816a055983e7
--- /dev/null
+++ b/remoting/src/test/java/org/apache/rocketmq/remoting/TlsTest.java
@@ -0,0 +1,298 @@
+/*
+ * 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.rocketmq.remoting;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import org.apache.rocketmq.remoting.common.TlsMode;
+import org.apache.rocketmq.remoting.exception.RemotingSendRequestException;
+import org.apache.rocketmq.remoting.netty.NettyClientConfig;
+import org.apache.rocketmq.remoting.netty.TlsHelper;
+import org.apache.rocketmq.remoting.protocol.LanguageCode;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_CLIENT_AUTHSERVER;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_CLIENT_CERTPATH;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_CLIENT_KEYPASSWORD;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_CLIENT_KEYPATH;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_CLIENT_TRUSTCERTPATH;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_SERVER_AUTHCLIENT;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_SERVER_CERTPATH;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_SERVER_KEYPASSWORD;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_SERVER_KEYPATH;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_SERVER_NEED_CLIENT_AUTH;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.TLS_SERVER_TRUSTCERTPATH;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsClientAuthServer;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsClientCertPath;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsClientKeyPassword;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsClientKeyPath;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsClientTrustCertPath;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsConfigFile;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsMode;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsServerAuthClient;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsServerCertPath;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsServerKeyPassword;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsServerKeyPath;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsServerNeedClientAuth;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsServerTrustCertPath;
+import static org.apache.rocketmq.remoting.netty.TlsSystemConfig.tlsTestModeEnable;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(MockitoJUnitRunner.class)
+public class TlsTest {
+ private RemotingServer remotingServer;
+ private RemotingClient remotingClient;
+
+ @Rule
+ public TestName name = new TestName();
+
+ @Rule
+ public TemporaryFolder tempFolder = new TemporaryFolder();
+
+ @Before
+ public void setUp() throws InterruptedException {
+ tlsMode = TlsMode.ENFORCING;
+ tlsTestModeEnable = false;
+ tlsServerNeedClientAuth = "require";
+ tlsServerKeyPath = getCertsPath("server.key");
+ tlsServerCertPath = getCertsPath("server.pem");
+ tlsServerAuthClient = true;
+ tlsServerTrustCertPath = getCertsPath("ca.pem");
+ tlsClientKeyPath = getCertsPath("client.key");
+ tlsClientCertPath = getCertsPath("client.pem");
+ tlsClientAuthServer = true;
+ tlsClientTrustCertPath = getCertsPath("ca.pem");
+ tlsClientKeyPassword = "1234";
+ tlsServerKeyPassword = "";
+
+ NettyClientConfig clientConfig = new NettyClientConfig();
+ clientConfig.setUseTLS(true);
+
+ if ("serverRejectsUntrustedClientCert".equals(name.getMethodName())) {
+ // Create a client. Its credentials come from a CA that the server does not trust. The client
+ // trusts both test CAs to ensure the handshake failure is due to the server rejecting the client's cert.
+ tlsClientKeyPath = getCertsPath("badClient.key");
+ tlsClientCertPath = getCertsPath("badClient.pem");
+ } else if ("serverAcceptsUntrustedClientCert".equals(name.getMethodName())) {
+ tlsClientKeyPath = getCertsPath("badClient.key");
+ tlsClientCertPath = getCertsPath("badClient.pem");
+ tlsServerAuthClient = false;
+ }
+ else if ("noClientAuthFailure".equals(name.getMethodName())) {
+ //Clear the client cert config to ensure produce the handshake error
+ tlsClientKeyPath = "";
+ tlsClientCertPath = "";
+ } else if ("clientRejectsUntrustedServerCert".equals(name.getMethodName())) {
+ tlsServerKeyPath = getCertsPath("badServer.key");
+ tlsServerCertPath = getCertsPath("badServer.pem");
+ } else if ("clientAcceptsUntrustedServerCert".equals(name.getMethodName())) {
+ tlsServerKeyPath = getCertsPath("badServer.key");
+ tlsServerCertPath = getCertsPath("badServer.pem");
+ tlsClientAuthServer = false;
+ } else if ("serverNotNeedClientAuth".equals(name.getMethodName())) {
+ tlsServerNeedClientAuth = "none";
+ tlsClientKeyPath = "";
+ tlsClientCertPath = "";
+ } else if ("serverWantClientAuth".equals(name.getMethodName())) {
+ tlsServerNeedClientAuth = "optional";
+ } else if ("serverWantClientAuth_ButClientNoCert".equals(name.getMethodName())) {
+ tlsServerNeedClientAuth = "optional";
+ tlsClientKeyPath = "";
+ tlsClientCertPath = "";
+ } else if ("serverAcceptsUnAuthClient".equals(name.getMethodName())) {
+ tlsMode = TlsMode.PERMISSIVE;
+ tlsClientKeyPath = "";
+ tlsClientCertPath = "";
+ clientConfig.setUseTLS(false);
+ } else if ("serverRejectsSSLClient".equals(name.getMethodName())) {
+ tlsMode = TlsMode.DISABLED;
+ }
+
+ remotingServer = RemotingServerTest.createRemotingServer();
+ remotingClient = RemotingServerTest.createRemotingClient(clientConfig);
+ }
+
+ @After
+ public void tearDown() {
+ remotingClient.shutdown();
+ remotingServer.shutdown();
+ tlsMode = TlsMode.PERMISSIVE;
+ }
+
+ /**
+ * Tests that a client and a server configured using two-way SSL auth can successfully
+ * communicate with each other.
+ */
+ @Test
+ public void basicClientServerIntegrationTest() throws Exception {
+ requestThenAssertResponse();
+ }
+
+ @Test
+ public void serverNotNeedClientAuth() throws Exception {
+ requestThenAssertResponse();
+ }
+
+ @Test
+ public void serverWantClientAuth_ButClientNoCert() throws Exception {
+ requestThenAssertResponse();
+ }
+
+ @Test
+ public void serverAcceptsUnAuthClient() throws Exception {
+ requestThenAssertResponse();
+ }
+
+ @Test
+ public void serverRejectsSSLClient() throws Exception {
+ try {
+ RemotingCommand response = remotingClient.invokeSync("localhost:8888", createRequest(), 1000 * 5);
+ failBecauseExceptionWasNotThrown(RemotingSendRequestException.class);
+ } catch (RemotingSendRequestException ignore) {
+ }
+ }
+
+ /**
+ * Tests that a server configured to require client authentication refuses to accept connections
+ * from a client that has an untrusted certificate.
+ */
+ @Test
+ public void serverRejectsUntrustedClientCert() throws Exception {
+ try {
+ RemotingCommand response = remotingClient.invokeSync("localhost:8888", createRequest(), 1000 * 5);
+ failBecauseExceptionWasNotThrown(RemotingSendRequestException.class);
+ } catch (RemotingSendRequestException ignore) {
+ }
+ }
+
+ @Test
+ public void serverAcceptsUntrustedClientCert() throws Exception {
+ requestThenAssertResponse();
+ }
+
+ /**
+ * Tests that a server configured to require client authentication actually does require client
+ * authentication.
+ */
+ @Test
+ public void noClientAuthFailure() throws Exception {
+ try {
+ RemotingCommand response = remotingClient.invokeSync("localhost:8888", createRequest(), 1000 * 3);
+ failBecauseExceptionWasNotThrown(RemotingSendRequestException.class);
+ } catch (RemotingSendRequestException ignore) {
+ }
+ }
+
+ /**
+ * Tests that a client configured using GrpcSslContexts refuses to talk to a server that has an
+ * an untrusted certificate.
+ */
+ @Test
+ public void clientRejectsUntrustedServerCert() throws Exception {
+ try {
+ RemotingCommand response = remotingClient.invokeSync("localhost:8888", createRequest(), 1000 * 3);
+ failBecauseExceptionWasNotThrown(RemotingSendRequestException.class);
+ } catch (RemotingSendRequestException ignore) {
+ }
+ }
+
+ @Test
+ public void clientAcceptsUntrustedServerCert() throws Exception {
+ requestThenAssertResponse();
+ }
+
+ @Test
+ public void testTlsConfigThroughFile() throws Exception {
+ File file = tempFolder.newFile("tls.config");
+ tlsTestModeEnable = true;
+
+ tlsConfigFile = file.getAbsolutePath();
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(TLS_SERVER_NEED_CLIENT_AUTH + "=require\n");
+ sb.append(TLS_SERVER_KEYPATH + "=/server.key\n");
+ sb.append(TLS_SERVER_CERTPATH + "=/server.pem\n");
+ sb.append(TLS_SERVER_KEYPASSWORD + "=2345\n");
+ sb.append(TLS_SERVER_AUTHCLIENT + "=true\n");
+ sb.append(TLS_SERVER_TRUSTCERTPATH + "=/ca.pem\n");
+ sb.append(TLS_CLIENT_KEYPATH + "=/client.key\n");
+ sb.append(TLS_CLIENT_KEYPASSWORD + "=1234\n");
+ sb.append(TLS_CLIENT_CERTPATH + "=/client.pem\n");
+ sb.append(TLS_CLIENT_AUTHSERVER + "=false\n");
+ sb.append(TLS_CLIENT_TRUSTCERTPATH + "=/ca.pem\n");
+
+ writeStringToFile(file.getAbsolutePath(), sb.toString());
+ TlsHelper.buildSslContext(false);
+
+ assertThat(tlsServerNeedClientAuth).isEqualTo("require");
+ assertThat(tlsServerKeyPath).isEqualTo("/server.key");
+ assertThat(tlsServerCertPath).isEqualTo("/server.pem");
+ assertThat(tlsServerKeyPassword).isEqualTo("2345");
+ assertThat(tlsServerAuthClient).isEqualTo(true);
+ assertThat(tlsServerTrustCertPath).isEqualTo("/ca.pem");
+ assertThat(tlsClientKeyPath).isEqualTo("/client.key");
+ assertThat(tlsClientKeyPassword).isEqualTo("1234");
+ assertThat(tlsClientCertPath).isEqualTo("/client.pem");
+ assertThat(tlsClientAuthServer).isEqualTo(false);
+ assertThat(tlsClientTrustCertPath).isEqualTo("/ca.pem");
+
+ tlsConfigFile = "/notFound";
+ }
+
+ private static void writeStringToFile(String path, String content) {
+ try {
+ PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(path, true)));
+ out.println(content);
+ out.close();
+ } catch (IOException ignore) {
+ }
+ }
+
+ private static String getCertsPath(String fileName) {
+ File resourcesDirectory = new File("src/test/resources/certs");
+ return resourcesDirectory.getAbsolutePath() + "/" + fileName;
+ }
+
+ private static RemotingCommand createRequest() {
+ RequestHeader requestHeader = new RequestHeader();
+ requestHeader.setCount(1);
+ requestHeader.setMessageTitle("Welcome");
+ return RemotingCommand.createRequestCommand(0, requestHeader);
+ }
+
+ private void requestThenAssertResponse() throws Exception {
+ RemotingCommand response = remotingClient.invokeSync("localhost:8888", createRequest(), 1000 * 3);
+ assertTrue(response != null);
+ assertThat(response.getLanguage()).isEqualTo(LanguageCode.JAVA);
+ assertThat(response.getExtFields()).hasSize(2);
+ assertThat(response.getExtFields().get("messageTitle")).isEqualTo("Welcome");
+ }
+}
diff --git a/remoting/src/test/resources/certs/badClient.key b/remoting/src/test/resources/certs/badClient.key
new file mode 100644
index 0000000000000000000000000000000000000000..2dfd7ab19439c4408637f799292a6972211a45e8
--- /dev/null
+++ b/remoting/src/test/resources/certs/badClient.key
@@ -0,0 +1,17 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIICoTAbBgkqhkiG9w0BBQMwDgQIc2h7vaLYK6gCAggABIICgNrkvD1Xxez79Jgk
+WhRJg06CG8UthncfeuymR4hgp9HIneUzUHOoaf64mpxUbDWe3YOzA29REcBQsjF0
+Rpv+Uyg3cyDG14TmeRoSufOxB3MWLcIenoPPyNNtxe3XXmdkJTXX2YR0j7EOzH2v
+qlmuxmN4A7UonV5RdGxCz0sm7bU7EyZKdLO/DwBNxlX7ukcVLxAAqsc7ondclYj0
+SFJKk1nzfysCsk/Pq+q3PAVVpG6x5RFaLVS7Zt+gU6IEp+0S0eeYukkTjGh9PMPl
+wjCOcRiR3O+g4b3DevmW8TcoBqAZ2cFaf4lGhYlNBfa9PaQ3spJLL8l8xBbRIs8T
+3UnaFIa49r9DO/ZpCwpDeUE+URCx/SpcO6lchWQhdEuFt+DnFKOPYDSCHtHJSWHf
+9Z2bltjcYYPy/8nkPeqsO9vn4/r6jo+l7MYWKyWolLCW+7RYbpx5R2s4SBGtBP6w
+bwQOtOASbpG+mqTf7+ARpffHaZm9cKoKwobXigjDojPeaBCg5DgRuLIS1tO46Pjg
+USJ8sZilXifUwc6qRZ/2KiTSiJYCPMJD2ZTvK2Inkv2qzg6X3kw7CYCaW+iDL9zN
+e3ES7bps1wZ6D8cGq80WUQgrtpaGAXLzIv4FvM5yDoqrre/dh/XDO9l2hYfUmRVv
+rynKdSxjhhyHaK2ei8cX4LGEIlRNiu9ZIxSYeUAy37IJ0rVC7vtBWTh30JTeMRop
+iIPmygBMX2FEhQ2l/eS2lRhiybR0QXA4kCeJkVQas3aMMBGp2ThPNahLpzP82B7V
+f9137okQC95/KXRz/ZLYFsJtY/53206mG7gU/+dYsYI4slLAlnSe8k2sS0D9qkWJ
+VV9F7PM=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/remoting/src/test/resources/certs/badClient.pem b/remoting/src/test/resources/certs/badClient.pem
new file mode 100644
index 0000000000000000000000000000000000000000..1d264d33fe9e725a6eea50ef90b2dfe3eef2b81c
--- /dev/null
+++ b/remoting/src/test/resources/certs/badClient.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8zCCAdsCAQIwDQYJKoZIhvcNAQEFBQAwfDELMAkGA1UEBhMCemgxCzAJBgNV
+BAgMAnpqMQswCQYDVQQHDAJoejEPMA0GA1UECgwGYXBhY2hlMREwDwYDVQQLDAhy
+b2NrZXRtcTEOMAwGA1UEAwwFeXVrb24xHzAdBgkqhkiG9w0BCQEWEHl1a29uQGFw
+YWNoZS5vcmcwHhcNMTcxMjExMDk0NDExWhcNMTgwMTEwMDk0NDExWjCBhjELMAkG
+A1UEBhMCemgxCzAJBgNVBAgMAnpqMQswCQYDVQQHDAJoejEPMA0GA1UECgwGYXBh
+Y2hlMREwDwYDVQQLDAhyb2NrZXRtcTEWMBQGA1UEAwwNZm9vYmFyLmNsaWVudDEh
+MB8GCSqGSIb3DQEJARYSZm9vQGJhci5jbGllbnQuY29tMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQC+3bvrKGF1Y9/kN5UBtf8bXRtxn6L1W6mCRrX6aHBb+vQp
+BEYk3Pwu/OLd7TkOC5zwjCIPIlwV4FaYnWh0KooqpmvXuKJLAQBFa8yGWERYys73
+9a/U31cu6lndnG2lZfb47NTy+KdzDYsqB4GfnASqA7PbxJHDU4Fu7wp7gN3HRQID
+AQABMA0GCSqGSIb3DQEBBQUAA4IBAQBsFroSKr3MbCq1HjWpCLDEz2uS4LQV6L1G
+smNfGNY17ELOcY9uweBBXOsfKVOEizYJJqatbJlz6FmPkIbfsGW2Wospkp1gvYMy
+NGL27vX3rB5vOo5vdFITaaV9/dEu53A0iWdsn3wH/FJnMsqBmynb+/3FY+Lff9d1
+XBaXLr+DeBx4lrE8rWTvhWh8gqDkuNLBTygdH0+g8/xkqhQhLqjIlMCSnrG2cTfj
+LewizVcX/VZ6DNC2M2vjEFbCShclZHocG80N7udl5KNsLEU2jyO1F61Q0yo+VYGS
+7n8dRYgbOKyCjMdu69fAfZvp4aoy1SXqtjMphDh5R7y7mhP60e0A
+-----END CERTIFICATE-----
diff --git a/remoting/src/test/resources/certs/badServer.key b/remoting/src/test/resources/certs/badServer.key
new file mode 100644
index 0000000000000000000000000000000000000000..de4f7c580fa133b3c6892c8ae6a88be6ceaf0c82
--- /dev/null
+++ b/remoting/src/test/resources/certs/badServer.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALBvxESq2VvSpJl1
+skv8SzyPYKgU8bZx37hEOCmoeYvd9gWNfeYZuITng2/5mpWX+zuAgKsgPU66YG0v
+++dT5GBQPr0Imb25IMl3xOY2eEiLeMokYiWbnA1C+pw1a27zMqk6pgbcRaMfLdh5
+npusWtqBzZIxqo1TpaOGEmyQTNRlAgMBAAECgYBSigbUZOTIWxObov7lI0MDMsPx
+/dJSGpWhe3CWtHUgJJdKY7XpJlE3A6Nuh+N0ZiQm4ufOpodnxDMGAXOj9ZAZY16Y
+i7I0ayXepcpTqYqo0o0+ze2x7SECAXe26bqvLRuKG2hpUyM59vAmll9gmQM5n8z4
+ZzoAzqRqkRHdo5bTxQJBAOF6SwSSfb8KEtTjWpJ48W1PO/NmKbW3QsNCWuk/w5p7
+E8L2g3nwakJiFmVNCga74rUbcgbCkw7y/lLeM8yC74MCQQDIUgCN/vuHm+eT85xk
+QoVKhDljXzLoog6wTUf5SMtrmUFTbQqyvw5xjHYdp3TWJM/Px8IyLxOr97sSnnft
+l7/3AkEAukYLv6U+GRs7X4DMDIG6AjIZNwXJo4PYtfMVo+i3seHH+6MoDw8c2eaq
+1dmFVPbXXgNkek04rHr2vIMxi90H/QJAAMOfUOtaFkhX986EGDXQwFoExgZE8XI8
+0BtbXO4UKJLrFuBhnBDygyhgAvjyjyaQzGAcs4hOcOd/BTEpj/R2PQJBANUKa9T9
+qWYhDhWN1Uj7qXhC1j2z/vTAzcYuwhpPRjt3RaVl27itI7cqiGquFhwfKZZFaOh5
+pnnWHv63YbGQ2Qc=
+-----END PRIVATE KEY-----
diff --git a/remoting/src/test/resources/certs/badServer.pem b/remoting/src/test/resources/certs/badServer.pem
new file mode 100644
index 0000000000000000000000000000000000000000..ada0a94f4febf0b4961baa792fa1beae7ddf8296
--- /dev/null
+++ b/remoting/src/test/resources/certs/badServer.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5DCCAcwCAQEwDQYJKoZIhvcNAQEFBQAwfDELMAkGA1UEBhMCemgxCzAJBgNV
+BAgMAnpqMQswCQYDVQQHDAJoejEPMA0GA1UECgwGYXBhY2hlMREwDwYDVQQLDAhy
+b2NrZXRtcTEOMAwGA1UEAwwFeXVrb24xHzAdBgkqhkiG9w0BCQEWEHl1a29uQGFw
+YWNoZS5vcmcwHhcNMTcxMjExMDk0MzE3WhcNMTgwMTEwMDk0MzE3WjB4MQswCQYD
+VQQGEwJ6aDELMAkGA1UECAwCemoxCzAJBgNVBAcMAmh6MQ8wDQYDVQQKDAZhcGFj
+aGUxETAPBgNVBAsMCHJvY2tldG1xMQ8wDQYDVQQDDAZmb29iYXIxGjAYBgkqhkiG
+9w0BCQEWC2Zvb0BiYXIuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCw
+b8REqtlb0qSZdbJL/Es8j2CoFPG2cd+4RDgpqHmL3fYFjX3mGbiE54Nv+ZqVl/s7
+gICrID1OumBtL/vnU+RgUD69CJm9uSDJd8TmNnhIi3jKJGIlm5wNQvqcNWtu8zKp
+OqYG3EWjHy3YeZ6brFragc2SMaqNU6WjhhJskEzUZQIDAQABMA0GCSqGSIb3DQEB
+BQUAA4IBAQAx+0Se3yIvUOe23oQp6UecaHtfXJCZmi1p5WbwJi7jUcYz78JB8oBj
+tVsa+1jftJG+cJJxqgxo2IeIAVbcEteO19xm7dc8tgfH/Bl0rxQz4WEYKb2oF/EQ
+eRgcvj4uZ0d9WuprAvJgA4r0Slu2ZZ0cVkzi06NevTweTBYIKFzHaPShqUWEw8ki
+42V5jAtRve7sT0c4TH/01dd2fs3V4Ul3E2U3LOP6VizIfKckdht0Bh6B6/5L8wvH
+4l1f4ni7w34vXGANpmTP2FGjQQ3kYjKL7GzgMphh3Kozhil6g1GLMhxvp6ccCA9W
+m5g0cPa3RZnjI/FoD0lZ5S1Q5s9qXbLm
+-----END CERTIFICATE-----
diff --git a/remoting/src/test/resources/certs/ca.pem b/remoting/src/test/resources/certs/ca.pem
new file mode 100644
index 0000000000000000000000000000000000000000..d33cb49c22a94cbd72fe22823684139bca6038de
--- /dev/null
+++ b/remoting/src/test/resources/certs/ca.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIIDyzCCArOgAwIBAgIJAKzXC2VLdPclMA0GCSqGSIb3DQEBBQUAMHwxCzAJBgNV
+BAYTAnpoMQswCQYDVQQIDAJ6ajELMAkGA1UEBwwCaHoxDzANBgNVBAoMBmFwYWNo
+ZTERMA8GA1UECwwIcm9ja2V0bXExDjAMBgNVBAMMBXl1a29uMR8wHQYJKoZIhvcN
+AQkBFhB5dWtvbkBhcGFjaGUub3JnMB4XDTE3MTIxMTA5MjUxNFoXDTE4MDExMDA5
+MjUxNFowfDELMAkGA1UEBhMCemgxCzAJBgNVBAgMAnpqMQswCQYDVQQHDAJoejEP
+MA0GA1UECgwGYXBhY2hlMREwDwYDVQQLDAhyb2NrZXRtcTEOMAwGA1UEAwwFeXVr
+b24xHzAdBgkqhkiG9w0BCQEWEHl1a29uQGFwYWNoZS5vcmcwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQDLUZi/zPj+7sYbfTng/gJeHpvvrWZkiudNwh1t
+5kxAusrJyGBkGm+xmRPJeQPZzbhfwfrz/UiQSbjlyV4K+SEZuNIHBSU80aTnXFWg
+wIgIAKvu3ZwYkcTjSDBvZv1DgbRkuqAB5ExsJ4vovoNqZcsLFLKsqT1G7lTAwRKU
+/FTKgD4g/zvhEoolonzKuk7CPivfKWFzcTpe8zRQlI0O9+j9Pq38F+5yxP7atK/b
+uYw36Efgt8nbkjusWIyXibpDMbAUroJNNYlFnunb+XKLpslkrIrfLGiMUq2Ru940
+ooQaANYWzogRQeIofsMN6H9CCRXtVIzcgJJU3wWXGXPRuNr7AgMBAAGjUDBOMB0G
+A1UdDgQWBBTd3bmAcazOY2/TI/h4zaGhni+nJzAfBgNVHSMEGDAWgBTd3bmAcazO
+Y2/TI/h4zaGhni+nJzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBp
+KRcnYsVtFZJejyt02+7SaMudTNRgh5SexFWsl1O7qWUc+fMVgMHHDzGRbkdevcdZ
+9uKDwUoa6M1wlOeosTTfQlH9b/AwW6QT7KqdpcpMXlmoV/PNCAVt2QeVclmplvqo
+Rx8qUHNckvvzNZt1W6AkBG93P0BLK/3FMJDyYmxkstwnpBPf/3A+t5k2INUI7yQf
+B3Tqzs/4iQ3idCLqz2WhTNUYpZOREtpJMcFaOdMsGNnIF+LvkKGij0MPVd/mwJtL
+UvQXwbOWpCS7A73sWFqPnrSzpi4VwcvAsi8lUYXsc0H064oagb58zvYz3kXqybcb
+KQntj5dP4C3lLHUTTcAV
+-----END CERTIFICATE-----
diff --git a/remoting/src/test/resources/certs/client.key b/remoting/src/test/resources/certs/client.key
new file mode 100644
index 0000000000000000000000000000000000000000..c30daea2b901cd041afa272a404c0ec8418aec95
--- /dev/null
+++ b/remoting/src/test/resources/certs/client.key
@@ -0,0 +1,17 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIICoTAbBgkqhkiG9w0BBQMwDgQI1vtPpDhOYRcCAggABIICgMHwgw0p9fx95R/+
+cWnNdEq8I3ZOOy2wDjammFvPrYXcCJzS3Xg/0GDJ8pdJRKrI7253e4u3mxf5oMuY
+RrvpB3KfdelU1k/5QKqOxL/N0gQafQLViN53f6JelyBEAmO1UxQtKZtkTrdZg8ZP
+0u1cPPWxmgNdn1Xx3taMw+Wo05ysHjnHJhOEDQ2WT3VXigiRmFSX3H567yjYMRD+
+zmvBq+qqR9JPbH9Cn7X1oRXX6c8VsZHWF/Ds0I4i+5zJxsSIuNZxjZw9XXNgXtFv
+7FEFC0HDgDQQUY/FNPUbmjQUp1y0YxoOBjlyIqBIx5FWxu95p2xITS0OimQPFT0o
+IngaSb+EKRDhqpLxxIVEbDdkQrdRqcmmLGJioAysExTBDsDwkaEJGOp44bLDM4QW
+SIA9SB01omuCXgn7RjUyVXb5g0Lz+Nvsfp1YXUkPDO9hILfz3eMHDSW7/FzbB81M
+r8URaTagQxBZnvIoCoWszLDXn3JwEjpZEA6y55Naptps3mMRf7+XMt42lX0e4y9a
+ogNu5Zw/RZD9YcaTjC2z5XeKiMCs1Ymhy9iuzbo+eRGESqzvUE4VirtsiEwxJRci
+JHAvuAl3X4XnpTty4ahOU+DihM9lALxdU68CN9++7mx581pYuvjzrV+Z5+PuptZX
+AjCZmkZLDh8TCHSzWRqvP/Hcvo9BjW8l1Lq6tOa222PefSNCc6gs6Hq+jUghbabZ
+/ux4WuFc0Zd6bfQWAZohSvd78/ixsdJPNGm2OP+LUIrEDKIkLuH1PM0uq4wzJZNu
+Bo7oJ5iFWF67u3MC8oq+BqOVKDNWaCMi7iiSrN2XW8FBo/rpx4Lf/VYREL+Y0mP6
+vzJrZqw=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/remoting/src/test/resources/certs/client.pem b/remoting/src/test/resources/certs/client.pem
new file mode 100644
index 0000000000000000000000000000000000000000..31412fea8b03e3e7e584a7a8e85a63216a2ce2cf
--- /dev/null
+++ b/remoting/src/test/resources/certs/client.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8zCCAdsCAQIwDQYJKoZIhvcNAQEFBQAwfDELMAkGA1UEBhMCemgxCzAJBgNV
+BAgMAnpqMQswCQYDVQQHDAJoejEPMA0GA1UECgwGYXBhY2hlMREwDwYDVQQLDAhy
+b2NrZXRtcTEOMAwGA1UEAwwFeXVrb24xHzAdBgkqhkiG9w0BCQEWEHl1a29uQGFw
+YWNoZS5vcmcwHhcNMTcxMjExMDkyOTUyWhcNMTgwMTEwMDkyOTUyWjCBhjELMAkG
+A1UEBhMCemgxCzAJBgNVBAgMAnpqMQswCQYDVQQHDAJoejEPMA0GA1UECgwGYXBh
+Y2hlMREwDwYDVQQLDAhyb2NrZXRtcTEWMBQGA1UEAwwNZm9vYmFyLmNsaWVudDEh
+MB8GCSqGSIb3DQEJARYSZm9vQGJhci5jbGllbnQuY29tMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDoz5Uo2ZN+1ywSQXORv6kDbDXVQ72ZxBp7a6ltFLh4xdk/
+yz7bBjmryz+cAh759s8DEdngl2cCnSiM0r5NC91zox/12Di4EWt3IPdJVe1s5/VD
+Bqt5zoxbYyDgz47c95cGALgLdTB/itBPgobghQYBanWPVBNLLltw19DLf1gd6QID
+AQABMA0GCSqGSIb3DQEBBQUAA4IBAQDEpVFFcFILdnEXjyDSbgJ8rxXaUYV2aK+a
+lgrYfoHBv83MlEuHbydmHBoTL7BmPIL7JCIfufvRnnyBwcECi0E6qFbvMYNoMy6b
+OUiTq3nAnPSSoOgi2MxFxpGaOE0s2dp4K9U5nV6bXKLIwIZbJAiZT6aPVenNYJbv
+4arzFDe0Yjs/l3VYn2k9TjiiU2fxaW/8Ikx6o9nGWLTKeX/WtXfBNISqOPIL5dPF
+eaf0YKCVzvBQ3dIJiUyanRP1BCJJFrCsrPpyu4xFprbjRmDTnOpYB6CdIas5TMC8
+6HzB1fSFoltNEiCjlnLlfjpb5ueSLSbs6h1A7VH7NUEmLmncSlHf
+-----END CERTIFICATE-----
diff --git a/remoting/src/test/resources/certs/privkey.pem b/remoting/src/test/resources/certs/privkey.pem
new file mode 100644
index 0000000000000000000000000000000000000000..6955568011d6f6c978331577f9ef44f273e7d947
--- /dev/null
+++ b/remoting/src/test/resources/certs/privkey.pem
@@ -0,0 +1,30 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIBTqUKpwFlcUCAggA
+MBQGCCqGSIb3DQMHBAii5M3Oni0WEwSCBMj5amhdPBva0QxgsWWrSBkfvmc3QQbl
+YQE5gHXzL5oaG0lbXCLQe19pr9jFckdDH8uPIi+aRie3WpZXzYLdihjsV0CgE7iy
+90ac9fgzIZbJIk/WQIDgwUZm5dEYo2v+B0WwKiD5IHzlTlXM6HVv+DRdPwKjHjAt
+TCcCSPUgjKVxHWFtQzY7mqo8P8wcNQHGkEfoQQub6tEsUDeesjS0FoK5Z2oYsmhW
+d0PNuGXw3UIMbG109DmC2ILFuTf5WSc7mxI11qL9Z5wTmcFqN7KKb8+MIQEoteni
+HICOFfKQWn8er14lmYw9anQAyaeyF/JnYkmVB8vaHBFYs/5EFZtvznpEIIhLKCuG
+lve8PJQmfWuBlPdwwJhCXHrLvjfwku4jUF8febU0BHZ5HETaB195g8r9RWfdZcrG
+f3fMO4Kq/YoP6oSxKhMP4L2pwj57EMV9N5P87ZyDFNp9BwgIjCawDRUc1Gi9YKak
+rpDNabTCr0I3NW27VGGF9m/mby7BLragc01LgTH7SFWS+1D/61/V2YBDFmWn2yV4
+4eKGeBkR3w0m/nWWfNXko8UzM/hjJ4P7Njq8HXdvEpnbDRZWzwdTGWTEvn/TAI3h
+j7vmWUHdpOQgb0WGlvEUx3V9wi2Fc1rCseHtYZgLf3KdKYHauPAMSON7KBtKaFuU
+6685sUoJbhahN7ILfP3sDxM3VYjSvlPL00lgOdqDT/iO6pNXvnNnQROCCE4kcOQT
+uSnEu+wmFHj+QlC60ftRl6zGVqjBxf1+TGmzTEByAOfZtEQ8V/clzRI4BCxYbCAG
+mJSa+q1RSju8yClBkXGT2zfhUeNqJnXEIaD/uXCPVGg7hfLyCcVVSmL97aw9QAIe
+lBJN/4bdxXLnJaHFKyztRe9N97JAKY9HAPMKKhKtqprWB7LedTIPHtXnpoSjyTrG
+SEtlOTQ38s3v9bUPXqF7TYZb+ytj5bIQpl6+WqF9ZNj3gRyx7rcsILhBBg08olVQ
+WZwr7LlIxUcDlrbYmrwd9lsMz2nOW2CLCD7mVqJQa2Wm35l6vJHQAI0WiQlHnopC
+M2Y49JruWWim2lC8ZzHgTyiU54bIfkXKQxua8G5WGxpW4dDRrM1d0uYe9M1TOqvP
+jFxq+XEIj/LntJpY7XIZs+33wuNLIVvkee9zsap++zYNH+KIGmbXz/HUO6gYeJYw
+EeaBTLfXtNlgHV9TpMjj3Js6p8hMoVDx27kzPOXa3nrFbHiHmUYY39ZSBCE53Wew
+SKIr/FlKtthzJwJkoMNxxsZ1QcI6WgmRANSC4oP9OyM+hPnWlISJZsy9UlXRZNEJ
+1lI8P/FUbdk/hsRN98j4pb/hzI0yyG1tKaj0TBipjdEsxfthKdwS2sE1wG+F2hrg
+A1jG+UOYmreQjCbrzEiq0J0H4wSL6/s/zN7SyXIWBG0UFalWFiehG5242mEOyX03
+0Yi94mPhF/kcqYsWZ/JJo6cq/EqgIeIqEzkbKx+TOsXk13K2y6apgvCxeHDDv3yT
+DqQueRgFicl0319yEK8ARnREFBm8D5oqwMHjJzVzjrqhFGLW1jfQG9HEW5A+s8WF
+d+2OtH1o/jVdAPXoP1DnxdF+G7fNXDI4cyjejC7uhLuxHCOx648UpRE9+mCiI2IO
+LDM=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/remoting/src/test/resources/certs/server.key b/remoting/src/test/resources/certs/server.key
new file mode 100644
index 0000000000000000000000000000000000000000..30df69619e6693ccdb2ee6e9a8723f468e01a8e2
--- /dev/null
+++ b/remoting/src/test/resources/certs/server.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOsmp4YtrIRsBdBQ
+LyPImafCRynTJls3NNF4g6nZr9e0efBY830gw9kBebcm603sdZNl95fzRr2+srXi
+5FJbG7Fmq1+F0xLNK/kKWirGtNMT2DubmhVdKyXYJSvInoGRkrQzbOG0MdAyzE6Q
+O6OjjNN+xGkmadWyCyNF6S8YqMJTAgMBAAECgYEAj0OlnOIG0Ube4+N2VN7KfqKm
+qJy0Ka6gx14dGUY/E7Qo9n27GujzaSq09RkJExiVKZBeIH1fBAtC5f2uDV7kpy0l
+uNpTpQkbw0g2EQLxDsVwaUEYbu+t9qVeXoDd1vFeoXHBuRwvI9UW1BrxVtvKODia
+5StU8Lw4yjcm2lQalwECQQD/sKj56thIsIY7D9qBHk7fnFLd8aYzhnP2GsbZX4V/
+T1KHRxr/8MqdNQX53DE5qcyM/Mqu95FIpTAniUtvcBujAkEA62+fAMYFTAEWj4Z4
+vCmcoPqfVPWhBKFR/wo3L8uUARiIzlbYNU3LIqC2s16QO50+bLUd41oVHNw9Y+uM
+fxQpkQJACg/WpncSadHghmR6UchyjCQnsqo2wyJQX+fv2VAD/d2OPtqSem3sW0Fh
+6dI7cax36zhrdXUyl2xAt92URV9hBwJALX93sdWSxnpbWsc449wCydVFH00MnfFz
+AB+ARLtJ0eBk58M+qyZqgDmgtQ8sPmkH3EgwC3SoKdiiAIJPt2s1EQJBAKnISZZr
+qB2F2PfAW2JJbQlrPyVzkxhv9XYdiVNOErmuxLFae3AI7nECgGuFBtvmeqzm2yRj
+7RBMCmzyWG7MF3o=
+-----END PRIVATE KEY-----
diff --git a/remoting/src/test/resources/certs/server.pem b/remoting/src/test/resources/certs/server.pem
new file mode 100644
index 0000000000000000000000000000000000000000..b73e33ef8693ef3bc39b512890d1baf6ceca9be9
--- /dev/null
+++ b/remoting/src/test/resources/certs/server.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5DCCAcwCAQEwDQYJKoZIhvcNAQEFBQAwfDELMAkGA1UEBhMCemgxCzAJBgNV
+BAgMAnpqMQswCQYDVQQHDAJoejEPMA0GA1UECgwGYXBhY2hlMREwDwYDVQQLDAhy
+b2NrZXRtcTEOMAwGA1UEAwwFeXVrb24xHzAdBgkqhkiG9w0BCQEWEHl1a29uQGFw
+YWNoZS5vcmcwHhcNMTcxMjExMDkyNjIwWhcNMTgwMTEwMDkyNjIwWjB4MQswCQYD
+VQQGEwJ6aDELMAkGA1UECAwCemoxCzAJBgNVBAcMAmh6MQ8wDQYDVQQKDAZhcGFj
+aGUxETAPBgNVBAsMCHJvY2tldG1xMQ8wDQYDVQQDDAZmb29iYXIxGjAYBgkqhkiG
+9w0BCQEWC2Zvb0BiYXIuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDr
+JqeGLayEbAXQUC8jyJmnwkcp0yZbNzTReIOp2a/XtHnwWPN9IMPZAXm3JutN7HWT
+ZfeX80a9vrK14uRSWxuxZqtfhdMSzSv5CloqxrTTE9g7m5oVXSsl2CUryJ6BkZK0
+M2zhtDHQMsxOkDujo4zTfsRpJmnVsgsjRekvGKjCUwIDAQABMA0GCSqGSIb3DQEB
+BQUAA4IBAQCmhSgxU5PRhBD2qahj2eWKcmz3FCevXgfyN/EUrwI2dZTU5fXPP+m9
+YBLAYUINI0eYGWt0wlGJ6UFyEgt1fcXP3gqsye9fjECmWoae1kVjvYdaxYGsEXrM
+bxSum1D1bz6yRA+eSOaT5aesfw1ZL74AkIq5aRKQ4cgLGzlbIYeoa62XcAj6GrBo
+V2s/mvKCc1FPrqnpUlTTYFM9eRbEyC7HkOm9c+NAy6FqoLFr3tegH+q8ZxENDw4k
+z9gojQ6t1LDPOAmLGHwvMshHa841CwfOduSvzldtxzjnLVUvYB9cyXS1JXvuC9jj
+Q6BOXIYI+0HVgkJbcPOIYDlgC+g6QJqf
+-----END CERTIFICATE-----