This is an automated email from the ASF dual-hosted git repository.

szetszwo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ratis.git


The following commit(s) were added to refs/heads/master by this push:
     new da5d868  RATIS-1542. Support TlsConf in netty streaming. (#619)
da5d868 is described below

commit da5d86892b20039c0d514686d834cc4298e6f4cd
Author: Tsz-Wo Nicholas Sze <[email protected]>
AuthorDate: Wed Mar 16 21:58:43 2022 +0800

    RATIS-1542. Support TlsConf in netty streaming. (#619)
---
 .../java/org/apache/ratis/client/RaftClient.java   |   4 +-
 .../apache/ratis/client/impl/ClientImplUtils.java  |  10 +-
 .../apache/ratis/client/impl/RaftClientImpl.java   |   5 +-
 .../main/java/org/apache/ratis/conf/ConfUtils.java |  13 ++
 .../apache/ratis/security/SecurityTestUtils.java   |  65 ++++++
 .../apache/ratis/grpc/MiniRaftClusterWithGrpc.java |   8 +-
 .../org/apache/ratis/netty/NettyConfigKeys.java    |  85 ++++---
 .../apache/ratis/netty/NettyDataStreamFactory.java |  20 +-
 .../java/org/apache/ratis/netty/NettyUtils.java    |  13 +-
 .../ratis/netty/client/NettyClientStreamRpc.java   |  23 +-
 ...usterWithRpcTypeGrpcAndDataStreamTypeNetty.java |  25 ++-
 ...amSslWithRpcTypeGrpcAndDataStreamTypeNetty.java |  72 ++++++
 .../apache/ratis/netty/TestTlsConfWithNetty.java   | 250 +++++++++++++++++++++
 ratis-test/src/test/resources/ssl/ca.crt           |  46 ++++
 ratis-test/src/test/resources/ssl/ca.key           |  71 ++++++
 ratis-test/src/test/resources/ssl/client.crt       |  44 ++++
 ratis-test/src/test/resources/ssl/client.csr       |  43 ++++
 ratis-test/src/test/resources/ssl/client.key       |  68 ++++++
 ratis-test/src/test/resources/ssl/client.pem       |  69 ++++++
 ratis-test/src/test/resources/ssl/generate.sh      |  50 +++++
 ratis-test/src/test/resources/ssl/server.crt       |  44 ++++
 ratis-test/src/test/resources/ssl/server.csr       |  43 ++++
 ratis-test/src/test/resources/ssl/server.key       |  68 ++++++
 ratis-test/src/test/resources/ssl/server.pem       |  69 ++++++
 24 files changed, 1130 insertions(+), 78 deletions(-)

diff --git a/ratis-client/src/main/java/org/apache/ratis/client/RaftClient.java 
b/ratis-client/src/main/java/org/apache/ratis/client/RaftClient.java
index 6529732..d4c2f16 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/RaftClient.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/RaftClient.java
@@ -118,8 +118,8 @@ public interface RaftClient extends Closeable {
         }
       }
       return ClientImplUtils.newRaftClient(clientId, group, leaderId, 
primaryDataStreamServer,
-          Objects.requireNonNull(clientRpc, "The 'clientRpc' field is not 
initialized."),
-          properties, retryPolicy);
+          Objects.requireNonNull(clientRpc, "The 'clientRpc' field is not 
initialized."), retryPolicy,
+          properties, parameters);
     }
 
     /** Set {@link RaftClient} ID. */
diff --git 
a/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientImplUtils.java 
b/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientImplUtils.java
index eeddb22..392dcd7 100644
--- 
a/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientImplUtils.java
+++ 
b/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientImplUtils.java
@@ -21,6 +21,7 @@ import org.apache.ratis.client.DataStreamClient;
 import org.apache.ratis.client.DataStreamClientRpc;
 import org.apache.ratis.client.RaftClient;
 import org.apache.ratis.client.RaftClientRpc;
+import org.apache.ratis.conf.Parameters;
 import org.apache.ratis.conf.RaftProperties;
 import org.apache.ratis.protocol.RaftGroup;
 import org.apache.ratis.protocol.RaftGroupId;
@@ -31,11 +32,12 @@ import org.apache.ratis.protocol.RaftPeerId;
 
 /** Client utilities for internal use. */
 public interface ClientImplUtils {
+  @SuppressWarnings("checkstyle:ParameterNumber")
   static RaftClient newRaftClient(ClientId clientId, RaftGroup group,
-      RaftPeerId leaderId, RaftPeer primaryDataStreamServer, RaftClientRpc 
clientRpc, RaftProperties properties,
-      RetryPolicy retryPolicy) {
-    return new RaftClientImpl(clientId, group, leaderId, 
primaryDataStreamServer, clientRpc, properties,
-        retryPolicy);
+      RaftPeerId leaderId, RaftPeer primaryDataStreamServer, RaftClientRpc 
clientRpc, RetryPolicy retryPolicy,
+      RaftProperties properties, Parameters parameters) {
+    return new RaftClientImpl(clientId, group, leaderId, 
primaryDataStreamServer, clientRpc, retryPolicy,
+        properties, parameters);
   }
 
   static DataStreamClient newDataStreamClient(ClientId clientId, RaftGroupId 
groupId, RaftPeer primaryDataStreamServer,
diff --git 
a/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java 
b/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java
index 22254be..75871bf 100644
--- 
a/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java
+++ 
b/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java
@@ -24,6 +24,7 @@ import org.apache.ratis.client.api.DataStreamApi;
 import org.apache.ratis.client.api.LeaderElectionManagementApi;
 import org.apache.ratis.client.api.SnapshotManagementApi;
 import org.apache.ratis.client.retry.ClientRetryEvent;
+import org.apache.ratis.conf.Parameters;
 import org.apache.ratis.conf.RaftProperties;
 import org.apache.ratis.proto.RaftProtos.SlidingWindowEntry;
 import org.apache.ratis.protocol.ClientId;
@@ -146,8 +147,9 @@ public final class RaftClientImpl implements RaftClient {
   private final ConcurrentMap<RaftPeerId, LeaderElectionManagementApi>
       leaderElectionManagement = new ConcurrentHashMap<>();
 
+  @SuppressWarnings("checkstyle:ParameterNumber")
   RaftClientImpl(ClientId clientId, RaftGroup group, RaftPeerId leaderId, 
RaftPeer primaryDataStreamServer,
-      RaftClientRpc clientRpc, RaftProperties properties, RetryPolicy 
retryPolicy) {
+      RaftClientRpc clientRpc, RetryPolicy retryPolicy, RaftProperties 
properties, Parameters parameters) {
     this.clientId = clientId;
     this.peers.set(group.getPeers());
     this.groupId = group.getGroupId();
@@ -173,6 +175,7 @@ public final class RaftClientImpl implements RaftClient {
         .setRaftGroupId(groupId)
         .setDataStreamServer(primaryDataStreamServer)
         .setProperties(properties)
+        .setParameters(parameters)
         .build());
     this.adminApi = JavaUtils.memoize(() -> new AdminImpl(this));
   }
diff --git a/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java 
b/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
index 1d7d9c5..e5b04c6 100644
--- a/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
+++ b/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
@@ -17,6 +17,7 @@
  */
 package org.apache.ratis.conf;
 
+import org.apache.ratis.security.TlsConf;
 import org.apache.ratis.thirdparty.com.google.common.base.Objects;
 import org.apache.ratis.util.NetUtils;
 import org.apache.ratis.util.SizeInBytes;
@@ -35,6 +36,7 @@ import java.util.List;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 public interface ConfUtils {
   Logger LOG = LoggerFactory.getLogger(ConfUtils.class);
@@ -198,6 +200,12 @@ public interface ConfUtils {
     return value;
   }
 
+  static TlsConf getTlsConf(
+      Function<String, TlsConf> tlsConfGetter,
+      String key, Consumer<String> logger) {
+    return get((k, d) -> tlsConfGetter.apply(k), key, null, logger);
+  }
+
   @SafeVarargs
   static <T> T get(BiFunction<String, T, T> getter,
       String key, T defaultValue, Consumer<String> logger, BiConsumer<String, 
T>... assertions) {
@@ -271,6 +279,11 @@ public interface ConfUtils {
     set(timeDurationSetter, key, value, assertions);
   }
 
+  static void setTlsConf(
+      BiConsumer<String, TlsConf> tlsConfSetter, String key, TlsConf value) {
+    set(tlsConfSetter, key, value);
+  }
+
   @SafeVarargs
   static <T> void set(
       BiConsumer<String, T> setter, String key, T value,
diff --git 
a/ratis-common/src/test/java/org/apache/ratis/security/SecurityTestUtils.java 
b/ratis-common/src/test/java/org/apache/ratis/security/SecurityTestUtils.java
new file mode 100644
index 0000000..d35a38b
--- /dev/null
+++ 
b/ratis-common/src/test/java/org/apache/ratis/security/SecurityTestUtils.java
@@ -0,0 +1,65 @@
+/*
+ * 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.ratis.security;
+
+import org.apache.ratis.security.TlsConf.Builder;
+import org.apache.ratis.security.TlsConf.CertificatesConf;
+import org.apache.ratis.security.TlsConf.PrivateKeyConf;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Optional;
+
+public interface SecurityTestUtils {
+  Logger LOG = LoggerFactory.getLogger(SecurityTestUtils.class);
+
+  ClassLoader CLASS_LOADER = SecurityTestUtils.class.getClassLoader();
+
+  static File getResource(String name) {
+    final File file = Optional.ofNullable(CLASS_LOADER.getResource(name))
+        .map(URL::getFile)
+        .map(File::new)
+        .orElse(null);
+    LOG.info("Getting resource {}: {}", name, file);
+    return file;
+  }
+
+  static TlsConf newServerTlsConfig(boolean mutualAuthn) {
+    LOG.info("newServerTlsConfig: mutualAuthn? {}", mutualAuthn);
+    return new Builder()
+        .setName("server")
+        .setPrivateKey(new PrivateKeyConf(getResource("ssl/server.pem")))
+        .setKeyCertificates(new 
CertificatesConf(getResource("ssl/server.crt")))
+        .setTrustCertificates(new 
CertificatesConf(getResource("ssl/client.crt")))
+        .setMutualTls(mutualAuthn)
+        .build();
+  }
+
+  static TlsConf newClientTlsConfig(boolean mutualAuthn) {
+    LOG.info("newClientTlsConfig: mutualAuthn? {}", mutualAuthn);
+    return new Builder()
+        .setName("client")
+        .setPrivateKey(new PrivateKeyConf(getResource("ssl/client.pem")))
+        .setKeyCertificates(new 
CertificatesConf(getResource("ssl/client.crt")))
+        .setTrustCertificates(new CertificatesConf(getResource("ssl/ca.crt")))
+        .setMutualTls(mutualAuthn)
+        .build();
+  }
+}
\ No newline at end of file
diff --git 
a/ratis-grpc/src/test/java/org/apache/ratis/grpc/MiniRaftClusterWithGrpc.java 
b/ratis-grpc/src/test/java/org/apache/ratis/grpc/MiniRaftClusterWithGrpc.java
index 1528349..097a762 100644
--- 
a/ratis-grpc/src/test/java/org/apache/ratis/grpc/MiniRaftClusterWithGrpc.java
+++ 
b/ratis-grpc/src/test/java/org/apache/ratis/grpc/MiniRaftClusterWithGrpc.java
@@ -41,7 +41,7 @@ public class MiniRaftClusterWithGrpc extends 
MiniRaftCluster.RpcBase {
     @Override
     public MiniRaftClusterWithGrpc newCluster(String[] ids, RaftProperties 
prop) {
       RaftConfigKeys.Rpc.setType(prop, SupportedRpcType.GRPC);
-      return new MiniRaftClusterWithGrpc(ids, prop);
+      return new MiniRaftClusterWithGrpc(ids, prop, null);
     }
   };
 
@@ -55,8 +55,8 @@ public class MiniRaftClusterWithGrpc extends 
MiniRaftCluster.RpcBase {
   public static final DelayLocalExecutionInjection sendServerRequestInjection =
       new DelayLocalExecutionInjection(GrpcService.GRPC_SEND_SERVER_REQUEST);
 
-  protected MiniRaftClusterWithGrpc(String[] ids, RaftProperties properties) {
-    super(ids, properties, null);
+  protected MiniRaftClusterWithGrpc(String[] ids, RaftProperties properties, 
Parameters parameters) {
+    super(ids, properties, parameters);
   }
 
   @Override
@@ -66,7 +66,7 @@ public class MiniRaftClusterWithGrpc extends 
MiniRaftCluster.RpcBase {
         GrpcConfigKeys.Client.setPort(properties, 
NetUtils.createSocketAddr(address).getPort()));
     Optional.ofNullable(getAddress(id, group, 
RaftPeer::getAdminAddress)).ifPresent(address ->
         GrpcConfigKeys.Admin.setPort(properties, 
NetUtils.createSocketAddr(address).getPort()));
-    return null;
+    return parameters;
   }
 
   @Override
diff --git 
a/ratis-netty/src/main/java/org/apache/ratis/netty/NettyConfigKeys.java 
b/ratis-netty/src/main/java/org/apache/ratis/netty/NettyConfigKeys.java
index e6fe6a2..fcd7eac 100644
--- a/ratis-netty/src/main/java/org/apache/ratis/netty/NettyConfigKeys.java
+++ b/ratis-netty/src/main/java/org/apache/ratis/netty/NettyConfigKeys.java
@@ -17,7 +17,10 @@
  */
 package org.apache.ratis.netty;
 
+import org.apache.ratis.conf.ConfUtils;
+import org.apache.ratis.conf.Parameters;
 import org.apache.ratis.conf.RaftProperties;
+import org.apache.ratis.security.TlsConf;
 import org.apache.ratis.thirdparty.io.netty.util.NettyRuntime;
 import org.apache.ratis.util.TimeDuration;
 import org.slf4j.Logger;
@@ -52,7 +55,7 @@ public interface NettyConfigKeys {
   }
 
   interface DataStream {
-    Logger LOG = LoggerFactory.getLogger(Server.class);
+    Logger LOG = LoggerFactory.getLogger(DataStream.class);
     static Consumer<String> getDefaultLog() {
       return LOG::info;
     }
@@ -71,44 +74,62 @@ public interface NettyConfigKeys {
       setInt(properties::setInt, PORT_KEY, port);
     }
 
-    String CLIENT_WORKER_GROUP_SIZE_KEY = PREFIX + ".client.worker-group.size";
-    int CLIENT_WORKER_GROUP_SIZE_DEFAULT = Math.max(1, 
NettyRuntime.availableProcessors() * 2);
-
-    static int clientWorkerGroupSize(RaftProperties properties) {
-      return getInt(properties::getInt, CLIENT_WORKER_GROUP_SIZE_KEY,
-          CLIENT_WORKER_GROUP_SIZE_DEFAULT, getDefaultLog(), requireMin(1), 
requireMax(65536));
-    }
-
-    static void setClientWorkerGroupSize(RaftProperties properties, int 
clientWorkerGroupSize) {
-      setInt(properties::setInt, CLIENT_WORKER_GROUP_SIZE_KEY, 
clientWorkerGroupSize);
-    }
-
-    String CLIENT_WORKER_GROUP_SHARE_KEY = PREFIX + 
".client.worker-group.share";
-    boolean CLIENT_WORKER_GROUP_SHARE_DEFAULT = false;
+    interface Client {
+      String PREFIX = DataStream.PREFIX + ".client";
 
-    static boolean clientWorkerGroupShare(RaftProperties properties) {
-      return getBoolean(properties::getBoolean, CLIENT_WORKER_GROUP_SHARE_KEY,
-          CLIENT_WORKER_GROUP_SHARE_DEFAULT, getDefaultLog());
-    }
-
-    static void setClientWorkerGroupShare(RaftProperties properties, boolean 
clientWorkerGroupShare) {
-      setBoolean(properties::setBoolean, CLIENT_WORKER_GROUP_SHARE_KEY, 
clientWorkerGroupShare);
-    }
+      String TLS_CONF_PARAMETER = PREFIX + ".tls.conf";
+      Class<TlsConf> TLS_CONF_CLASS = TlsConf.class;
+      static TlsConf tlsConf(Parameters parameters) {
+        return getTlsConf(key -> parameters.get(key, TLS_CONF_CLASS), 
TLS_CONF_PARAMETER, getDefaultLog());
+      }
+      static void setTlsConf(Parameters parameters, TlsConf conf) {
+        LOG.info("setTlsConf " + conf);
+        ConfUtils.setTlsConf((key, value) -> parameters.put(key, value, 
TLS_CONF_CLASS), TLS_CONF_PARAMETER, conf);
+      }
 
-    String CLIENT_REPLY_QUEUE_GRACE_PERIOD_KEY = PREFIX + 
".client.reply.queue.grace-period";
-    TimeDuration CLIENT_REPLY_QUEUE_GRACE_PERIOD_DEFAULT = 
TimeDuration.ONE_SECOND;
+      String WORKER_GROUP_SIZE_KEY = PREFIX + ".worker-group.size";
+      int WORKER_GROUP_SIZE_DEFAULT = Math.max(1, 
NettyRuntime.availableProcessors() * 2);
+      static int workerGroupSize(RaftProperties properties) {
+        return getInt(properties::getInt, WORKER_GROUP_SIZE_KEY,
+            WORKER_GROUP_SIZE_DEFAULT, getDefaultLog(), requireMin(1), 
requireMax(65536));
+      }
+      static void setWorkerGroupSize(RaftProperties properties, int 
clientWorkerGroupSize) {
+        setInt(properties::setInt, WORKER_GROUP_SIZE_KEY, 
clientWorkerGroupSize);
+      }
 
-    static TimeDuration clientReplyQueueGracePeriod(RaftProperties properties) 
{
-      return 
getTimeDuration(properties.getTimeDuration(CLIENT_REPLY_QUEUE_GRACE_PERIOD_DEFAULT.getUnit()),
-          CLIENT_REPLY_QUEUE_GRACE_PERIOD_KEY, 
CLIENT_REPLY_QUEUE_GRACE_PERIOD_DEFAULT, getDefaultLog());
-    }
+      String WORKER_GROUP_SHARE_KEY = PREFIX + ".worker-group.share";
+      boolean WORKER_GROUP_SHARE_DEFAULT = false;
+      static boolean workerGroupShare(RaftProperties properties) {
+        return getBoolean(properties::getBoolean, WORKER_GROUP_SHARE_KEY,
+            WORKER_GROUP_SHARE_DEFAULT, getDefaultLog());
+      }
+      static void setWorkerGroupShare(RaftProperties properties, boolean 
clientWorkerGroupShare) {
+        setBoolean(properties::setBoolean, WORKER_GROUP_SHARE_KEY, 
clientWorkerGroupShare);
+      }
 
-    static void setClientReplyQueueGracePeriod(RaftProperties properties, 
TimeDuration timeoutDuration) {
-      setTimeDuration(properties::setTimeDuration, 
CLIENT_REPLY_QUEUE_GRACE_PERIOD_KEY, timeoutDuration);
+      String REPLY_QUEUE_GRACE_PERIOD_KEY = PREFIX + 
".reply.queue.grace-period";
+      TimeDuration REPLY_QUEUE_GRACE_PERIOD_DEFAULT = TimeDuration.ONE_SECOND;
+      static TimeDuration replyQueueGracePeriod(RaftProperties properties) {
+        return 
getTimeDuration(properties.getTimeDuration(REPLY_QUEUE_GRACE_PERIOD_DEFAULT.getUnit()),
+            REPLY_QUEUE_GRACE_PERIOD_KEY, REPLY_QUEUE_GRACE_PERIOD_DEFAULT, 
getDefaultLog());
+      }
+      static void setReplyQueueGracePeriod(RaftProperties properties, 
TimeDuration timeoutDuration) {
+        setTimeDuration(properties::setTimeDuration, 
REPLY_QUEUE_GRACE_PERIOD_KEY, timeoutDuration);
+      }
     }
 
     interface Server {
-      String PREFIX = NettyConfigKeys.PREFIX + ".server";
+      String PREFIX = DataStream.PREFIX + ".server";
+
+      String TLS_CONF_PARAMETER = PREFIX + ".tls.conf";
+      Class<TlsConf> TLS_CONF_CLASS = TlsConf.class;
+      static TlsConf tlsConf(Parameters parameters) {
+        return getTlsConf(key -> parameters.get(key, TLS_CONF_CLASS), 
TLS_CONF_PARAMETER, getDefaultLog());
+      }
+      static void setTlsConf(Parameters parameters, TlsConf conf) {
+        LOG.info("setTlsConf " + conf);
+        ConfUtils.setTlsConf((key, value) -> parameters.put(key, value, 
TLS_CONF_CLASS), TLS_CONF_PARAMETER, conf);
+      }
 
       String USE_EPOLL_KEY = PREFIX + ".use-epoll";
       boolean USE_EPOLL_DEFAULT = false;
diff --git 
a/ratis-netty/src/main/java/org/apache/ratis/netty/NettyDataStreamFactory.java 
b/ratis-netty/src/main/java/org/apache/ratis/netty/NettyDataStreamFactory.java
index 31bec8d..11aa051 100644
--- 
a/ratis-netty/src/main/java/org/apache/ratis/netty/NettyDataStreamFactory.java
+++ 
b/ratis-netty/src/main/java/org/apache/ratis/netty/NettyDataStreamFactory.java
@@ -17,29 +17,25 @@
  */
 package org.apache.ratis.netty;
 
-import org.apache.ratis.client.DataStreamClientRpc;
 import org.apache.ratis.client.DataStreamClientFactory;
+import org.apache.ratis.client.DataStreamClientRpc;
 import org.apache.ratis.conf.Parameters;
 import org.apache.ratis.conf.RaftProperties;
 import org.apache.ratis.datastream.SupportedDataStreamType;
 import org.apache.ratis.netty.client.NettyClientStreamRpc;
 import org.apache.ratis.netty.server.NettyServerStreamRpc;
 import org.apache.ratis.protocol.RaftPeer;
-import org.apache.ratis.security.TlsConf;
-import org.apache.ratis.server.DataStreamServerRpc;
 import org.apache.ratis.server.DataStreamServerFactory;
+import org.apache.ratis.server.DataStreamServerRpc;
 import org.apache.ratis.server.RaftServer;
 
+import java.util.Optional;
+
 public class NettyDataStreamFactory implements DataStreamServerFactory, 
DataStreamClientFactory {
-  private final TlsConf tlsConf;
+  private final Parameters parameters;
 
   public NettyDataStreamFactory(Parameters parameters) {
-    //TODO: RATIS-1542: get TlsConf from parameters and add tls tests
-    this((TlsConf) null);
-  }
-
-  private NettyDataStreamFactory(TlsConf tlsConf) {
-    this.tlsConf = tlsConf;
+    this.parameters = 
Optional.ofNullable(parameters).orElseGet(Parameters::new);
   }
 
   @Override
@@ -49,11 +45,11 @@ public class NettyDataStreamFactory implements 
DataStreamServerFactory, DataStre
 
   @Override
   public DataStreamClientRpc newDataStreamClientRpc(RaftPeer server, 
RaftProperties properties) {
-    return new NettyClientStreamRpc(server, tlsConf, properties);
+    return new NettyClientStreamRpc(server, 
NettyConfigKeys.DataStream.Client.tlsConf(parameters), properties);
   }
 
   @Override
   public DataStreamServerRpc newDataStreamServerRpc(RaftServer server) {
-    return new NettyServerStreamRpc(server, tlsConf);
+    return new NettyServerStreamRpc(server, 
NettyConfigKeys.DataStream.Server.tlsConf(parameters));
   }
 }
diff --git a/ratis-netty/src/main/java/org/apache/ratis/netty/NettyUtils.java 
b/ratis-netty/src/main/java/org/apache/ratis/netty/NettyUtils.java
index 560605e..146e199 100644
--- a/ratis-netty/src/main/java/org/apache/ratis/netty/NettyUtils.java
+++ b/ratis-netty/src/main/java/org/apache/ratis/netty/NettyUtils.java
@@ -96,7 +96,7 @@ public interface NettyUtils {
   }
 
   static SslContext buildSslContextForServer(TlsConf tlsConf) {
-    return buildSslContext(tlsConf, true, 
NettyUtils::initSslContextBuilderForServer);
+    return buildSslContext("server", tlsConf, 
NettyUtils::initSslContextBuilderForServer);
   }
 
   static SslContextBuilder initSslContextBuilderForClient(TlsConf tlsConf) {
@@ -109,18 +109,21 @@ public interface NettyUtils {
   }
 
   static SslContext buildSslContextForClient(TlsConf tlsConf) {
-    return buildSslContext(tlsConf, false, 
NettyUtils::initSslContextBuilderForClient);
+    return buildSslContext("client", tlsConf, 
NettyUtils::initSslContextBuilderForClient);
   }
 
-  static SslContext buildSslContext(TlsConf tlsConf, boolean isServer, 
Function<TlsConf, SslContextBuilder> builder) {
+  static SslContext buildSslContext(String name, TlsConf tlsConf, 
Function<TlsConf, SslContextBuilder> builder) {
     if (tlsConf == null) {
       return null;
     }
+    final SslContext sslContext;
     try {
-      return builder.apply(tlsConf).build();
+      sslContext = builder.apply(tlsConf).build();
     } catch (Exception e) {
-      final String message = "Failed to build a " + (isServer ? "server" : 
"client") + " SslContext from " + tlsConf;
+      final String message = "Failed to buildSslContext for " + name + " from 
" + tlsConf;
       throw new IllegalArgumentException(message, e);
     }
+    LOG.debug("buildSslContext for {} from {} returns {}", name, tlsConf, 
sslContext.getClass().getName());
+    return sslContext;
   }
 }
\ No newline at end of file
diff --git 
a/ratis-netty/src/main/java/org/apache/ratis/netty/client/NettyClientStreamRpc.java
 
b/ratis-netty/src/main/java/org/apache/ratis/netty/client/NettyClientStreamRpc.java
index b2714c7..dce9b63 100644
--- 
a/ratis-netty/src/main/java/org/apache/ratis/netty/client/NettyClientStreamRpc.java
+++ 
b/ratis-netty/src/main/java/org/apache/ratis/netty/client/NettyClientStreamRpc.java
@@ -57,6 +57,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.net.InetSocketAddress;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Optional;
@@ -78,14 +79,14 @@ public class NettyClientStreamRpc implements 
DataStreamClientRpc {
     static EventLoopGroup newWorkerGroup(RaftProperties properties) {
       return NettyUtils.newEventLoopGroup(
           JavaUtils.getClassSimpleName(NettyClientStreamRpc.class) + 
"-workerGroup",
-          NettyConfigKeys.DataStream.clientWorkerGroupSize(properties), false);
+          NettyConfigKeys.DataStream.Client.workerGroupSize(properties), 
false);
     }
 
     private final EventLoopGroup workerGroup;
     private final boolean ignoreShutdown;
 
     WorkerGroupGetter(RaftProperties properties) {
-      if (NettyConfigKeys.DataStream.clientWorkerGroupShare(properties)) {
+      if (NettyConfigKeys.DataStream.Client.workerGroupShare(properties)) {
         workerGroup = SHARED_WORKER_GROUP.updateAndGet(g -> g != null? g: 
newWorkerGroup(properties));
         ignoreShutdown = true;
       } else {
@@ -142,14 +143,14 @@ public class NettyClientStreamRpc implements 
DataStreamClientRpc {
   static class Connection {
     static final TimeDuration RECONNECT = TimeDuration.valueOf(100, 
TimeUnit.MILLISECONDS);
 
-    private final String address;
+    private final InetSocketAddress address;
     private final WorkerGroupGetter workerGroup;
     private final Supplier<ChannelInitializer<SocketChannel>> 
channelInitializerSupplier;
 
     /** The {@link ChannelFuture} is null when this connection is closed. */
     private final AtomicReference<ChannelFuture> ref;
 
-    Connection(String address, WorkerGroupGetter workerGroup,
+    Connection(InetSocketAddress address, WorkerGroupGetter workerGroup,
         Supplier<ChannelInitializer<SocketChannel>> 
channelInitializerSupplier) {
       this.address = address;
       this.workerGroup = workerGroup;
@@ -179,7 +180,7 @@ public class NettyClientStreamRpc implements 
DataStreamClientRpc {
           .channel(NioSocketChannel.class)
           .handler(channelInitializerSupplier.get())
           .option(ChannelOption.SO_KEEPALIVE, true)
-          .connect(NetUtils.createSocketAddr(address))
+          .connect(address)
           .addListener(new ChannelFutureListener() {
             @Override
             public void operationComplete(ChannelFuture future) {
@@ -236,12 +237,13 @@ public class NettyClientStreamRpc implements 
DataStreamClientRpc {
 
   public NettyClientStreamRpc(RaftPeer server, TlsConf tlsConf, RaftProperties 
properties) {
     this.name = JavaUtils.getClassSimpleName(getClass()) + "->" + server;
-    this.replyQueueGracePeriod = 
NettyConfigKeys.DataStream.clientReplyQueueGracePeriod(properties);
+    this.replyQueueGracePeriod = 
NettyConfigKeys.DataStream.Client.replyQueueGracePeriod(properties);
 
+    final InetSocketAddress address = 
NetUtils.createSocketAddr(server.getDataStreamAddress());
     final SslContext sslContext = NettyUtils.buildSslContextForClient(tlsConf);
-    this.connection = new Connection(server.getDataStreamAddress(),
+    this.connection = new Connection(address,
         new WorkerGroupGetter(properties),
-        () -> newChannelInitializer(sslContext, getClientHandler()));
+        () -> newChannelInitializer(address, sslContext, getClientHandler()));
   }
 
   private ChannelInboundHandler getClientHandler(){
@@ -306,13 +308,14 @@ public class NettyClientStreamRpc implements 
DataStreamClientRpc {
     };
   }
 
-  static ChannelInitializer<SocketChannel> newChannelInitializer(SslContext 
sslContext, ChannelInboundHandler handler) {
+  static ChannelInitializer<SocketChannel> newChannelInitializer(
+      InetSocketAddress address, SslContext sslContext, ChannelInboundHandler 
handler) {
     return new ChannelInitializer<SocketChannel>(){
       @Override
       public void initChannel(SocketChannel ch) {
         ChannelPipeline p = ch.pipeline();
         if (sslContext != null) {
-          p.addLast("ssl", sslContext.newHandler(ch.alloc()));
+          p.addLast("ssl", sslContext.newHandler(ch.alloc(), 
address.getHostName(), address.getPort()));
         }
         p.addLast(newEncoder());
         p.addLast(newEncoderDataStreamRequestFilePositionCount());
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/datastream/MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty.java
 
b/ratis-test/src/test/java/org/apache/ratis/datastream/MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty.java
index f5e97c1..1a210d8 100644
--- 
a/ratis-test/src/test/java/org/apache/ratis/datastream/MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty.java
+++ 
b/ratis-test/src/test/java/org/apache/ratis/datastream/MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty.java
@@ -17,6 +17,7 @@
  */
 package org.apache.ratis.datastream;
 
+import org.apache.ratis.security.TlsConf;
 import org.apache.ratis.server.impl.MiniRaftCluster;
 import org.apache.ratis.RaftConfigKeys;
 import org.apache.ratis.conf.Parameters;
@@ -31,25 +32,33 @@ import org.apache.ratis.rpc.SupportedRpcType;
  * A {@link MiniRaftCluster} with {{@link SupportedRpcType#GRPC}} and {@link 
SupportedDataStreamType#NETTY}.
  */
 public class MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty extends 
MiniRaftClusterWithGrpc {
-  public static final 
Factory<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty> FACTORY
-      = new Factory<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty>() {
+  static class Factory extends 
MiniRaftCluster.Factory<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty> {
+    private final Parameters parameters;
+
+    Factory(Parameters parameters) {
+      this.parameters = parameters;
+    }
+
     @Override
     public MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty 
newCluster(String[] ids, RaftProperties prop) {
       RaftConfigKeys.Rpc.setType(prop, SupportedRpcType.GRPC);
       RaftConfigKeys.DataStream.setType(prop, SupportedDataStreamType.NETTY);
-      return new MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty(ids, 
prop);
+      return new MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty(ids, 
prop, parameters);
     }
-  };
+  }
+
+  public static final Factory FACTORY = new Factory(null);
 
-  public interface FactoryGet extends 
Factory.Get<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty> {
+  public interface FactoryGet extends 
MiniRaftCluster.Factory.Get<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty>
 {
     @Override
-    default Factory<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty> 
getFactory() {
+    default 
MiniRaftCluster.Factory<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty> 
getFactory() {
       return FACTORY;
     }
   }
 
-  private MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty(String[] ids, 
RaftProperties properties) {
-    super(ids, properties);
+  private MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty(String[] ids, 
RaftProperties properties,
+      Parameters parameters) {
+    super(ids, properties, parameters);
   }
 
   @Override
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/datastream/TestDataStreamSslWithRpcTypeGrpcAndDataStreamTypeNetty.java
 
b/ratis-test/src/test/java/org/apache/ratis/datastream/TestDataStreamSslWithRpcTypeGrpcAndDataStreamTypeNetty.java
new file mode 100644
index 0000000..4a69aa4
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/datastream/TestDataStreamSslWithRpcTypeGrpcAndDataStreamTypeNetty.java
@@ -0,0 +1,72 @@
+/*
+ * 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.ratis.datastream;
+
+import org.apache.log4j.Level;
+import org.apache.ratis.conf.Parameters;
+import org.apache.ratis.netty.NettyConfigKeys;
+import org.apache.ratis.netty.NettyUtils;
+import org.apache.ratis.security.SecurityTestUtils;
+import org.apache.ratis.security.TlsConf;
+import org.apache.ratis.util.JavaUtils;
+import org.apache.ratis.util.Log4jUtils;
+import org.junit.Ignore;
+
+import java.util.function.Supplier;
+
+public class TestDataStreamSslWithRpcTypeGrpcAndDataStreamTypeNetty
+    extends 
DataStreamAsyncClusterTests<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty>
 {
+  {
+    Log4jUtils.setLogLevel(NettyUtils.LOG, Level.DEBUG);
+  }
+
+  Parameters newParameters() {
+    final Parameters parameters = new Parameters();
+    final TlsConf serverTlsConfig = SecurityTestUtils.newServerTlsConfig(true);
+    NettyConfigKeys.DataStream.Server.setTlsConf(parameters, serverTlsConfig);
+    final TlsConf clientTlsConfig = SecurityTestUtils.newClientTlsConfig(true);
+    NettyConfigKeys.DataStream.Client.setTlsConf(parameters, clientTlsConfig);
+    return parameters;
+  }
+
+  private final Supplier<Parameters> parameters = 
JavaUtils.memoize(this::newParameters);
+
+  public Parameters getParameters() {
+    return parameters.get();
+  }
+
+  @Override
+  public MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty.Factory 
getFactory() {
+    return new 
MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty.Factory(getParameters());
+  }
+
+  @Ignore
+  @Override
+  public void testStreamWrites() {
+  }
+
+  @Ignore
+  @Override
+  public void testMultipleStreamsMultipleServers() {
+  }
+
+  @Ignore
+  @Override
+  public void testMultipleStreamsMultipleServersStepDownLeader() {
+  }
+}
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/netty/TestTlsConfWithNetty.java 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestTlsConfWithNetty.java
new file mode 100644
index 0000000..d33639c
--- /dev/null
+++ b/ratis-test/src/test/java/org/apache/ratis/netty/TestTlsConfWithNetty.java
@@ -0,0 +1,250 @@
+/*
+ * 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.ratis.netty;
+
+import org.apache.ratis.security.SecurityTestUtils;
+import org.apache.ratis.security.TlsConf;
+import org.apache.ratis.thirdparty.io.netty.bootstrap.Bootstrap;
+import org.apache.ratis.thirdparty.io.netty.bootstrap.ServerBootstrap;
+import org.apache.ratis.thirdparty.io.netty.buffer.ByteBuf;
+import org.apache.ratis.thirdparty.io.netty.buffer.Unpooled;
+import org.apache.ratis.thirdparty.io.netty.channel.ChannelFuture;
+import org.apache.ratis.thirdparty.io.netty.channel.ChannelHandlerContext;
+import org.apache.ratis.thirdparty.io.netty.channel.ChannelInboundHandler;
+import 
org.apache.ratis.thirdparty.io.netty.channel.ChannelInboundHandlerAdapter;
+import org.apache.ratis.thirdparty.io.netty.channel.ChannelInitializer;
+import org.apache.ratis.thirdparty.io.netty.channel.ChannelOption;
+import org.apache.ratis.thirdparty.io.netty.channel.ChannelPipeline;
+import org.apache.ratis.thirdparty.io.netty.channel.EventLoopGroup;
+import org.apache.ratis.thirdparty.io.netty.channel.nio.NioEventLoopGroup;
+import org.apache.ratis.thirdparty.io.netty.channel.socket.SocketChannel;
+import 
org.apache.ratis.thirdparty.io.netty.channel.socket.nio.NioServerSocketChannel;
+import 
org.apache.ratis.thirdparty.io.netty.channel.socket.nio.NioSocketChannel;
+import org.apache.ratis.thirdparty.io.netty.handler.logging.LogLevel;
+import org.apache.ratis.thirdparty.io.netty.handler.logging.LoggingHandler;
+import org.apache.ratis.thirdparty.io.netty.handler.ssl.SslContext;
+import org.junit.Assert;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Closeable;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Testing {@link TlsConf} and the security related utility methods in {@link 
NettyUtils}.
+ */
+public class TestTlsConfWithNetty {
+  private final static Logger LOG = 
LoggerFactory.getLogger(TestTlsConfWithNetty.class);
+
+  static String buffer2String(ByteBuf buf){
+    try {
+      return buf.toString(StandardCharsets.UTF_8);
+    } finally {
+      buf.release();
+    }
+  }
+
+  static ByteBuf unpooledBuffer(String s) {
+    final ByteBuf buf = Unpooled.buffer();
+    buf.writeBytes(s.getBytes(StandardCharsets.UTF_8));
+    return buf;
+  }
+
+  static int randomPort() {
+    final int port = 50000 + ThreadLocalRandom.current().nextInt(10000);
+    LOG.info("randomPort: {}", port);
+    return port;
+  }
+
+  @Test
+  public void testNoSsl() throws Exception {
+    runTest(randomPort(), null, null);
+  }
+
+  @Test
+  public void testSsl() throws Exception {
+    final TlsConf serverTlsConfig = SecurityTestUtils.newServerTlsConfig(true);
+    final TlsConf clientTlsConfig = SecurityTestUtils.newClientTlsConfig(true);
+    runTest(randomPort(), serverTlsConfig, clientTlsConfig);
+  }
+
+  static void runTest(int port, TlsConf serverSslConf, TlsConf clientSslConf) 
throws Exception {
+    final SslContext serverSslContext = serverSslConf == null? null
+        : NettyUtils.buildSslContextForServer(serverSslConf);
+    final SslContext clientSslContext = clientSslConf == null? null
+        : NettyUtils.buildSslContextForClient(clientSslConf);
+
+    final String message = "Hey, how are you?";
+    final String[] words = message.split(" ");
+    try (NettyTestServer server = new NettyTestServer(port, serverSslContext);
+         NettyTestClient client = new NettyTestClient("localhost", port, 
clientSslContext)) {
+      final List<CompletableFuture<String>> replyFutures = new ArrayList<>();
+      for(String word : words) {
+        final ByteBuf buf = unpooledBuffer(word + " ");
+        final CompletableFuture<String> f = client.writeAndFlush(buf);
+        replyFutures.add(f);
+      }
+      for(int i = 0; i < replyFutures.size(); i++) {
+        final CompletableFuture<String> future = replyFutures.get(i);
+        final String reply = future.get(3, TimeUnit.SECONDS);
+        LOG.info(reply);
+        Assert.assertEquals(NettyTestServer.toReply(words[i]), reply);
+      }
+    }
+  }
+
+  static class NettyTestServer implements Closeable {
+    private static final Logger LOG = 
LoggerFactory.getLogger(NettyTestServer.class);
+
+    private final EventLoopGroup bossGroup = new NioEventLoopGroup(3);
+    private final EventLoopGroup workerGroup = new NioEventLoopGroup(3);
+    private final ChannelFuture channelFuture;
+
+    public NettyTestServer(int port, SslContext sslContext) {
+      this.channelFuture = new ServerBootstrap()
+          .group(bossGroup, workerGroup)
+          .channel(NioServerSocketChannel.class)
+          .handler(new LoggingHandler(getClass(), LogLevel.INFO))
+          .childHandler(newChannelInitializer(sslContext))
+          .bind(port)
+          .syncUninterruptibly();
+    }
+
+    private ChannelInitializer<SocketChannel> newChannelInitializer(SslContext 
sslContext){
+      return new ChannelInitializer<SocketChannel>(){
+        @Override
+        public void initChannel(SocketChannel ch) {
+          final ChannelPipeline p = ch.pipeline();
+          if (sslContext != null) {
+            p.addLast("ssl", sslContext.newHandler(ch.alloc()));
+          }
+          p.addLast(newServerHandler());
+        }
+      };
+    }
+
+    private ChannelInboundHandler newServerHandler(){
+      return new ChannelInboundHandlerAdapter() {
+        @Override
+        public void channelRead(ChannelHandlerContext ctx, Object obj) {
+          if (obj instanceof ByteBuf) {
+            final String s = buffer2String((ByteBuf) obj);
+            LOG.info("channelRead: " + s);
+            for(String word : s.split(" ")) {
+              ctx.writeAndFlush(unpooledBuffer(toReply(word) + " "));
+            }
+          }
+        }
+
+        @Override
+        public void exceptionCaught(ChannelHandlerContext ctx, Throwable 
throwable) {
+          LOG.error(NettyTestServer.this.getClass().getSimpleName() + ": 
exceptionCaught", throwable);
+          ctx.close();
+        }
+      };
+    }
+
+    static String toReply(String request) {
+      return "[" + request + "]";
+    }
+
+
+    @Override
+    public void close() {
+      channelFuture.channel().close();
+      bossGroup.shutdownGracefully();
+      workerGroup.shutdownGracefully();
+    }
+  }
+
+  static class NettyTestClient implements Closeable {
+    private static final Logger LOG = 
LoggerFactory.getLogger(NettyTestClient.class);
+
+    private final EventLoopGroup workerGroup = new NioEventLoopGroup(3);
+    private final ChannelFuture channelFuture;
+
+
+    private final Queue<CompletableFuture<String>> queue = new LinkedList<>();
+
+    public NettyTestClient(String host, int port, SslContext sslContext) {
+      this.channelFuture = new Bootstrap()
+          .group(workerGroup)
+          .channel(NioSocketChannel.class)
+          .handler(new LoggingHandler(getClass(), LogLevel.INFO))
+          .handler(newChannelInitializer(sslContext, host, port))
+          .option(ChannelOption.SO_KEEPALIVE, true)
+          .option(ChannelOption.TCP_NODELAY, true)
+          .connect(host, port)
+          .syncUninterruptibly();
+    }
+
+    public CompletableFuture<String> writeAndFlush(ByteBuf buf) {
+      final CompletableFuture<String> reply = new CompletableFuture<>();
+      queue.offer(reply);
+      channelFuture.channel().writeAndFlush(buf);
+      return reply;
+    }
+
+    private ChannelInitializer<SocketChannel> newChannelInitializer(SslContext 
sslContext, String host, int port) {
+      return new ChannelInitializer<SocketChannel>(){
+        @Override
+        public void initChannel(SocketChannel ch) {
+          ChannelPipeline p = ch.pipeline();
+          if (sslContext != null) {
+            p.addLast("ssl", sslContext.newHandler(ch.alloc(), host, port));
+          }
+          p.addLast(getClientHandler());
+        }
+      };
+    }
+
+    private ChannelInboundHandler getClientHandler(){
+      return new ChannelInboundHandlerAdapter(){
+        @Override
+        public void channelRead(ChannelHandlerContext ctx, Object obj) {
+          final String s = buffer2String((ByteBuf) obj);
+          LOG.info("received: " + s);
+          for(String word : s.split(" ")) {
+            queue.remove().complete(word);
+          }
+        }
+
+        @Override
+        public void exceptionCaught(ChannelHandlerContext ctx, Throwable 
cause) {
+          LOG.error(NettyTestClient.this.getClass().getSimpleName() + ": 
exceptionCaught", cause);
+          ctx.close();
+        }
+      };
+    }
+
+    @Override
+    public void close() {
+      channelFuture.channel().close();
+      workerGroup.shutdownGracefully();
+    }
+  }
+}
diff --git a/ratis-test/src/test/resources/ssl/ca.crt 
b/ratis-test/src/test/resources/ssl/ca.crt
new file mode 100644
index 0000000..4e0519c
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/ca.crt
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+-----BEGIN CERTIFICATE-----
+MIIFCTCCAvGgAwIBAgIUIVxtEYUqKP75yGl4BFak/NlORQEwDQYJKoZIhvcNAQEL
+BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIxMDYwNDEzMDgzM1oXDTMxMDYw
+MjEzMDgzM1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEAsDabxsyPdRnF5J/xIo3K6QQA8wx2FKz04YzJQO6A03A2
+ICIHzWnDIcDxkCgmMS0lujq3Ai/2/uaeEiUsItajqboQ45b7C5LmPrc5qM1NTnHj
+AfxD6BZ2+8fbJfkRvLKrjvErck3zKIsMEdrHXbGYo68l34YwKLdlfkFThJox7mPY
+EqCDXYuRgI4eWZ5FFkAWZd2sOo6bdnsg+2+7Y9hawhqfn9RldNj7wyQJ5Pv/kWr2
+SzskoJENJMkxrgJfYfCn8E7jQzCP/XZmTEpW2dXJb3KMxrO5BN933pA687k7WniC
+Ft8E3zeKLV2LM6gijModOk4VCPLbw1+j8D3wt68lFfMWvWqS1YEizJvFDWPVC0RT
+JHk3k80Vcz91wFoAvcrpkLDmFrbdHHj2lRl3dpY+P0ymk9DGkDnK4DsrpZ2QyOzm
+0fT0BmMKvSqnNjxvGf0psrdjUZMiNuvE8+7YBwTyw/FUMEBtuY3S6MFnsrsK6vmO
+g1lRGRjuF0dVq86AYE1QPZQmB1ZiN8kIymN2bHpKPF7huLDKZ6YUzlXey8Ak4Osu
+AmoULN7O9lGB4kFXRy2Ll+KYWnP/T0YJS0e3UyQRtj6sNRIX+ckkWMRkEpvZR/+O
+ozg0Pu8pXQK2hTKiZxcvYUyXl10Db+0jkKjCXZqTFCRsibo9lOlcxY4ZCGd7fR8C
+AwEAAaNTMFEwHQYDVR0OBBYEFKs6E+inQbqexOtm8AB1YFsAtbcWMB8GA1UdIwQY
+MBaAFKs6E+inQbqexOtm8AB1YFsAtbcWMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
+hvcNAQELBQADggIBAF+igdCKtvKYNgP9yuvM31cpLPdAtvnG9uTv9YIFPrmjhMJ4
+80dZ5i0rboQsjrS45pQ5/Q+6vLCP+SI+tbtcQBuAIlNVdFztHpVrxbwLqom/nZGo
+kGGo1BgrSZbVtx40Rwl4oOC5HBqkoV1nkVe1SyDsDfe6yn8gZJz9KhjA+T5MHI8c
+COlTVsAKdP27yNlagXmI2VkZV3VtISAUCU9HlrIIIQyC14/Zcgd1n84hKAaQKT5U
+9VFOCvc/3sc69vG8CpyQNRmX4UDreUaAtU1EnZFUGUuHCQvB+a1baXp69dcjrG/6
+jFNL/5MTMf+UQ6xRGeKp0fB/i91Z2mfLsrAUl3rkTjm9MV1SJJUwTZ0qlh+5NmBS
+15+eJoHe3JRfOPTNabmnmtpGyrBVY+RPiUfOmQSq2HAejIUGjA+hUcDSCslhBtML
+/wxhqgggLlvDZ1d/zq8nnXRl4tpHYlaQICuBFZu2aElN5XcLi6Gt53J1yPJqd91l
+IgF5FzAmg74wpbzT8S3UN2Snhn/Ys8t68c3etOsGr8uiljn874WhQF0kvdDmfgx5
+BGAQyhXPfNnBRnD+tMiabwyBOUfOAN8+YGEh8LrJe+QWWPUHmoiEJd2LMdJFpCYD
+HEJvQjxZPlKDVCyG9kKkS1B3Fa+kaDVhR9aGWj9IWq33i+iyBIQe5VkJ+p+7
+-----END CERTIFICATE-----
diff --git a/ratis-test/src/test/resources/ssl/ca.key 
b/ratis-test/src/test/resources/ssl/ca.key
new file mode 100644
index 0000000..3481897
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/ca.key
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,F9550AC98392E96B
+
+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
+-----END RSA PRIVATE KEY-----
diff --git a/ratis-test/src/test/resources/ssl/client.crt 
b/ratis-test/src/test/resources/ssl/client.crt
new file mode 100644
index 0000000..7081283
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/client.crt
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+-----BEGIN CERTIFICATE-----
+MIIEnDCCAoQCAQEwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJbG9jYWxob3N0
+MB4XDTIxMDYwNDEzMDgzM1oXDTMxMDYwMjEzMDgzM1owFDESMBAGA1UEAwwJbG9j
+YWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA066Z8xTn9sX2
+BqMv+jVD302CeOFyJPH5disAx+f5K73rhSSaq0itTXExR6w2vhI+Zj6hLIq+jjpg
+xWK7Ki9hvpkvnQdKT7CqllDHL//9PvEe75MZjWWAqeqfYc2rJ5cik4p/4nRp1ldP
+brTi8ziAEBmgRWZ7TDrEN07Myi1bII7EzuGD0c7JXGePQOCfQyu+gozFDyftQX+p
+wEtJ9P7VF53aqfy2dw0+Bt5F2cQmIIaeS/wQlM9brlvmuFEelo333kbtAi8CXv1K
+hoPGYAGXDHc/ldjatOHymhJ6WewvMalFgpFcq+r9hQvED0eEtvagD/baNLWLkSL9
+w9aBbdULzA08ERY1onr95+ZjXaEc8v2yKJwPLLy/AVdssEmtJFfQYiwh+HT31TYU
+QWe47w1E5RUTBMPonzmcyjsAAq8JEMA7my4g2Zaj4YmuRddD0vFJB627WGW089qr
+zo7qzQRvj6I/VNzyswaNuOG2rNKSwAuUQL0PgijlQvNnyHylUR0tlzU9yehQdYW+
+mmMGnv2eF7p+cFGQDjhELS8ua0+uVEFysLynzn1rZWKXz7RdIcCbugio5ORtXupp
+TOSSDq+ctYKMMR5kW65sBINFI3Vw+KhqYQXKmLKcyOKRP4IS7bFSMZwlHjasULn3
+fwVlzq/jN0Ahg4hRIgJCp+fAK0XhKKcCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEA
+QoM8w5JkeWqJsNr4s6s2tSXefvzHUWmqYGKd/GsBMkf0aL7Q2QxWPHM3jbP1E2EW
+gNLpqIyZ6w/mP6nC/ljcNoQH6bvGUdA/0mB+uOptERQwaoncvmff9X39GCqc65+b
+Cstm9gw+B3+RWnOLMcM87/khqkB5zN0nNyqqgPVZIjluJ2CsmbZUTnNV5ME+95XI
+OfhT5nm4+BQyPrmR5xIi2LtA5hqAGUF/lv+WbwqFRPhkOX0CVed+1r58sxHqP2mu
+vHpLNYmLLIcM3yCsOgs4QuGulHma5ysxUPQQlyf0e7iNoRkPZDcpb8JYNxOk/Qv8
+SN9s3KEewD6OQT/gpcaSh3vkfd0ZlPDs3ShSq2ywz1V6u0WW2rBTLOBYpqMkO0ku
+SOWdNbWck8vz+7qgDE2T7hW1o9XlG1yjFQeKei7Af+LnkjOJDkqZAnOyGVubY5Bd
+tuE+RpIInZCkQIEV5Du7eT7ncCUpLQGzl5Zpb6yFz03pXqL9HtZCwu1LF9vIbBFi
+4Q9smxUrim1WShis3vY0VsFzxFDi/RVrPTUy9oxIAwYW548IW/13xQZcIPgrYn3m
+q5mdPdS+NcywJt7NEN1CE8SV8D00eeCB8gkj/5DDZ+yj7A23uZ2+AFRi1GdiR5PH
+aGk8S4VRmummXTnMsW5FYzsdBr2xfGFyZ8V2PUqF5UA=
+-----END CERTIFICATE-----
diff --git a/ratis-test/src/test/resources/ssl/client.csr 
b/ratis-test/src/test/resources/ssl/client.csr
new file mode 100644
index 0000000..e4ed6cd
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/client.csr
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+-----BEGIN CERTIFICATE REQUEST-----
+MIIEWTCCAkECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA066Z8xTn9sX2BqMv+jVD302CeOFyJPH5disAx+f5
+K73rhSSaq0itTXExR6w2vhI+Zj6hLIq+jjpgxWK7Ki9hvpkvnQdKT7CqllDHL//9
+PvEe75MZjWWAqeqfYc2rJ5cik4p/4nRp1ldPbrTi8ziAEBmgRWZ7TDrEN07Myi1b
+II7EzuGD0c7JXGePQOCfQyu+gozFDyftQX+pwEtJ9P7VF53aqfy2dw0+Bt5F2cQm
+IIaeS/wQlM9brlvmuFEelo333kbtAi8CXv1KhoPGYAGXDHc/ldjatOHymhJ6Wewv
+MalFgpFcq+r9hQvED0eEtvagD/baNLWLkSL9w9aBbdULzA08ERY1onr95+ZjXaEc
+8v2yKJwPLLy/AVdssEmtJFfQYiwh+HT31TYUQWe47w1E5RUTBMPonzmcyjsAAq8J
+EMA7my4g2Zaj4YmuRddD0vFJB627WGW089qrzo7qzQRvj6I/VNzyswaNuOG2rNKS
+wAuUQL0PgijlQvNnyHylUR0tlzU9yehQdYW+mmMGnv2eF7p+cFGQDjhELS8ua0+u
+VEFysLynzn1rZWKXz7RdIcCbugio5ORtXuppTOSSDq+ctYKMMR5kW65sBINFI3Vw
++KhqYQXKmLKcyOKRP4IS7bFSMZwlHjasULn3fwVlzq/jN0Ahg4hRIgJCp+fAK0Xh
+KKcCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4ICAQCDCV5pC0dh7rL6QaxWaYHeMy5a
+Khw25nNCs18D4Ttz2o5t7mb06xpzbl1T+6QBFXtMe4qEPrAq6xMPu7Pf4uiJDR4e
+UO4yvGbAz4MFyy01qbl6StVTpkERSINS/ykxi04/GOd9U91+b3rBiMAAmQa8673Q
+k1k0aqNTmmvOdJjr3OZHnTiIRWYzKzVUc9uq9ntodGUKAS99ewi+CIG3SHeQCbql
+3HQ3iAVkSszMgjPc6JtpZ6MwGl00CLkCpjty7jME9n3ZtoA0H2hLUoja+oiqTory
+i33ACkTLz4WoN3I8DaQOQBOHqJ1X7kz/vqzCM0u7UrC6i1l0/DhV8knGPcWXduDB
+0joj+3UQNwxA6Qoj/xgr7OQ13Qz4mjraGtqqXJuQNaz0qzLcUxwXB4JgEZ+sZjPy
+CjvLMZWPPWLh3lNr1hLMPU8+QIAq/OrcCpPqD1tmXiAPLi9vKd21D1C6xripGRIB
+XEdh6L1Y0WJdwr7jNC4Jjk/HHxCf8AYU0zr9P+9UCsE29V2VF11LYTMW+Jrn4Pog
+mkJC0wKEfrwH20rHsHm8r06i+TbXtNQd0a+sB5auHdUz+qR566/nSWBP0LXdY2Y6
+SzLVkBGDbRfDp27R1zh2TqK4A+mqX+f7hocWtn9FcWRQsoVhS8EYh3Q+A4CAaRU2
+PrXS6ii1ODpa6QhelA==
+-----END CERTIFICATE REQUEST-----
diff --git a/ratis-test/src/test/resources/ssl/client.key 
b/ratis-test/src/test/resources/ssl/client.key
new file mode 100644
index 0000000..8a5a158
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/client.key
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/ratis-test/src/test/resources/ssl/client.pem 
b/ratis-test/src/test/resources/ssl/client.pem
new file mode 100644
index 0000000..a833dde
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/client.pem
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+-----BEGIN PRIVATE KEY-----
+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
+2aZkEK99uNWcsTLOgMbe555x+EObmSsb
+-----END PRIVATE KEY-----
diff --git a/ratis-test/src/test/resources/ssl/generate.sh 
b/ratis-test/src/test/resources/ssl/generate.sh
new file mode 100755
index 0000000..809333c
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/generate.sh
@@ -0,0 +1,50 @@
+# 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.
+
+# Changes these CN's to match your hosts in your environment if needed.
+SERVER_CN=localhost
+CLIENT_CN=localhost # Used when doing mutual TLS
+
+echo Generate CA key:
+openssl genrsa -passout pass:1111 -des3 -out ca.key 4096
+echo Generate CA certificate:
+# Generates ca.crt which is the trustCertCollectionFile
+openssl req -passin pass:1111 -new -x509 -days 3650 -key ca.key -out ca.crt 
-subj "/CN=${SERVER_CN}"
+echo Generate server key:
+openssl genrsa -passout pass:1111 -des3 -out server.key 4096
+echo Generate server signing request:
+openssl req -passin pass:1111 -new -key server.key -out server.csr -subj 
"/CN=${SERVER_CN}"
+echo Self-signed server certificate:
+# Generates server.crt which is the certChainFile for the server
+openssl x509 -req -passin pass:1111 -days 3650 -in server.csr -CA ca.crt 
-CAkey ca.key -set_serial 01 -out server.crt 
+echo Remove passphrase from server key:
+openssl rsa -passin pass:1111 -in server.key -out server.key
+echo Generate client key
+openssl genrsa -passout pass:1111 -des3 -out client.key 4096
+echo Generate client signing request:
+openssl req -passin pass:1111 -new -key client.key -out client.csr -subj 
"/CN=${CLIENT_CN}"
+echo Self-signed client certificate:
+# Generates client.crt which is the clientCertChainFile for the client (need 
for mutual TLS only)
+openssl x509 -passin pass:1111 -req -days 3650 -in client.csr -CA ca.crt 
-CAkey ca.key -set_serial 01 -out client.crt
+echo Remove passphrase from client key:
+openssl rsa -passin pass:1111 -in client.key -out client.key
+echo Converting the private keys to X.509:
+# Generates client.pem which is the clientPrivateKeyFile for the Client 
(needed for mutual TLS only)
+openssl pkcs8 -topk8 -nocrypt -in client.key -out client.pem
+# Generates server.pem which is the privateKeyFile for the Server
+openssl pkcs8 -topk8 -nocrypt -in server.key -out server.pem
+
+
diff --git a/ratis-test/src/test/resources/ssl/server.crt 
b/ratis-test/src/test/resources/ssl/server.crt
new file mode 100644
index 0000000..aa43f66
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/server.crt
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+-----BEGIN CERTIFICATE-----
+MIIEnDCCAoQCAQEwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJbG9jYWxob3N0
+MB4XDTIxMDYwNDEzMDgzM1oXDTMxMDYwMjEzMDgzM1owFDESMBAGA1UEAwwJbG9j
+YWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuceyZa7AKmiq
+Yof4KcEb84MOu+zPmujOpBRLH74Bc0z05hC0pCLMMVLkEGAkrVBoBHLfeuhoRdr+
+hKVdnPaNUv5uW2qpyuFU4DG43wEXKYeQTleuMDefzx37xgUJNyqFJAnZsu+Hst9r
+aO6TfbJ4g1swo2VK3F+ga0j38IDsimj8Qt1V3lZoEPFV4GfZt86D+OIWV0zQdko1
+/kk+zO/wbja2uxgtu8O9oINbyrCI0HldVEWXv8+ye9NiIVnt3FLWYiJrZJ7iF4hs
+MHRafZySu64OXexOBI0IIa6i20KRSzPImThhbtOXR5Pp6lMcdiBiWCn9DRhDn/Ds
+0hSyOeKDO+GB8Pw7b/ihmpB8b+IeB0u1Z8CTRkFabOUPH5Q0ZxekszphB65pROrL
+bcRDhIJc43lxdp/RltnXWt+yjqyfOO9zQ02STGyfe4ckc/xdEVZ4IlGEej0qNA+J
+jRN0yBDy0824jDVXy4EHAeXH68VReU4XM+xRQJef/6QSKzo9S6R5atzyZJBoBLTO
+liobwH6MITb52zDDWmi8qkTtyaa2Y1W3v6+R0JU/zDG3WclI97aBn6jEq8Yxm243
+3ZOdnMHdJrYVxSwoDYR/zmR7bekSkhX/azbd4I4WqX51JADSJIngocST813f47oX
+CU5hk9o+Oz4PQUI5u/j4tE3fwoRizmsCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEA
+SsN61v0xDIQsXsTSNpiO5x8osdFKopegx3xzgi48pk2GwcjJ6Oy7hg1Jvfsbju1q
+hdYyT4pPFEpEUrKlT31RzgvRk/No7Ck8T6zA4eZj4fnH7n30AhClchmdwSiY8C5Q
+MU0oTvZC7QtAHbOvyDaY5/L1NTJE5ttY/p2zRmSEvOmvxpCjKxvoaYH2KDxsI9qh
+8GtTK2p/M5x4EQSFImTthEsAe40Uq1ss3eTsZc17K0S1YRong99TELOAwSCvRLJG
+7tqu1f1zTjGXlCNdNa4quehpgWTEk5RoAhOfhnu6Swxke192pyi/Ea60zFgqmDEx
+PXzFR+zpIom5CHeLWtKcgKwfNkJ4EHYkUjmJKsulxxE2fG+FU0s/iNUyd1IWtH1y
+1B9yRu5zeB8gSZc2Iypp4pyFYYyw/lVEKJJRZPvRYUyOK2JLdC8QTiWyvG3Aw3lL
+/vd/X5QgxWrOIRlrUIfeoJUd4eBhPdgKWwT4q2g0c6fqHAZKXaXA62enVU9vigRz
+BVFYWO/VGfMa15L6Jfslc0TqW2JODcDhFYRJEelKEtv6q8p1Z31yaQ1TwZRO5D5I
+IxyquzR/jD5EVH41wYSRmO+p4ci7vEr8chGtQSri2jIKkmuhjgPT1taUit33Y1xd
+E1+GDptymFLTQmzlXpjknMj7Wecwl6hpnCNXJV6Wh9s=
+-----END CERTIFICATE-----
diff --git a/ratis-test/src/test/resources/ssl/server.csr 
b/ratis-test/src/test/resources/ssl/server.csr
new file mode 100644
index 0000000..e7daafb
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/server.csr
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+-----BEGIN CERTIFICATE REQUEST-----
+MIIEWTCCAkECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAuceyZa7AKmiqYof4KcEb84MOu+zPmujOpBRLH74B
+c0z05hC0pCLMMVLkEGAkrVBoBHLfeuhoRdr+hKVdnPaNUv5uW2qpyuFU4DG43wEX
+KYeQTleuMDefzx37xgUJNyqFJAnZsu+Hst9raO6TfbJ4g1swo2VK3F+ga0j38IDs
+imj8Qt1V3lZoEPFV4GfZt86D+OIWV0zQdko1/kk+zO/wbja2uxgtu8O9oINbyrCI
+0HldVEWXv8+ye9NiIVnt3FLWYiJrZJ7iF4hsMHRafZySu64OXexOBI0IIa6i20KR
+SzPImThhbtOXR5Pp6lMcdiBiWCn9DRhDn/Ds0hSyOeKDO+GB8Pw7b/ihmpB8b+Ie
+B0u1Z8CTRkFabOUPH5Q0ZxekszphB65pROrLbcRDhIJc43lxdp/RltnXWt+yjqyf
+OO9zQ02STGyfe4ckc/xdEVZ4IlGEej0qNA+JjRN0yBDy0824jDVXy4EHAeXH68VR
+eU4XM+xRQJef/6QSKzo9S6R5atzyZJBoBLTOliobwH6MITb52zDDWmi8qkTtyaa2
+Y1W3v6+R0JU/zDG3WclI97aBn6jEq8Yxm2433ZOdnMHdJrYVxSwoDYR/zmR7bekS
+khX/azbd4I4WqX51JADSJIngocST813f47oXCU5hk9o+Oz4PQUI5u/j4tE3fwoRi
+zmsCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4ICAQCY1/hPvxArJ6XR6DHVnjNBk8Jc
+sMm8QQQT4yPY9liuLiQrvg+rJYkbHbmDtkOF/a6FP8v+0Zea9VOY7lGZAEhGDVOy
+maA7WABV00JFNfozMr9mJiYFeAunJGA7leA3C70uKtyzhUCoZanY+3F3m2tSPkJs
+1fvUjJWxQ/IO/pJCfPS+nWpWP9dDUsKhCYfbrt2W1cKUVlWHhuM2Eup9uHJh5h+d
+5hRA5M2/mc2DyVonGTSh/AWCINJgbmpx/b3/OYlAGKfWZQUgnbDoipxao1iu1b1N
+H20/E7FN1NHU2Pe1F1MnVAvlycicTUmo/SokhA07wgoNarbnLtFheQPjY3HI8m8R
+8ljRZTSmmrEV12wZ4oyNBmp7KCiZwDFmXzSh+EQGo81KC3fiBhDJbtnxG3ZoEcLF
+5rizSLnRCELUFvPTf5Ufj8kJvEshpPTogJfswRCUrc2lLqqujzEhSFBQcqJTyYsk
+1wwtynKwR+WsaYZTAGn6W1oBI6cMbfb54v0W6/naI+ohzS3eaCR8IO1p/YYr/q//
+nJKEIkFsKIgRLUjFBZ3lZ0CKKwp8vTAYkjcjFUWj1Dt8WqBArsZ8MN9dzwDBrOPJ
+RVBfnmrHFbcWmbxUNKPDczxEiTJfGC68V83ejVEzaixZfrEBCmjTzOS8TUCz+esZ
+RhvhWXXlPZSJXZO02A==
+-----END CERTIFICATE REQUEST-----
diff --git a/ratis-test/src/test/resources/ssl/server.key 
b/ratis-test/src/test/resources/ssl/server.key
new file mode 100644
index 0000000..6fed777
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/server.key
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/ratis-test/src/test/resources/ssl/server.pem 
b/ratis-test/src/test/resources/ssl/server.pem
new file mode 100644
index 0000000..7a20975
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/server.pem
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+-----BEGIN PRIVATE KEY-----
+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
+lrugObUwuMHdvaNJN7HsTS9rwAQefA==
+-----END PRIVATE KEY-----

Reply via email to