This is an automated email from the ASF dual-hosted git repository.
yuzhou pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/rocketmq.git
The following commit(s) were added to refs/heads/develop by this push:
new 4a29b7e0bf [ISSUE #10077] Support password-encrypted private keys for
Proxy TLS (#10078)
4a29b7e0bf is described below
commit 4a29b7e0bf145be4fdb3001295f1bab520f67d84
Author: majialong <[email protected]>
AuthorDate: Fri Mar 13 14:27:49 2026 +0800
[ISSUE #10077] Support password-encrypted private keys for Proxy TLS
(#10078)
* [ISSUE #10077] Support password-encrypted private keys for Proxy TLS
* Fix bazel error
* Fix unit test
* Add comments
---
docs/cn/Configuration_TLS.md | 43 ++++++++++++++++-
docs/en/Configuration_TLS.md | 41 +++++++++++++++-
proxy/BUILD.bazel | 2 +-
.../apache/rocketmq/proxy/config/ProxyConfig.java | 9 ++++
.../proxy/grpc/ProxyAndTlsProtocolNegotiator.java | 4 +-
.../proxy/remoting/RemotingProtocolServer.java | 2 +
.../grpc/ProxyAndTlsProtocolNegotiatorTest.java | 55 +++++++++++++++++++++-
proxy/src/test/resources/certs/client.key | 17 +++++++
proxy/src/test/resources/certs/client.pem | 19 ++++++++
proxy/src/test/resources/certs/server.key | 16 +++++++
proxy/src/test/resources/certs/server.pem | 19 ++++++++
11 files changed, 221 insertions(+), 6 deletions(-)
diff --git a/docs/cn/Configuration_TLS.md b/docs/cn/Configuration_TLS.md
index 46daf5d3ef..fe83710db9 100644
--- a/docs/cn/Configuration_TLS.md
+++ b/docs/cn/Configuration_TLS.md
@@ -126,6 +126,45 @@ public class ExampleProducer {
// Send messages as usual.
producer.shutdown();
- }
+ }
}
-```
\ No newline at end of file
+```
+
+## 5 Proxy TLS 配置
+
+RocketMQ Proxy 使用 `rmq-proxy.json`(而非 `tls.properties`)进行 TLS 配置。Proxy 的 gRPC
和 Remoting 协议端口均支持 TLS。
+
+### 5.1 配置 rmq-proxy.json
+
+在 `distribution/conf/rmq-proxy.json` 中添加 TLS 相关字段:
+
+```json
+{
+ "rocketMQClusterName": "DefaultCluster",
+ "tlsTestModeEnable": false,
+ "tlsKeyPath": "/opt/certFiles/server.key",
+ "tlsKeyPassword": "123456",
+ "tlsCertPath": "/opt/certFiles/server.pem"
+}
+```
+
+| 字段 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| `tlsTestModeEnable` | boolean | `true` | 是否使用自签名测试证书,生产环境需设为 `false` |
+| `tlsKeyPath` | string | `${PROXY_HOME}/conf/tls/rocketmq.key` |
服务端私钥文件路径(PKCS#8 PEM 格式) |
+| `tlsKeyPassword` | string | `""` | 加密私钥的密码,私钥未加密时留空 |
+| `tlsCertPath` | string | `${PROXY_HOME}/conf/tls/rocketmq.crt` |
服务端证书链文件路径(X.509 PEM 格式) |
+| `tlsCertWatchIntervalMs` | int | `3600000` | 证书文件变更检测间隔(毫秒) |
+
+### 5.2 配置 Proxy 启动参数
+
+编辑 `runproxy.sh`(或启动 Proxy 的脚本),启用 TLS enforcing 模式:
+
+```shell
+JAVA_OPT="${JAVA_OPT} -Dtls.server.mode=enforcing"
+```
+
+三种 TLS 模式说明:
+- `disabled` - 不支持 TLS,拒绝所有 TLS 握手请求
+- `permissive` - TLS 可选,同时接受 TLS 和非 TLS 连接
+- `enforcing` - 强制 TLS,拒绝非 TLS 连接
\ No newline at end of file
diff --git a/docs/en/Configuration_TLS.md b/docs/en/Configuration_TLS.md
index f9716f92f5..54a63f4ab1 100644
--- a/docs/en/Configuration_TLS.md
+++ b/docs/en/Configuration_TLS.md
@@ -118,6 +118,45 @@ public class ExampleProducer {
// Send messages as usual.
producer.shutdown();
- }
+ }
}
```
+
+## 5 Proxy TLS Configuration
+
+RocketMQ Proxy uses `rmq-proxy.json` (not `tls.properties`) for TLS
configuration. The proxy supports TLS for both its gRPC and Remoting protocol
endpoints.
+
+### 5.1 Configure rmq-proxy.json
+
+Add TLS-related fields to `distribution/conf/rmq-proxy.json`:
+
+```json
+{
+ "rocketMQClusterName": "DefaultCluster",
+ "tlsTestModeEnable": false,
+ "tlsKeyPath": "/opt/certFiles/server.key",
+ "tlsKeyPassword": "123456",
+ "tlsCertPath": "/opt/certFiles/server.pem"
+}
+```
+
+| Field | Type | Default | Description |
+|-------|------|---------|-------------|
+| `tlsTestModeEnable` | boolean | `true` | Use self-signed certificates for
testing. Set to `false` for production. |
+| `tlsKeyPath` | string | `${PROXY_HOME}/conf/tls/rocketmq.key` | Path to the
server private key file (PKCS#8 PEM format). |
+| `tlsKeyPassword` | string | `""` | Password for the encrypted private key.
Leave empty if the key is not encrypted. |
+| `tlsCertPath` | string | `${PROXY_HOME}/conf/tls/rocketmq.crt` | Path to the
server certificate chain file (X.509 PEM format). |
+| `tlsCertWatchIntervalMs` | int | `3600000` | Interval in milliseconds to
check for certificate file changes. |
+
+### 5.2 Update Proxy JVM parameters
+
+Edit `runproxy.sh` (or the script that launches the proxy) to enable TLS
enforcing mode:
+
+```shell
+JAVA_OPT="${JAVA_OPT} -Dtls.server.mode=enforcing"
+```
+
+The three available TLS modes are:
+- `disabled` - TLS is not supported; incoming TLS handshakes are rejected.
+- `permissive` - TLS is optional; the proxy accepts both TLS and non-TLS
connections.
+- `enforcing` - TLS is required; non-TLS connections are rejected.
diff --git a/proxy/BUILD.bazel b/proxy/BUILD.bazel
index 5d7fe24435..c0655ec698 100644
--- a/proxy/BUILD.bazel
+++ b/proxy/BUILD.bazel
@@ -75,7 +75,7 @@ java_library(
"src/test/resources/rmq-proxy-home/conf/broker.conf",
"src/test/resources/rmq-proxy-home/conf/logback_proxy.xml",
"src/test/resources/rmq-proxy-home/conf/rmq-proxy.json",
- ],
+ ] + glob(["src/test/resources/certs/*.pem"]) +
glob(["src/test/resources/certs/*.key"]),
visibility = ["//visibility:public"],
deps = [
"//auth",
diff --git
a/proxy/src/main/java/org/apache/rocketmq/proxy/config/ProxyConfig.java
b/proxy/src/main/java/org/apache/rocketmq/proxy/config/ProxyConfig.java
index cc75947966..d44b82aff5 100644
--- a/proxy/src/main/java/org/apache/rocketmq/proxy/config/ProxyConfig.java
+++ b/proxy/src/main/java/org/apache/rocketmq/proxy/config/ProxyConfig.java
@@ -81,6 +81,7 @@ public class ProxyConfig implements ConfigFile {
*/
private boolean tlsTestModeEnable = true;
private String tlsKeyPath = ConfigurationManager.getProxyHome() +
"/conf/tls/rocketmq.key";
+ private String tlsKeyPassword = "";
private String tlsCertPath = ConfigurationManager.getProxyHome() +
"/conf/tls/rocketmq.crt";
private int tlsCertWatchIntervalMs = 60 * 60 * 1000; // 1 hour
/**
@@ -501,6 +502,14 @@ public class ProxyConfig implements ConfigFile {
this.tlsKeyPath = tlsKeyPath;
}
+ public String getTlsKeyPassword() {
+ return tlsKeyPassword;
+ }
+
+ public void setTlsKeyPassword(String tlsKeyPassword) {
+ this.tlsKeyPassword = tlsKeyPassword;
+ }
+
public String getTlsCertPath() {
return tlsCertPath;
}
diff --git
a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/ProxyAndTlsProtocolNegotiator.java
b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/ProxyAndTlsProtocolNegotiator.java
index 43e2c8ae34..4222dacaad 100644
---
a/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/ProxyAndTlsProtocolNegotiator.java
+++
b/proxy/src/main/java/org/apache/rocketmq/proxy/grpc/ProxyAndTlsProtocolNegotiator.java
@@ -123,12 +123,14 @@ public class ProxyAndTlsProtocolNegotiator implements
InternalProtocolNegotiator
} else {
String tlsCertPath =
ConfigurationManager.getProxyConfig().getTlsCertPath();
String tlsKeyPath =
ConfigurationManager.getProxyConfig().getTlsKeyPath();
+ String tlsKeyPassword =
ConfigurationManager.getProxyConfig().getTlsKeyPassword();
try (InputStream serverKeyInputStream = Files.newInputStream(
Paths.get(tlsKeyPath));
InputStream serverCertificateStream = Files.newInputStream(
Paths.get(tlsCertPath))) {
sslContext = GrpcSslContexts.forServer(serverCertificateStream,
- serverKeyInputStream)
+ serverKeyInputStream,
+ StringUtils.isNotBlank(tlsKeyPassword) ?
tlsKeyPassword : null)
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.clientAuth(ClientAuth.NONE)
.build();
diff --git
a/proxy/src/main/java/org/apache/rocketmq/proxy/remoting/RemotingProtocolServer.java
b/proxy/src/main/java/org/apache/rocketmq/proxy/remoting/RemotingProtocolServer.java
index da130769ab..a01c23fce6 100644
---
a/proxy/src/main/java/org/apache/rocketmq/proxy/remoting/RemotingProtocolServer.java
+++
b/proxy/src/main/java/org/apache/rocketmq/proxy/remoting/RemotingProtocolServer.java
@@ -118,6 +118,8 @@ public class RemotingProtocolServer implements
StartAndShutdown, RemotingProxyOu
System.setProperty(TlsSystemConfig.TLS_SERVER_CERTPATH,
config.getTlsCertPath());
TlsSystemConfig.tlsServerKeyPath = config.getTlsKeyPath();
System.setProperty(TlsSystemConfig.TLS_SERVER_KEYPATH,
config.getTlsKeyPath());
+ TlsSystemConfig.tlsServerKeyPassword = config.getTlsKeyPassword();
+ System.setProperty(TlsSystemConfig.TLS_SERVER_KEYPASSWORD,
config.getTlsKeyPassword());
this.tlsCertificateManager = tlsCertificateManager;
this.tlsReloadHandler = new RemotingTlsReloadHandler();
diff --git
a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/ProxyAndTlsProtocolNegotiatorTest.java
b/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/ProxyAndTlsProtocolNegotiatorTest.java
index 9034da7f36..2509f26b44 100644
---
a/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/ProxyAndTlsProtocolNegotiatorTest.java
+++
b/proxy/src/test/java/org/apache/rocketmq/proxy/grpc/ProxyAndTlsProtocolNegotiatorTest.java
@@ -20,8 +20,15 @@ import io.grpc.Attributes;
import io.grpc.netty.shaded.io.netty.buffer.ByteBuf;
import io.grpc.netty.shaded.io.netty.buffer.Unpooled;
import io.grpc.netty.shaded.io.netty.handler.codec.haproxy.HAProxyTLV;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
import org.apache.rocketmq.proxy.config.ConfigurationManager;
+import org.apache.rocketmq.proxy.config.ProxyConfig;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,6 +46,15 @@ public class ProxyAndTlsProtocolNegotiatorTest {
negotiator = new ProxyAndTlsProtocolNegotiator();
}
+ @After
+ public void tearDown() {
+ ProxyConfig proxyConfig = ConfigurationManager.getProxyConfig();
+ proxyConfig.setTlsTestModeEnable(true);
+ proxyConfig.setTlsKeyPath("");
+ proxyConfig.setTlsCertPath("");
+ proxyConfig.setTlsKeyPassword("");
+ }
+
@Test
public void handleHAProxyTLV() {
ByteBuf content = Unpooled.buffer();
@@ -46,4 +62,41 @@ public class ProxyAndTlsProtocolNegotiatorTest {
HAProxyTLV haProxyTLV = new HAProxyTLV((byte) 0xE1, content);
negotiator.handleHAProxyTLV(haProxyTLV, Attributes.newBuilder());
}
-}
\ No newline at end of file
+
+ @Test
+ public void testLoadSslContextWithUnencryptedKey() throws Exception {
+ configureTls("server.key", "server.pem", "");
+ ProxyAndTlsProtocolNegotiator.loadSslContext();
+ }
+
+ @Test
+ public void testLoadSslContextWithEncryptedKey() throws Exception {
+ // "1234" is the password of certs/client.key, inherited from remoting
module test resources
+ configureTls("client.key", "client.pem", "1234");
+ ProxyAndTlsProtocolNegotiator.loadSslContext();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testLoadSslContextWithWrongPassword() throws Exception {
+ configureTls("client.key", "client.pem", "wrong_password");
+ ProxyAndTlsProtocolNegotiator.loadSslContext();
+ }
+
+ private void configureTls(String keyFile, String certFile, String
password) throws IOException {
+ ProxyConfig proxyConfig = ConfigurationManager.getProxyConfig();
+ proxyConfig.setTlsTestModeEnable(false);
+ proxyConfig.setTlsKeyPath(getCertsPath(keyFile));
+ proxyConfig.setTlsCertPath(getCertsPath(certFile));
+ proxyConfig.setTlsKeyPassword(password);
+ }
+
+ private static String getCertsPath(String fileName) throws IOException {
+ File tempFile = File.createTempFile(fileName, null);
+ tempFile.deleteOnExit();
+ try (InputStream is = ProxyAndTlsProtocolNegotiatorTest.class
+ .getClassLoader().getResourceAsStream("certs/" + fileName)) {
+ Files.copy(is, tempFile.toPath(),
StandardCopyOption.REPLACE_EXISTING);
+ }
+ return tempFile.getAbsolutePath();
+ }
+}
diff --git a/proxy/src/test/resources/certs/client.key
b/proxy/src/test/resources/certs/client.key
new file mode 100644
index 0000000000..c30daea2b9
--- /dev/null
+++ b/proxy/src/test/resources/certs/client.key
@@ -0,0 +1,17 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIICoTAbBgkqhkiG9w0BBQMwDgQI1vtPpDhOYRcCAggABIICgMHwgw0p9fx95R/+
+cWnNdEq8I3ZOOy2wDjammFvPrYXcCJzS3Xg/0GDJ8pdJRKrI7253e4u3mxf5oMuY
+RrvpB3KfdelU1k/5QKqOxL/N0gQafQLViN53f6JelyBEAmO1UxQtKZtkTrdZg8ZP
+0u1cPPWxmgNdn1Xx3taMw+Wo05ysHjnHJhOEDQ2WT3VXigiRmFSX3H567yjYMRD+
+zmvBq+qqR9JPbH9Cn7X1oRXX6c8VsZHWF/Ds0I4i+5zJxsSIuNZxjZw9XXNgXtFv
+7FEFC0HDgDQQUY/FNPUbmjQUp1y0YxoOBjlyIqBIx5FWxu95p2xITS0OimQPFT0o
+IngaSb+EKRDhqpLxxIVEbDdkQrdRqcmmLGJioAysExTBDsDwkaEJGOp44bLDM4QW
+SIA9SB01omuCXgn7RjUyVXb5g0Lz+Nvsfp1YXUkPDO9hILfz3eMHDSW7/FzbB81M
+r8URaTagQxBZnvIoCoWszLDXn3JwEjpZEA6y55Naptps3mMRf7+XMt42lX0e4y9a
+ogNu5Zw/RZD9YcaTjC2z5XeKiMCs1Ymhy9iuzbo+eRGESqzvUE4VirtsiEwxJRci
+JHAvuAl3X4XnpTty4ahOU+DihM9lALxdU68CN9++7mx581pYuvjzrV+Z5+PuptZX
+AjCZmkZLDh8TCHSzWRqvP/Hcvo9BjW8l1Lq6tOa222PefSNCc6gs6Hq+jUghbabZ
+/ux4WuFc0Zd6bfQWAZohSvd78/ixsdJPNGm2OP+LUIrEDKIkLuH1PM0uq4wzJZNu
+Bo7oJ5iFWF67u3MC8oq+BqOVKDNWaCMi7iiSrN2XW8FBo/rpx4Lf/VYREL+Y0mP6
+vzJrZqw=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/proxy/src/test/resources/certs/client.pem
b/proxy/src/test/resources/certs/client.pem
new file mode 100644
index 0000000000..cb6580d900
--- /dev/null
+++ b/proxy/src/test/resources/certs/client.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDATCCAekCAQIwDQYJKoZIhvcNAQEFBQAwfDELMAkGA1UEBhMCemgxCzAJBgNV
+BAgMAnpqMQswCQYDVQQHDAJoejEPMA0GA1UECgwGYXBhY2hlMREwDwYDVQQLDAhy
+b2NrZXRtcTEOMAwGA1UEAwwFeXVrb24xHzAdBgkqhkiG9w0BCQEWEHl1a29uQGFw
+YWNoZS5vcmcwIBcNMTgwMTE2MDYxNjQ0WhgPMjExNzEyMjMwNjE2NDRaMIGSMQsw
+CQYDVQQGEwJDTjERMA8GA1UECAwIWmhlamlhbmcxETAPBgNVBAcMCEhhbmd6aG91
+MQ8wDQYDVQQKDAZhcGFjaGUxETAPBgNVBAsMCHJvY2tldG1xMRgwFgYDVQQDDA9h
+cGFjaGUgcm9ja2V0bXExHzAdBgkqhkiG9w0BCQEWEHl1a29uQGFwYWNoZS5vcmcw
+gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOjPlSjZk37XLBJBc5G/qQNsNdVD
+vZnEGntrqW0UuHjF2T/LPtsGOavLP5wCHvn2zwMR2eCXZwKdKIzSvk0L3XOjH/XY
+OLgRa3cg90lV7Wzn9UMGq3nOjFtjIODPjtz3lwYAuAt1MH+K0E+ChuCFBgFqdY9U
+E0suW3DX0Mt/WB3pAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAFGPaZKyCZzQihKj
+n/7I1J0wKl1HrU7N4sOie8E+ntcpKeX9zKYAou/4Iy0qwgxgRsnucB1rDous560a
++8DFDU8+FnikK9cQtKfQqu4F266IkkXolviZMSfkmB+NIsByIl95eMJlQHVlAvnX
+vnpGdhD/Jhs+acE1VHhO6K+8omKLA6Og8MmYGRwmnBLcxIvqoSNDlEShfQyjaECg
+I4bEi4ZhH3lSHE46FybJdoxDbj9IjHWqpOnjM23EOyfd1zcwOZJA7a54kfOpiTjz
+wrtes5yoQznun5WtGcLM8ZmyaQ+Jr3j6NyZhOwULzK1+A8YUsW6Ww39xTxQoIHEQ
+7eirb54=
+-----END CERTIFICATE-----
diff --git a/proxy/src/test/resources/certs/server.key
b/proxy/src/test/resources/certs/server.key
new file mode 100644
index 0000000000..30df69619e
--- /dev/null
+++ b/proxy/src/test/resources/certs/server.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOsmp4YtrIRsBdBQ
+LyPImafCRynTJls3NNF4g6nZr9e0efBY830gw9kBebcm603sdZNl95fzRr2+srXi
+5FJbG7Fmq1+F0xLNK/kKWirGtNMT2DubmhVdKyXYJSvInoGRkrQzbOG0MdAyzE6Q
+O6OjjNN+xGkmadWyCyNF6S8YqMJTAgMBAAECgYEAj0OlnOIG0Ube4+N2VN7KfqKm
+qJy0Ka6gx14dGUY/E7Qo9n27GujzaSq09RkJExiVKZBeIH1fBAtC5f2uDV7kpy0l
+uNpTpQkbw0g2EQLxDsVwaUEYbu+t9qVeXoDd1vFeoXHBuRwvI9UW1BrxVtvKODia
+5StU8Lw4yjcm2lQalwECQQD/sKj56thIsIY7D9qBHk7fnFLd8aYzhnP2GsbZX4V/
+T1KHRxr/8MqdNQX53DE5qcyM/Mqu95FIpTAniUtvcBujAkEA62+fAMYFTAEWj4Z4
+vCmcoPqfVPWhBKFR/wo3L8uUARiIzlbYNU3LIqC2s16QO50+bLUd41oVHNw9Y+uM
+fxQpkQJACg/WpncSadHghmR6UchyjCQnsqo2wyJQX+fv2VAD/d2OPtqSem3sW0Fh
+6dI7cax36zhrdXUyl2xAt92URV9hBwJALX93sdWSxnpbWsc449wCydVFH00MnfFz
+AB+ARLtJ0eBk58M+qyZqgDmgtQ8sPmkH3EgwC3SoKdiiAIJPt2s1EQJBAKnISZZr
+qB2F2PfAW2JJbQlrPyVzkxhv9XYdiVNOErmuxLFae3AI7nECgGuFBtvmeqzm2yRj
+7RBMCmzyWG7MF3o=
+-----END PRIVATE KEY-----
diff --git a/proxy/src/test/resources/certs/server.pem
b/proxy/src/test/resources/certs/server.pem
new file mode 100644
index 0000000000..0187247af2
--- /dev/null
+++ b/proxy/src/test/resources/certs/server.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDATCCAekCAQIwDQYJKoZIhvcNAQEFBQAwfDELMAkGA1UEBhMCemgxCzAJBgNV
+BAgMAnpqMQswCQYDVQQHDAJoejEPMA0GA1UECgwGYXBhY2hlMREwDwYDVQQLDAhy
+b2NrZXRtcTEOMAwGA1UEAwwFeXVrb24xHzAdBgkqhkiG9w0BCQEWEHl1a29uQGFw
+YWNoZS5vcmcwIBcNMTgwMTE2MDYxMzQ5WhgPMjExNzEyMjMwNjEzNDlaMIGSMQsw
+CQYDVQQGEwJDTjERMA8GA1UECAwIWmhlamlhbmcxETAPBgNVBAcMCEhhbmd6aG91
+MQ8wDQYDVQQKDAZhcGFjaGUxETAPBgNVBAsMCHJvY2tldG1xMRgwFgYDVQQDDA9h
+cGFjaGUgcm9ja2V0bXExHzAdBgkqhkiG9w0BCQEWEHl1a29uQGFwYWNoZS5vcmcw
+gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOsmp4YtrIRsBdBQLyPImafCRynT
+Jls3NNF4g6nZr9e0efBY830gw9kBebcm603sdZNl95fzRr2+srXi5FJbG7Fmq1+F
+0xLNK/kKWirGtNMT2DubmhVdKyXYJSvInoGRkrQzbOG0MdAyzE6QO6OjjNN+xGkm
+adWyCyNF6S8YqMJTAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAAzbwXyAULmXitiU
++8/2vbUZQlzB/nXY52OIq7qu3F55hE5qlHkcVxG2JZjO3p5UETwOyNUpU4dpu3uT
+7WSdygH4Iagl87ILpGsob9pAf0joAbaXAY4sGDhg+WjR5JInAxbmT+QWZ+4NTuLQ
+fSudUSJrv+HmUlmcVOvLiNStgt9rbtcgJAvpVwY+iCv0HQziFuQxmOkDv09ZLzu/
+lxCMqnbgkEFYkwdntN6MVk38K3MovszedGO/n19hNOFss7nn5XDEeEnc6BqKGdck
+YDoy6amohY0Ds0o0gJ2rq0Y8Gjl9spQ3oeXpoNUoz84OF4KIBRTzSMv8CrmqPdFY
+Zd2MGjw=
+-----END CERTIFICATE-----