提交 96b74a3a 编写于 作者: Y yukon 提交者: von gosling

[ROCKETMQ-315][ROCKETMQ-327] Enhance TLS feature (#202)

* Add an interface DecryptionStrategy for SslHelper

* Centralize the TLS related configurations

* Use tls test mode by default

* Add SSL related tests

* Add tls related tests

* Add tls config related unit tests

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