This is an automated email from the ASF dual-hosted git repository.
albumenj pushed a commit to branch 3.3
in repository https://gitbox.apache.org/repos/asf/dubbo.git
The following commit(s) were added to refs/heads/3.3 by this push:
new 9cb26bbb79 add SSL certificate configuration support for HTTP/3
(#14520)
9cb26bbb79 is described below
commit 9cb26bbb79ecd562d6bba3d389b880a943e21fe9
Author: blueuuufish <[email protected]>
AuthorDate: Wed Aug 21 17:40:02 2024 +0800
add SSL certificate configuration support for HTTP/3 (#14520)
* add SSL certificate configuration support for HTTP/3
* add SSL support
* Refine Http3 ssl context
* test
* Recheck test
* 50 years pem files
* Refactored code
---------
Co-authored-by: Sean Yang <[email protected]>
---
.../dubbo/remoting/http3/Http3SslContexts.java | 168 +++++++++++++++++++++
.../netty4/NettyHttp3ConnectionClient.java | 10 +-
.../transport/netty4/NettyHttp3Server.java | 12 +-
.../rpc/protocol/tri/TripleHttp3ProtocolTest.java | 139 +++++++++++++++++
.../src/test/resources/certs/ca.key | 28 ++++
.../src/test/resources/certs/ca.pem | 19 +++
.../src/test/resources/certs/client.key | 28 ++++
.../src/test/resources/certs/client.pem | 18 +++
.../src/test/resources/certs/server.key | 28 ++++
.../src/test/resources/certs/server.pem | 18 +++
10 files changed, 450 insertions(+), 18 deletions(-)
diff --git
a/dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/http3/Http3SslContexts.java
b/dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/http3/Http3SslContexts.java
new file mode 100644
index 0000000000..950fbcad7d
--- /dev/null
+++
b/dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/http3/Http3SslContexts.java
@@ -0,0 +1,168 @@
+/*
+ * 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.dubbo.remoting.http3;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.ssl.AuthPolicy;
+import org.apache.dubbo.common.ssl.Cert;
+import org.apache.dubbo.common.ssl.CertManager;
+import org.apache.dubbo.common.ssl.ProviderCert;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSessionContext;
+
+import java.io.InputStream;
+import java.util.List;
+
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.handler.ssl.ApplicationProtocolNegotiator;
+import io.netty.handler.ssl.ClientAuth;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
+import io.netty.handler.ssl.util.SelfSignedCertificate;
+import io.netty.incubator.codec.http3.Http3;
+import io.netty.incubator.codec.quic.QuicSslContext;
+import io.netty.incubator.codec.quic.QuicSslContextBuilder;
+
+public final class Http3SslContexts extends SslContext {
+
+ private static final ErrorTypeAwareLogger LOGGER =
LoggerFactory.getErrorTypeAwareLogger(Http3SslContexts.class);
+
+ private Http3SslContexts() {}
+
+ public static QuicSslContext buildServerSslContext(URL url) {
+ CertManager certManager = getCertManager(url);
+ ProviderCert cert = certManager.getProviderConnectionConfig(url,
url.toInetSocketAddress());
+ if (cert == null) {
+ return buildSelfSignedServerSslContext(url);
+ }
+ QuicSslContextBuilder builder;
+ try {
+ try (InputStream privateKeyIn = cert.getPrivateKeyInputStream();
+ InputStream keyCertChainIn =
cert.getKeyCertChainInputStream()) {
+ if (keyCertChainIn == null || privateKeyIn == null) {
+ return buildSelfSignedServerSslContext(url);
+ }
+ builder = QuicSslContextBuilder.forServer(
+ toPrivateKey(privateKeyIn, cert.getPassword()),
+ cert.getPassword(),
+ toX509Certificates(keyCertChainIn));
+ try (InputStream trustCertIn = cert.getTrustCertInputStream())
{
+ if (trustCertIn != null) {
+ ClientAuth clientAuth = cert.getAuthPolicy() ==
AuthPolicy.CLIENT_AUTH
+ ? ClientAuth.REQUIRE
+ : ClientAuth.OPTIONAL;
+
builder.trustManager(toX509Certificates(trustCertIn)).clientAuth(clientAuth);
+ }
+ }
+ }
+ } catch (IllegalStateException t) {
+ throw t;
+ } catch (Throwable t) {
+ throw new IllegalArgumentException("Could not find certificate
file or the certificate is invalid.", t);
+ }
+ try {
+ return
builder.applicationProtocols(Http3.supportedApplicationProtocols())
+ .build();
+ } catch (Throwable t) {
+ throw new IllegalStateException("Build SslSession failed.", t);
+ }
+ }
+
+ @SuppressWarnings("DataFlowIssue")
+ private static QuicSslContext buildSelfSignedServerSslContext(URL url) {
+ LOGGER.info("Provider cert not configured, build self signed
sslContext, url=[{}]", url.toString(""));
+ SelfSignedCertificate certificate;
+ try {
+ certificate = new SelfSignedCertificate();
+ } catch (Throwable e) {
+ throw new IllegalStateException("Failed to create self signed
certificate, Please import bcpkix jar", e);
+ }
+ return QuicSslContextBuilder.forServer(certificate.privateKey(), null,
certificate.certificate())
+ .applicationProtocols(Http3.supportedApplicationProtocols())
+ .build();
+ }
+
+ public static QuicSslContext buildClientSslContext(URL url) {
+ CertManager certManager = getCertManager(url);
+ Cert cert = certManager.getConsumerConnectionConfig(url);
+ QuicSslContextBuilder builder = QuicSslContextBuilder.forClient();
+ try {
+ if (cert == null) {
+ LOGGER.info("Consumer cert not configured, build insecure
sslContext, url=[{}]", url.toString(""));
+ builder.trustManager(InsecureTrustManagerFactory.INSTANCE);
+ } else {
+ try (InputStream trustCertIn = cert.getTrustCertInputStream();
+ InputStream privateKeyIn =
cert.getPrivateKeyInputStream();
+ InputStream keyCertChainIn =
cert.getKeyCertChainInputStream()) {
+ if (trustCertIn != null) {
+ builder.trustManager(toX509Certificates(trustCertIn));
+ }
+ builder.keyManager(
+ toPrivateKey(privateKeyIn, cert.getPassword()),
+ cert.getPassword(),
+ toX509Certificates(keyCertChainIn));
+ }
+ }
+ } catch (Throwable t) {
+ throw new IllegalArgumentException("Could not find certificate
file or the certificate is invalid.", t);
+ }
+ try {
+ return
builder.applicationProtocols(Http3.supportedApplicationProtocols())
+ .build();
+ } catch (Throwable t) {
+ throw new IllegalStateException("Build SslSession failed.", t);
+ }
+ }
+
+ private static CertManager getCertManager(URL url) {
+ return
url.getOrDefaultFrameworkModel().getBeanFactory().getBean(CertManager.class);
+ }
+
+ @Override
+ public boolean isClient() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<String> cipherSuites() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public ApplicationProtocolNegotiator applicationProtocolNegotiator() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SSLEngine newEngine(ByteBufAllocator alloc) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int
peerPort) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SSLSessionContext sessionContext() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git
a/dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyHttp3ConnectionClient.java
b/dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyHttp3ConnectionClient.java
index 1395d2e43b..fc4388e75d 100644
---
a/dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyHttp3ConnectionClient.java
+++
b/dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyHttp3ConnectionClient.java
@@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.remoting.RemotingException;
+import org.apache.dubbo.remoting.http3.Http3SslContexts;
import org.apache.dubbo.remoting.utils.UrlUtils;
import java.util.concurrent.atomic.AtomicReference;
@@ -31,14 +32,11 @@ import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPromise;
import io.netty.channel.socket.nio.NioDatagramChannel;
-import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.incubator.codec.http3.Http3;
import io.netty.incubator.codec.http3.Http3ClientConnectionHandler;
import io.netty.incubator.codec.quic.QuicChannel;
import io.netty.incubator.codec.quic.QuicChannelBootstrap;
-import io.netty.incubator.codec.quic.QuicSslContext;
-import io.netty.incubator.codec.quic.QuicSslContextBuilder;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
@@ -62,14 +60,10 @@ public final class NettyHttp3ConnectionClient extends
AbstractNettyConnectionCli
@Override
protected void initBootstrap() throws Exception {
- QuicSslContext context = QuicSslContextBuilder.forClient()
- .trustManager(InsecureTrustManagerFactory.INSTANCE)
- .applicationProtocols(Http3.supportedApplicationProtocols())
- .build();
int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
io.netty.channel.ChannelHandler codec =
Helper.configCodec(Http3.newQuicClientCodecBuilder(), getUrl())
.maxIdleTimeout(idleTimeout, MILLISECONDS)
- .sslContext(context)
+ .sslContext(Http3SslContexts.buildClientSslContext(getUrl()))
.build();
io.netty.channel.Channel nettyDatagramChannel = new Bootstrap()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,
getConnectTimeout())
diff --git
a/dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyHttp3Server.java
b/dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyHttp3Server.java
index 64b44fca97..cd45f5c286 100644
---
a/dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyHttp3Server.java
+++
b/dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyHttp3Server.java
@@ -26,6 +26,7 @@ import org.apache.dubbo.remoting.Channel;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.RemotingException;
import org.apache.dubbo.remoting.http12.netty4.HttpWriteQueueHandler;
+import org.apache.dubbo.remoting.http3.Http3SslContexts;
import org.apache.dubbo.remoting.http3.netty4.NettyHttp3FrameCodec;
import
org.apache.dubbo.remoting.http3.netty4.NettyHttp3ProtocolSelectorHandler;
import org.apache.dubbo.remoting.transport.AbstractServer;
@@ -44,14 +45,11 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
-import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.incubator.codec.http3.Http3;
import io.netty.incubator.codec.http3.Http3ServerConnectionHandler;
import io.netty.incubator.codec.quic.InsecureQuicTokenHandler;
import io.netty.incubator.codec.quic.QuicChannel;
-import io.netty.incubator.codec.quic.QuicSslContext;
-import io.netty.incubator.codec.quic.QuicSslContextBuilder;
import io.netty.incubator.codec.quic.QuicStreamChannel;
import io.netty.util.concurrent.Future;
@@ -88,15 +86,9 @@ public class NettyHttp3Server extends AbstractServer {
NettyHttp3ProtocolSelectorHandler selectorHandler =
new NettyHttp3ProtocolSelectorHandler(getUrl(),
frameworkModel);
- SelfSignedCertificate certificate = new SelfSignedCertificate();
- QuicSslContext context = QuicSslContextBuilder.forServer(
- certificate.privateKey(), null,
certificate.certificate())
- .applicationProtocols(Http3.supportedApplicationProtocols())
- .build();
-
int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
io.netty.channel.ChannelHandler codec =
Helper.configCodec(Http3.newQuicServerCodecBuilder(), getUrl())
- .sslContext(context)
+ .sslContext(Http3SslContexts.buildServerSslContext(getUrl()))
.maxIdleTimeout(idleTimeout, MILLISECONDS)
.tokenHandler(InsecureQuicTokenHandler.INSTANCE)
.handler(new ChannelInitializer<QuicChannel>() {
diff --git
a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp3ProtocolTest.java
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp3ProtocolTest.java
new file mode 100644
index 0000000000..5c5ec54a96
--- /dev/null
+++
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp3ProtocolTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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.dubbo.rpc.protocol.tri;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.common.utils.ClassUtils;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.config.SslConfig;
+import org.apache.dubbo.config.context.ConfigManager;
+import org.apache.dubbo.rpc.Constants;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ConsumerModel;
+import org.apache.dubbo.rpc.model.ModuleServiceRepository;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
+import org.apache.dubbo.rpc.model.ServiceMetadata;
+import org.apache.dubbo.rpc.protocol.tri.support.IGreeter;
+import org.apache.dubbo.rpc.protocol.tri.support.IGreeterImpl;
+import org.apache.dubbo.rpc.protocol.tri.support.MockStreamObserver;
+
+import java.nio.file.Paths;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.rpc.protocol.tri.support.IGreeter.SERVER_MSG;
+
+class TripleHttp3ProtocolTest {
+
+ @Test
+ void testDemoProtocol() throws Exception {
+ IGreeter serviceImpl = new IGreeterImpl();
+ int availablePort = NetUtils.getAvailablePort();
+ ApplicationModel applicationModel = ApplicationModel.defaultModel();
+ ConfigManager configManager =
applicationModel.getApplicationConfigManager();
+
+ SslConfig sslConfig = new SslConfig();
+ sslConfig.setScopeModel(applicationModel);
+
+
sslConfig.setServerKeyCertChainPath(getAbsolutePath("/certs/server.pem"));
+
sslConfig.setServerPrivateKeyPath(getAbsolutePath("/certs/server.key"));
+
sslConfig.setServerTrustCertCollectionPath(getAbsolutePath("/certs/ca.pem"));
+
sslConfig.setClientKeyCertChainPath(getAbsolutePath("/certs/client.pem"));
+
sslConfig.setClientPrivateKeyPath(getAbsolutePath("/certs/client.key"));
+
sslConfig.setClientTrustCertCollectionPath(getAbsolutePath("/certs/ca.pem"));
+
+ configManager.setSsl(sslConfig);
+
+ URL providerUrl = URL.valueOf("tri://127.0.0.1:" + availablePort + "/"
+ IGreeter.class.getName());
+
+ providerUrl = providerUrl.addParameter(Constants.HTTP3_KEY, "true");
+ ModuleServiceRepository serviceRepository =
+ applicationModel.getDefaultModule().getServiceRepository();
+ ServiceDescriptor serviceDescriptor =
serviceRepository.registerService(IGreeter.class);
+
+ ProviderModel providerModel = new ProviderModel(
+ providerUrl.getServiceKey(),
+ serviceImpl,
+ serviceDescriptor,
+ new ServiceMetadata(),
+ ClassUtils.getClassLoader(IGreeter.class));
+ serviceRepository.registerProvider(providerModel);
+ providerUrl = providerUrl.setServiceModel(providerModel);
+
+ Protocol protocol = new
TripleProtocol(providerUrl.getOrDefaultFrameworkModel());
+ ProxyFactory proxy =
+
applicationModel.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ Invoker<IGreeter> invoker = proxy.getInvoker(serviceImpl,
IGreeter.class, providerUrl);
+ Exporter<IGreeter> export = protocol.export(invoker);
+ URL consumerUrl = URL.valueOf("tri://127.0.0.1:" + availablePort + "/"
+ IGreeter.class.getName());
+
+ ConsumerModel consumerModel =
+ new ConsumerModel(consumerUrl.getServiceKey(), null,
serviceDescriptor, null, null, null);
+ consumerUrl = consumerUrl.setServiceModel(consumerModel);
+ consumerUrl = consumerUrl.addParameter(Constants.HTTP3_KEY, "true");
+ IGreeter greeterProxy = proxy.getProxy(protocol.refer(IGreeter.class,
consumerUrl));
+ Thread.sleep(1000);
+
+ // 1. test unaryStream
+ String REQUEST_MSG = "hello world";
+ Assertions.assertEquals(REQUEST_MSG, greeterProxy.echo(REQUEST_MSG));
+ Assertions.assertEquals(REQUEST_MSG,
serviceImpl.echoAsync(REQUEST_MSG).get());
+
+ // 2. test serverStream
+ MockStreamObserver outboundMessageSubscriber1 = new
MockStreamObserver();
+ greeterProxy.serverStream(REQUEST_MSG, outboundMessageSubscriber1);
+ outboundMessageSubscriber1.getLatch().await(3000,
TimeUnit.MILLISECONDS);
+ Assertions.assertEquals(outboundMessageSubscriber1.getOnNextData(),
REQUEST_MSG);
+ Assertions.assertTrue(outboundMessageSubscriber1.isOnCompleted());
+
+ // 3. test bidirectionalStream
+ MockStreamObserver outboundMessageSubscriber2 = new
MockStreamObserver();
+ StreamObserver<String> inboundMessageObserver =
greeterProxy.bidirectionalStream(outboundMessageSubscriber2);
+ inboundMessageObserver.onNext(REQUEST_MSG);
+ inboundMessageObserver.onCompleted();
+ outboundMessageSubscriber2.getLatch().await(3000,
TimeUnit.MILLISECONDS);
+ // verify client
+ Assertions.assertEquals(outboundMessageSubscriber2.getOnNextData(),
SERVER_MSG);
+ Assertions.assertTrue(outboundMessageSubscriber2.isOnCompleted());
+ // verify server
+ MockStreamObserver serverOutboundMessageSubscriber =
+ (MockStreamObserver) ((IGreeterImpl)
serviceImpl).getMockStreamObserver();
+ serverOutboundMessageSubscriber.getLatch().await(1000,
TimeUnit.MILLISECONDS);
+ Assertions.assertEquals(REQUEST_MSG,
serverOutboundMessageSubscriber.getOnNextData());
+ Assertions.assertTrue(serverOutboundMessageSubscriber.isOnCompleted());
+
+ export.unexport();
+ protocol.destroy();
+ // resource recycle.
+ serviceRepository.destroy();
+ System.out.println("serviceRepository destroyed");
+ }
+
+ private String getAbsolutePath(String resourcePath) throws Exception {
+ java.net.URL resourceUrl =
TripleHttp3ProtocolTest.class.getResource(resourcePath);
+ Assertions.assertNotNull(resourceUrl, "Cert file '/certs/" +
resourcePath + " is required");
+ return Paths.get(resourceUrl.toURI()).toAbsolutePath().toString();
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/ca.key
b/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/ca.key
new file mode 100644
index 0000000000..129f9c5186
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/ca.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDUGNg9BSrAuIc7
+D2FjD+1m9EjMUA27GNExgUfvmOAsMK1XrZRTaRTJPU4jXFJo9x6cuk+c7Gceggtz
+4ZfCRXXw/45uDj0MXeKO8kKXKglNfrB7gfD9HYlGuTx8HxJ0X5KuMHZlGf8L/wp/
+0tVowlutp8CEPECgHrsRLP8Im3gGZfue+nzouaul33C53EnwK2p8zqfREwZFTPSA
+5fkV1/wXP0JIixpUY+KQrJ6rgJNP6/pvxHxJDgwDYadOgkWP0t5IWGsYu53zTSK8
+H/4lN5m9mVyZpf1eg0H6A9HkN43nhhBp/5hMWNJAE600qCKPU4fldVVUcbkp9Jir
+SNey66o9AgMBAAECggEAB0P2nFiB92oaz84eEuunwUAbAky3u7IE8IIyOaXt//WG
+6pr6msTdvmsIMzPlas0UUmAbfA+pFKV1yPlwXmpMuP24HlMEiljo+uaXm2AIQ4xU
+dValL7aETlToGoRh+HSRrL/iPmJel0WcUXaict09kLxPXvhFanbDB7qX3e6ddPKX
+Z8FAtS4bRupS9sj3eYT1emgTLz905JGPXsotE2Ou+1ZMz59RFuL6ai26w6kIX5gZ
+UUwSD4QY5ZQ8uxb4WlicmN9kaOG+7OnF5dVO46xaeopmEiCRvux168+6LzdR+Q2C
+8PEpQt0yFtVIwE+fE00gXYjKijxgHzn0kUixrcFeTQKBgQD2YQepIjcZLs6y+mxM
+JNo3+I3BcXpFMeW2Ldi5lxUeTmqFxmbb4BsPdgnBpnMqwyLiGMdfTnNm+3ptajIV
+JWaFHXyt8JyxfYYxpw6QDudpeCkhlfq9Fl2mZeX3xN9xcHqbaOCveZ8GrcJ2a5U+
+iQskArqDO9xuaDdv5WfpiSco6wKBgQDcYRvwk3H8SJYOso9SAgCxDK+OiFjERAr/
+y6JqiNLJ06G/XjUKFzoxu2dYeJbP551RMjn3Db5KEiaXlpP8uTkKbkCCoKJPP/0g
+ys9wBx6RUuh2tOoYxVRg0k2+4B0FgJFxwoGF9Ip6Q5fYwYFXxmb9EXb2smu+bgf8
+usmsFn2vdwKBgQCeMe8ZSj7WjJdrlB+1RHRZO97JNnOruj4km4tsnvWQYAlFm/6v
+b1Xwt3nMPXP1IRZk1kQtjdHLbe1OE8HUkg8hqa1EghoaXN6tQva2yNsNfXnrdsFo
+Z9P547OlQ2O1EuOmpdCe2xig0TDrmGhbnuLoXFglq2n9+iyeTE5MRSQ/wQKBgC1G
+Zg8Ou6Qw5WMvm/IlJy84g/isWlA4Cmpy03M/mM6OA/TkzdC85N/CuICchZ9A3jkI
+MJBo6mcdQ4BxFwYcp3NVIzuy9KWDZ+WMxQEm9Ui117xLfqyIHcWmbwJ/2HaA62CL
+rUBv9OMZb+DAIGqAADiNlfsovKnqwQlS9ou8MyYRAoGAGW4dgYoQm8UxNzhFzvnc
+C+9ViPdCw4pb4cFFyQn1YwSL4hUTdFj1zhOf1JMXEujEboMRw/L744InT/1zswgt
+02/ZgUylww71N9nP773EDkJYC0DLEMQGKxji3fcOiPg/qq7dkoH8NPok2AKpPWxj
+GjnRPy8XMHUWTMTherHIsiE=
+-----END PRIVATE KEY-----
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/ca.pem
b/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/ca.pem
new file mode 100644
index 0000000000..ca0e341435
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/ca.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDBzCCAe+gAwIBAgIUE8rx3Vh8pCFm0a4RO/jdmtMzTJswDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHcXVpYy1jYTAgFw0yNDA4MjEwNjM3NTlaGA8yMDc0MDgw
+OTA2Mzc1OVowEjEQMA4GA1UEAwwHcXVpYy1jYTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBANQY2D0FKsC4hzsPYWMP7Wb0SMxQDbsY0TGBR++Y4CwwrVet
+lFNpFMk9TiNcUmj3Hpy6T5zsZx6CC3Phl8JFdfD/jm4OPQxd4o7yQpcqCU1+sHuB
+8P0diUa5PHwfEnRfkq4wdmUZ/wv/Cn/S1WjCW62nwIQ8QKAeuxEs/wibeAZl+576
+fOi5q6XfcLncSfAranzOp9ETBkVM9IDl+RXX/Bc/QkiLGlRj4pCsnquAk0/r+m/E
+fEkODANhp06CRY/S3khYaxi7nfNNIrwf/iU3mb2ZXJml/V6DQfoD0eQ3jeeGEGn/
+mExY0kATrTSoIo9Th+V1VVRxuSn0mKtI17Lrqj0CAwEAAaNTMFEwHQYDVR0OBBYE
+FAQr8dTHZueMEw3eIEyMBYrTFjptMB8GA1UdIwQYMBaAFAQr8dTHZueMEw3eIEyM
+BYrTFjptMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBALNTH7Hx
+l2nUxn68AHagnzYBGgGzJDmGGZU5tNuzPSTUnCDe+6x527KQsRMJ8gLBbAYzef07
+98JK3SDKyl1AHus6oYBavtrE3D2vQXbCZSHR5BkdNsuwAxnqB+1qxfFCR3GUPry5
++KocqHHG1kBApHeSYM43mAbZ2bfo9BAHAu/xq4JDc8Hdm6bwFkdMsvkOZJAVtEYi
+m+ARw6XvWOsp9atRnhzfCBBhpayl6NtsnNth1wY/FuSmm2ZUlDVMsa3hjSnY0/E/
+aV9Mi1WvgGklc5efveGufoIYHy/twYwUtkYaz6Z1Z+TFoC2tTRjlXjESTN/ti+Un
+XRMjTKn90G9c5bQ=
+-----END CERTIFICATE-----
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/client.key
b/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/client.key
new file mode 100644
index 0000000000..1297f49fd1
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/client.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC1jcPcCCtgysUO
+YvtQkFU7OPAPUqP69M9pHvB61mGomrCfpeuzc2XcdnyDa/FBITH6Czd8p88eteXa
++LpgOcrU+W93ziOLHXwS1mtETOSxfzK+HQuwcUfhEGs/0HUc1wEfWeWldGaGri0C
+OAtKI/vzqFaaspK+L4I++UvPO9aiypQ3WEwVlLgHnXckQl4fx8fi2HZ0wGKR7OoL
+KbVITlKopN0QqRnuA1L8vohrOf8P51KioAWBDUsWJ54cnrla09NPEkEKu4DHz6aO
+GJTRYttsXQwNIPUeq6z+4GzVFDih9sEI3rQFTQgXSLiUBLKqrjYnqF8vR8Yru3Jd
+fvifyr1zAgMBAAECggEAMIC9rRhfol25lHTcKDNPGeHFvIo69c4WZUKXQ3HnqDgf
+bJ9Sn1/6fBIJhMC/NMOuCtuZSMu6GT3GOSBPBmsaJZi3711agI1xesTe5mHWjRbq
+0wCGgW5aIiYVSPijeP91tM76+59jzqJUsu2PEpEXfApijLSdYILnvHQ0jRoy5s1c
+rGJEZbOwmL7z6w8S2fpnmvZyRum3khv+d4v98MHXKL4FiygUJPLSbb37UYeCDXzz
+5Q/KrLTk6u6mZm6FLLbFq0yKqatM4AADkLMNrAZD5OZ+wo9bbrVDTMZT08DWXMAk
+sRrGAIHI9HSZPH9/iXuW7vTBMac5pcpxfa0AN2X2BQKBgQD5fK/bNtyEYP3l4TLq
+2KGpxJnRIIJvHNIlZ5X9bXEngR4mFXMsA7RtiCIRETlSR+rbaXgFbIW5WV1rDQW8
+Cur/EVfvmL/R5Yqx5yEjFMs2cUhkAfh7iaxvwvG4/Q6EyL/P6jPWq74wHXOkuUWL
+0okPS3vKqT1FgtcF8yiA/zuRPQKBgQC6SxUYox8D/F7FHehF/LbtbjsPX5Lfa9dG
+OJ1AZrT18/YCXBN77Qc9ACjpb3gwiumIWiO6NiaPs0cP28mgUtAIYzbT7JWu4SUn
+LG/anYer8+r0IgY9JolJpa4rTrAOOtWz38fwGQd8Z8rVGQQe5df1f0943HKh3Wdp
+woUcgfYUbwKBgEcL6exsUBnmoQcgvWZBcLAuy3rRXjOUKof6Wq1DyFBupY5E13p2
+R6BVfxYLKJ602j3HaCVrRR2GRGMi3zowhcFDSB50ClQUoQ2Oe1JJLqF/WxUXtpI/
+n3poMnvynHrVzEHCOSbt24hKKs+C7zolJ2DSpxsMXOV5oBASsbsic+0lAoGBAIBe
+zKK1hOHSPdvGyA+yHLtvgfMOAL9EKIuS54lBPOFewt7NY/+5TYuTWJ3C2idZEaOL
+uaLPPbM37nfXFBEN/5xIAbf3Bw4Cxv8/d7RFaP/sjLK9316z91ZwfJwLFPY5RJk2
+Uyf3l1j6jKJbIqPH7hCVMcYu4i73yhLun5sNUuyxAoGBAJTh0qK3MJPFFM0P5zqJ
+gZsrQZi6DAawFiHnNdab99npti07zUvNtgcbdszLfcpHvYSMI1Xpg+hZqC1BifIj
+hAQdZIc3APnGeCghjemDg2NWa2yHcqkKNVkVB7647bgsKP1ewg0GFFLN17XiUfiv
+6/NvZACAHJCj0YqQUpJ8dsEK
+-----END PRIVATE KEY-----
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/client.pem
b/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/client.pem
new file mode 100644
index 0000000000..80c8291a59
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/client.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC+jCCAeKgAwIBAgIUFrQFPMAmmmGTMmlyK4+r3aaCRw0wDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHcXVpYy1jYTAgFw0yNDA4MjEwNjM4MjZaGA8yMDc0MDgw
+OTA2MzgyNlowFjEUMBIGA1UEAwwLcXVpYy1jbGllbnQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQC1jcPcCCtgysUOYvtQkFU7OPAPUqP69M9pHvB61mGo
+mrCfpeuzc2XcdnyDa/FBITH6Czd8p88eteXa+LpgOcrU+W93ziOLHXwS1mtETOSx
+fzK+HQuwcUfhEGs/0HUc1wEfWeWldGaGri0COAtKI/vzqFaaspK+L4I++UvPO9ai
+ypQ3WEwVlLgHnXckQl4fx8fi2HZ0wGKR7OoLKbVITlKopN0QqRnuA1L8vohrOf8P
+51KioAWBDUsWJ54cnrla09NPEkEKu4DHz6aOGJTRYttsXQwNIPUeq6z+4GzVFDih
+9sEI3rQFTQgXSLiUBLKqrjYnqF8vR8Yru3Jdfvifyr1zAgMBAAGjQjBAMB0GA1Ud
+DgQWBBQl6Ogm/VZ8q41hpdeUr7HgZY0mCDAfBgNVHSMEGDAWgBQEK/HUx2bnjBMN
+3iBMjAWK0xY6bTANBgkqhkiG9w0BAQsFAAOCAQEAqD1x6VU7Ugm4Dk55dXu/Raf6
+yKWkKXzFnBukkdoaP/TO9ba/K4+l2jC7lNQ62ROTjZ0CQvRbO6Eg9eoil9bEa7Af
+YayVG1zOJmpHzFg/XXYPSfqZXvEWiwxU6kSpx+eUtFx4pXVecqiySyAcQBge2aGh
+0IOW86oqIcTVSA0l7ROY+vWoHOVZBvS2ujJ43z29k6/4SbmYJL6cLeqzgXnBwHKW
+kND/LIUOd9OrHgP3CTiM0LjwFPxGg9C8OiUymmMqbX4D2gPj0z+al9Orr0VGMHgF
+Sw4h3ovTCeU4nq+iwBc5gwI+U0BuwLFqE4emdz4s2CatWQQ8YcVCBG3eS+nHWQ==
+-----END CERTIFICATE-----
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/server.key
b/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/server.key
new file mode 100644
index 0000000000..7693b31ba0
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/server.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC/wX9dQdJmAA1b
+GCaLV09Fve9gLJc8/o9ERCpQyV2fCEJmWXYEzS1n8z0k3MAGxLF8pw98K6A8J7/6
+lWB0f4edsgMu05zUco/fu9nMIHnnVSEXVcEDxh1E9LcPCWKLyukPSJy1eW8VJtxA
+R/sTyZUK6u7fWLQW/yp9KPdOvicv4ynHDv/S1BCUjH/N/bv8Lwc4a0U/QArlOSZ8
+CJfNWNV2gCtJtQLJZWK27qrLLMYO/A4ZgnJ79ssCaaO6KxXKmETW5y+Q3+aawLdT
+0jrIcjwhmWAok7RgLeIYT1fK8QM2ON5y4efJIH116XK1B//Mcyx8ymunNcPpPPrx
+MeT/ibmbAgMBAAECggEALacw3pAUOoqao2x9iMExs2YD/r6/BxuIGnWsKW23p2w8
++scjzQLYY55KmWMhKLtdklLn4eU1Ef1YvJ0hyLqwHABMt7JT7VSVImxD71BwFX50
+EW0uNerN/yPQDXlrh+K4WBYukRonMz78QyWgBlcA8Ad89ZnVzn/Tqta9AndNNx4R
+cSpsZE0raZXlPoQDN2M0BE2l3by0Yu1qvB04IoL7gI4hm9AjKTPqUG34bax6aME0
+hzvKIKoqWeZ5OCnv7hxq99sgCYW1wDG5XJm3zKO67vxX19ivN3ARv+pwuYyG0U9d
+BPi7Msgq1ZI97OViwJB3x6VL5h4a1Fw3qnLNHg1bIQKBgQDwoKsWxFPJc34d4kV4
+neTbiweLhwRdfiDG0OoeJFETcjucWrVi0fUkADCaS7L/WyvHgWbwV5Ywq+Z6NhWv
+tLxeR4LQtt+9rWSHl/cWZyUO8gVmwbxR2DdKF6R4G7LZWZHSPeBgAThp/QbK136d
+UrzSVjO2VhYE0PI6CyAT0VKUywKBgQDMAY78fLb723wqS0ENteWScW1WVeVfvM8j
+UHyqEM2kyWvqkXMMm9pO84J55yuLtw1WneCmcY0ydqggGq6KhlciEG6xPMaInW7u
+P2xa1FKuDUhbUPIUBWhCUzEr+k0auImboedshTB0Pt5dhcUcvv7yq1ZZxl5880Kq
+FPhHQ8ykcQKBgDYUVf9K5m3LGBgNR4HBMrMovuxbzt5YP3OPdl6J2PLe5IjSVhu8
+hjSuGj5DLxp22hL/gSwY8zdDGwxyZVNU8lTaC4tu3kAZ3RMgUzbkvY5rVE1w0yst
+xddQEvWve/WfTMeMgoXxIEWpyUIDRoCDHM7E8uBS3qX8c8QZxf7ON5obAoGASYQS
+Q/ipdE9fClq+IbO3/DL15NPlab+/oYlCs4KEqOqRAiHSJOwRGrxz+3wL3DhmDR2t
+tC1dZZ8O7SrQVUkeIz9qtGIjZV3eeOrQIFc9SE2vEoE0C3T7YlNad++mU4fGxxfs
+LtY8ZNe3CFrJcpd9hnOqFYX/zOVY07Pk2MGdAHECgYBTm0Bok9sI5s6sG29Jm1I4
+/dE4qsZoAjsoCwzJp+9G17e5aqqiQGczOYf7vd8lxjH6JhUowVw+H7AC1JCOv/ol
+yhDkpAKAbeF135TTTXCLzXPNQpuPBuaZvZHaFauAlpKiTDEU0Ggr2/bw0FXr2VXJ
+PAlImidzzicjm0wIGBb5KA==
+-----END PRIVATE KEY-----
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/server.pem
b/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/server.pem
new file mode 100644
index 0000000000..6c7cd60b42
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/server.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC+jCCAeKgAwIBAgIUFrQFPMAmmmGTMmlyK4+r3aaCRwwwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHcXVpYy1jYTAgFw0yNDA4MjEwNjM4MDdaGA8yMDc0MDgw
+OTA2MzgwN1owFjEUMBIGA1UEAwwLcXVpYy1zZXJ2ZXIwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQC/wX9dQdJmAA1bGCaLV09Fve9gLJc8/o9ERCpQyV2f
+CEJmWXYEzS1n8z0k3MAGxLF8pw98K6A8J7/6lWB0f4edsgMu05zUco/fu9nMIHnn
+VSEXVcEDxh1E9LcPCWKLyukPSJy1eW8VJtxAR/sTyZUK6u7fWLQW/yp9KPdOvicv
+4ynHDv/S1BCUjH/N/bv8Lwc4a0U/QArlOSZ8CJfNWNV2gCtJtQLJZWK27qrLLMYO
+/A4ZgnJ79ssCaaO6KxXKmETW5y+Q3+aawLdT0jrIcjwhmWAok7RgLeIYT1fK8QM2
+ON5y4efJIH116XK1B//Mcyx8ymunNcPpPPrxMeT/ibmbAgMBAAGjQjBAMB0GA1Ud
+DgQWBBTGNCm9e2SitiqMincY+d3xjexS9zAfBgNVHSMEGDAWgBQEK/HUx2bnjBMN
+3iBMjAWK0xY6bTANBgkqhkiG9w0BAQsFAAOCAQEAUuPFchVrD/zTsww7voBwBm9f
+Jv3Ix87feArTof0hQ3YVPhPhDwPVxSHOp2SGZ2HiqrFPyAsFDgUn7VFjdeVZxEr5
+E090UsoWCEikHp49aw9jN8IIKEvguP2AiIdBfl4wa/We5y9CtiLvcEHWk+MdB7dX
+leX1HPFyWgCqm2JIknITV1ZYpmeovAzMz3Qh+IJWAub+6ANYA9F1CkKoRVy9Guio
+g35+8my2PZtS8dP60Ef4YmoSQ0D/WMgwscrpUScdKzcnybsWk3OCKS2MY+joUQp+
+6AhkeB8Jafgmb7Zh8PINdJEM7Ab/Mc8pf11ghYUeWYM+1jEgOI0BzPijMCx6bQ==
+-----END CERTIFICATE-----