added SslUtil and tests

This commit is contained in:
YevhenBondarenko 2023-10-17 23:20:00 +02:00
parent 9d19a15413
commit cf32544e71
8 changed files with 245 additions and 90 deletions

View File

@ -36,6 +36,14 @@
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId> <artifactId>spring-core</artifactId>

View File

@ -0,0 +1,105 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed 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.thingsboard.common.util;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder;
import org.thingsboard.server.common.data.StringUtils;
import java.io.StringReader;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class SslUtil {
public static final char[] EMPTY_PASS = {};
public static final BouncyCastleProvider DEFAULT_PROVIDER = new BouncyCastleProvider();
static {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(DEFAULT_PROVIDER);
}
}
private SslUtil() {
}
@SneakyThrows
public static List<X509Certificate> readCertFile(String fileContent) {
List<X509Certificate> certificates = new ArrayList<>();
JcaX509CertificateConverter certConverter = new JcaX509CertificateConverter();
try (PEMParser pemParser = new PEMParser(new StringReader(fileContent))) {
Object object;
while ((object = pemParser.readObject()) != null) {
if (object instanceof X509CertificateHolder) {
X509Certificate x509Cert = certConverter.getCertificate((X509CertificateHolder) object);
certificates.add(x509Cert);
}
}
}
return certificates;
}
@SneakyThrows
public static PrivateKey readPrivateKey(String fileContent, String passStr) {
char[] password = StringUtils.isEmpty(passStr) ? EMPTY_PASS : passStr.toCharArray();
PrivateKey privateKey = null;
JcaPEMKeyConverter keyConverter = new JcaPEMKeyConverter();
if (StringUtils.isNotEmpty(fileContent)) {
try (PEMParser pemParser = new PEMParser(new StringReader(fileContent))) {
Object object;
while ((object = pemParser.readObject()) != null) {
if (object instanceof PEMEncryptedKeyPair) {
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password);
privateKey = keyConverter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv)).getPrivate();
break;
} else if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
InputDecryptorProvider decProv =
new JcePKCSPBEInputDecryptorProviderBuilder().setProvider(DEFAULT_PROVIDER).build(password);
privateKey = keyConverter.getPrivateKey(((PKCS8EncryptedPrivateKeyInfo) object).decryptPrivateKeyInfo(decProv));
break;
} else if (object instanceof PEMKeyPair) {
privateKey = keyConverter.getKeyPair((PEMKeyPair) object).getPrivate();
break;
} else if (object instanceof PrivateKeyInfo) {
privateKey = keyConverter.getPrivateKey((PrivateKeyInfo) object);
}
}
}
}
return privateKey;
}
}

View File

@ -20,35 +20,17 @@ import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslContextBuilder;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.thingsboard.common.util.SslUtil;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException;
import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.io.StringReader;
import java.security.GeneralSecurityException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertPath; import java.security.cert.CertPath;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -61,16 +43,11 @@ public class CertPemCredentials implements ClientCredentials {
public static final String X_509 = "X.509"; public static final String X_509 = "X.509";
public static final String CERT_ALIAS_PREFIX = "cert-"; public static final String CERT_ALIAS_PREFIX = "cert-";
public static final String CA_CERT_CERT_ALIAS_PREFIX = "caCert-cert-"; public static final String CA_CERT_CERT_ALIAS_PREFIX = "caCert-cert-";
protected String caCert; protected String caCert;
private String cert; private String cert;
private String privateKey; private String privateKey;
private String password; private String password = "";
public CertPemCredentials() {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
@Override @Override
public CredentialsType getType() { public CredentialsType getType() {
@ -95,7 +72,7 @@ public class CertPemCredentials implements ClientCredentials {
} }
protected TrustManagerFactory createAndInitTrustManagerFactory() throws Exception { protected TrustManagerFactory createAndInitTrustManagerFactory() throws Exception {
List<X509Certificate> caCerts = readCertFile(caCert); List<X509Certificate> caCerts = SslUtil.readCertFile(caCert);
KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
caKeyStore.load(null, null); caKeyStore.load(null, null);
@ -108,15 +85,15 @@ public class CertPemCredentials implements ClientCredentials {
return trustManagerFactory; return trustManagerFactory;
} }
protected KeyManagerFactory createAndInitKeyManagerFactory() throws Exception { private KeyManagerFactory createAndInitKeyManagerFactory() throws Exception {
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(loadKeyStore(), password.toCharArray()); kmf.init(loadKeyStore(), password.toCharArray());
return kmf; return kmf;
} }
private KeyStore loadKeyStore() throws Exception { protected KeyStore loadKeyStore() throws Exception {
List<X509Certificate> certificates = readCertFile(this.cert); List<X509Certificate> certificates = SslUtil.readCertFile(this.cert);
PrivateKey privateKey = readPrivateKey(this.privateKey, this.password); PrivateKey privateKey = SslUtil.readPrivateKey(this.privateKey, password);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null); keyStore.load(null);
@ -135,47 +112,4 @@ public class CertPemCredentials implements ClientCredentials {
return keyStore; return keyStore;
} }
protected List<X509Certificate> readCertFile(String fileContent) throws IOException, GeneralSecurityException {
List<X509Certificate> certificates = new ArrayList<>();
JcaX509CertificateConverter certConverter = new JcaX509CertificateConverter();
try (PEMParser pemParser = new PEMParser(new StringReader(fileContent))) {
Object object;
while ((object = pemParser.readObject()) != null) {
if (object instanceof X509CertificateHolder) {
X509Certificate x509Cert = certConverter.getCertificate((X509CertificateHolder) object);
certificates.add(x509Cert);
}
}
}
return certificates;
}
protected PrivateKey readPrivateKey(String fileContent, String password) throws IOException, PKCSException {
PrivateKey privateKey = null;
JcaPEMKeyConverter keyConverter = new JcaPEMKeyConverter();
if (StringUtils.isNotEmpty(fileContent)) {
try (PEMParser pemParser = new PEMParser(new StringReader(fileContent))) {
Object object;
while ((object = pemParser.readObject()) != null) {
if (object instanceof PEMEncryptedKeyPair) {
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
privateKey = keyConverter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv)).getPrivate();
break;
} else if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
InputDecryptorProvider decProv =
new JcePKCSPBEInputDecryptorProviderBuilder().setProvider(new BouncyCastleProvider()).build(password.toCharArray());
privateKey = keyConverter.getPrivateKey(((PKCS8EncryptedPrivateKeyInfo) object).decryptPrivateKeyInfo(decProv));
break;
} else if (object instanceof PEMKeyPair) {
privateKey = keyConverter.getKeyPair((PEMKeyPair) object).getPrivate();
break;
} else if (object instanceof PrivateKeyInfo) {
privateKey = keyConverter.getPrivateKey((PrivateKeyInfo) object);
}
}
}
}
return privateKey;
}
} }

View File

@ -22,28 +22,32 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import org.thingsboard.common.util.SslUtil;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.security.PrivateKey; import java.security.Key;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.List; import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
public class CertPemCredentialsTest { import static org.thingsboard.rule.engine.credentials.CertPemCredentials.CERT_ALIAS_PREFIX;
import static org.thingsboard.rule.engine.credentials.CertPemCredentials.PRIVATE_KEY_ALIAS;
private final CertPemCredentials credentials = new CertPemCredentials(); public class CertPemCredentialsTest {
private static final String PASS = "test"; private static final String PASS = "test";
private static final String EMPTY_PASS = ""; private static final String EMPTY_PASS = "";
private static final String RSA = "RSA"; private static final String RSA = "RSA";
private static final String ECDSA = "ECDSA"; private static final String EC = "EC";
@Test @Test
public void testChainOfCertificates() throws Exception { public void testChainOfCertificates() throws Exception {
String fileContent = fileContent("pem/tb-cloud-chain.pem"); String fileContent = fileContent("pem/tb-cloud-chain.pem");
List<X509Certificate> x509Certificates = credentials.readCertFile(fileContent); List<X509Certificate> x509Certificates = SslUtil.readCertFile(fileContent);
Assert.assertEquals(4, x509Certificates.size()); Assert.assertEquals(4, x509Certificates.size());
Assert.assertEquals("CN=*.thingsboard.cloud, O=\"ThingsBoard, Inc.\", ST=New York, C=US", Assert.assertEquals("CN=*.thingsboard.cloud, O=\"ThingsBoard, Inc.\", ST=New York, C=US",
@ -60,7 +64,7 @@ public class CertPemCredentialsTest {
public void testSingleCertificate() throws Exception { public void testSingleCertificate() throws Exception {
String fileContent = fileContent("pem/tb-cloud.pem"); String fileContent = fileContent("pem/tb-cloud.pem");
List<X509Certificate> x509Certificates = credentials.readCertFile(fileContent); List<X509Certificate> x509Certificates = SslUtil.readCertFile(fileContent);
Assert.assertEquals(1, x509Certificates.size()); Assert.assertEquals(1, x509Certificates.size());
Assert.assertEquals("CN=*.thingsboard.cloud, O=\"ThingsBoard, Inc.\", ST=New York, C=US", Assert.assertEquals("CN=*.thingsboard.cloud, O=\"ThingsBoard, Inc.\", ST=New York, C=US",
@ -71,26 +75,41 @@ public class CertPemCredentialsTest {
public void testEmptyFileContent() throws Exception { public void testEmptyFileContent() throws Exception {
String fileContent = fileContent("pem/empty.pem"); String fileContent = fileContent("pem/empty.pem");
List<X509Certificate> x509Certificates = credentials.readCertFile(fileContent); List<X509Certificate> x509Certificates = SslUtil.readCertFile(fileContent);
Assert.assertEquals(0, x509Certificates.size()); Assert.assertEquals(0, x509Certificates.size());
} }
private static Stream<Arguments> testReadPrivateKey() { private static Stream<Arguments> testLoadKeyStore() {
return Stream.of( return Stream.of(
Arguments.of("pem/rsa_key.pem", EMPTY_PASS, RSA), Arguments.of("pem/rsa_cert.pem", "pem/rsa_key.pem", EMPTY_PASS, RSA),
Arguments.of("pem/rsa_encrypted_key.pem", PASS, RSA), Arguments.of("pem/rsa_encrypted_cert.pem", "pem/rsa_encrypted_key.pem", PASS, RSA),
Arguments.of("pem/rsa_encrypted_traditional_key.pem", PASS, RSA), Arguments.of("pem/rsa_encrypted_traditional_cert.pem", "pem/rsa_encrypted_traditional_key.pem", PASS, RSA),
Arguments.of("pem/ec_key.pem", EMPTY_PASS, ECDSA) Arguments.of("pem/ec_cert.pem", "pem/ec_key.pem", EMPTY_PASS, EC)
); );
} }
@ParameterizedTest @ParameterizedTest
@MethodSource @MethodSource
public void testReadPrivateKey(String keyPath, String password, String algorithm) throws Exception { public void testLoadKeyStore(String certPath, String keyPath, String password, String algorithm) throws Exception {
PrivateKey privateKey = credentials.readPrivateKey(fileContent(keyPath), password); CertPemCredentials certPemCredentials = new CertPemCredentials();
Assertions.assertNotNull(privateKey); String certContent = fileContent(certPath);
Assertions.assertEquals(algorithm, privateKey.getAlgorithm()); certPemCredentials.setCert(certContent);
certPemCredentials.setPrivateKey(fileContent(keyPath));
certPemCredentials.setPassword(password);
KeyStore keyStore = certPemCredentials.loadKeyStore();
Assertions.assertNotNull(keyStore);
Key key = keyStore.getKey(PRIVATE_KEY_ALIAS, password.toCharArray());
Assertions.assertNotNull(key);
Assertions.assertEquals(algorithm, key.getAlgorithm());
List<X509Certificate> certs = SslUtil.readCertFile(certContent);
for (X509Certificate cert : certs) {
String alias = CERT_ALIAS_PREFIX + cert.getIssuerDN().getName();
Certificate certificate = keyStore.getCertificate(alias);
Assertions.assertNotNull(certificate);
Assertions.assertEquals(new String(cert.getEncoded()), new String(certificate.getEncoded()));
}
} }
private String fileContent(String fileName) throws IOException { private String fileContent(String fileName) throws IOException {

View File

@ -0,0 +1,13 @@
-----BEGIN CERTIFICATE-----
MIICCDCCAa2gAwIBAgIUGx/SZqIWza/i/gaKFUVIyTEu2oMwCgYIKoZIzj0EAwIw
WTELMAkGA1UEBhMCVUExDTALBgNVBAgMBEtZSVYxDTALBgNVBAcMBEtZSVYxCzAJ
BgNVBAoMAlRCMQswCQYDVQQLDAJUQjESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIz
MTAxNjEyMjMyMVoXDTI0MTAxNTEyMjMyMVowWTELMAkGA1UEBhMCVUExDTALBgNV
BAgMBEtZSVYxDTALBgNVBAcMBEtZSVYxCzAJBgNVBAoMAlRCMQswCQYDVQQLDAJU
QjESMBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
z4MgawieJfVc5zUOPiw5WFxfHGJf7dOMsHvudDxdOs27PXPbJfi09BVJ3+JjNxA2
wQz9KUk877oWRYrN/e+MbKNTMFEwHQYDVR0OBBYEFDTV8VD3m+8IBQOBJ+V/bcbl
4preMB8GA1UdIwQYMBaAFDTV8VD3m+8IBQOBJ+V/bcbl4preMA8GA1UdEwEB/wQF
MAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAOgIkl8j8m51W7pWlNUAuUnHnOVhVjGr
h8Rc6cbwTapKAiEA2CLrduTweXEF5fBRtWyOsG8c9af6+MWHKmwHL1IDw9Q=
-----END CERTIFICATE-----

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFkzCCA3ugAwIBAgIUUQa3cWUVoF58dzg8ycb/y7SdCj8wDQYJKoZIhvcNAQEL
BQAwWTELMAkGA1UEBhMCVUExDTALBgNVBAgMBEtZSVYxDTALBgNVBAcMBEtZSVYx
CzAJBgNVBAoMAlRCMQswCQYDVQQLDAJUQjESMBAGA1UEAwwJbG9jYWxob3N0MB4X
DTIzMTAxMzEyMzcwMVoXDTI0MTAxMjEyMzcwMVowWTELMAkGA1UEBhMCVUExDTAL
BgNVBAgMBEtZSVYxDTALBgNVBAcMBEtZSVYxCzAJBgNVBAoMAlRCMQswCQYDVQQL
DAJUQjESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEAsHn27cH+pYFI0eJYer8ww29g/xlKgr9aarYlkILeXnBhPPHBCXG+
FegeMpHa8FUPANIqYJiwM13altO6hMLPa0J7+nQhwF5NCbxzAdi/kU8ofhIwJH+K
gOsD3BKdR7Ua7KMDQFnGTFRR9ZxsuYZ/0AHuzPHwxSLUvvMbiWbu5P2FYMrEyyLo
uVVihZPkeBhcnI6SJRyCdMdMy282nWQ+47gAUI3cFa7dXxUcXvRbbToMNPTIDUy4
VhxJYhL4T6ED0Ds7tZRsG71LcMfw2RQUgiS1FuYh+O7N8lUMukMy2/umQluM0+qB
CYWa2p1UCbVzlrW1qgKQm1Q8E91XSR9KL/zdO8m9/uNeI1jyJu6i1cibWR7gnh6J
ChLxouQlrBzuLzSz7PG8q1MOWi+oHYJWSvmsckbQDhwEsfhFrYVgndJdxnmlkzvS
1OP7RGSYXLfMF+ZxC2YEJiU65QACCl2IHknyNiL8Jg5ahXgZMNshyfvOv5RB5jnz
4vzRpGhUYCcyLzORT+5gY9ZYbX/51cOomQV1ryTTQs+zA8mfEVLjbbLqvYdI84LC
3chMdcOm8Z9U1xdb2FX/c724XDyPnQNy1PLggzqvOFZzLeey0nBVUWyVrcCydbS5
PAvVoAucO8kqP6b7uB7QnDeGaCiAVF+9QaXxjyQEdLEu3z5JMM7uH4UCAwEAAaNT
MFEwHQYDVR0OBBYEFHXrT3L+O3kJ2xNZ4Lh1ThGG6M1vMB8GA1UdIwQYMBaAFHXr
T3L+O3kJ2xNZ4Lh1ThGG6M1vMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggIBABFDqkTdxsJyu5L2x3WSpw4jw4vgJYlgUvTSeU8i54DaSzncLZdpWsqb
37LFHkvlquIfvOi9f9EBT2KuZwaajPQBNE4m7kLchoAv8Mc8a2EXhN2caXamnN3F
vWAb4QW/VHKKz2vWprfARwqQO58TEPgzU4FcW1lPpX2ULBeoS5kZDDEgyfaFZETF
FnsSb9E3/YuH6sJCu880kbW8BIyQmbUytrbn+16J/iaZBwc+iD49t2VBLDOsr1x4
5qzxknG3h9wiz9ob9v6hWFfMpdiK12S0P5FVsUkCpxoae8jc8rPS7W3HaYowFjVR
OHOjtWy5/SV2rypKShjg9manf6iwGdTGkD0qoqsRs9JQFabjNR23IQv+1OUbrEVC
DbS65IjwLJlIZBX8JuJaU3I8zqj/9q7TtRDp1NCiG5W0NgipERRCciWaLJ+Fz6Lu
QzhI2ZOJrl49hmr6e0bsyNUv9l89WcbKm3/IC+V7o80uADYCOaz2jDGfKbvcPHzN
mTma8qVsjpcedttsvNMyZOsM/Rpk+dbChgReRVvcmzQV0izEvJJBWFr4HrfcM6Ev
sZrnUiT8ENUZqiK40d+T3Q6JheHwm+ENI1aUDkYCpoWZ/PzKe+Bj8lR8dPvmeVrc
eiwS37nMFO/5X7aIkszTouScNO99cN0UqPldfJo+8ZTbai5VFxGD
-----END CERTIFICATE-----

View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDkzCCAnugAwIBAgIUKAylzm/K5OfbXSjm1zY9bX1a8HQwDQYJKoZIhvcNAQEL
BQAwWTELMAkGA1UEBhMCVUExDTALBgNVBAgMBEtZSVYxDTALBgNVBAcMBEtZSVYx
CzAJBgNVBAoMAlRCMQswCQYDVQQLDAJUQjESMBAGA1UEAwwJbG9jYWxob3N0MB4X
DTIzMTAxNjIxMDQwNVoXDTI0MTAxNTIxMDQwNVowWTELMAkGA1UEBhMCVUExDTAL
BgNVBAgMBEtZSVYxDTALBgNVBAcMBEtZSVYxCzAJBgNVBAoMAlRCMQswCQYDVQQL
DAJUQjESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEA87nLliszEWml8QvyAC+H80NZCxf4TcG826NBOp0AUPJ8xQBHCzc1
t1ohVm2/fn2VJZAYXG2xSVcHyXjjjv3iGLE2AIDbXh06/yFg4TVjlbrWrAHFehyN
FwrK8ez36oGLa3ZVq+mx1fLfBQw5mStbh09NXmKTzqP6m9ggKtt63cUwoWdUTemT
qrjryJd69LiJi+MVqtbKO2j30/lgAZmaHtbojl9EcvWfeXLb20TnXRIctaIS1VGo
SluzjbNQErdN/VRW4RAOP6UFsK0xID2EuLODBmAWnI49fXO/OS+u3Kd3suABE0o9
slfDXqNTp0r5N0OoSAFcc4EsV3+9Gf+mqwIDAQABo1MwUTAdBgNVHQ4EFgQUhS5K
XQDxGvaBCpKY1de+JZl8zjYwHwYDVR0jBBgwFoAUhS5KXQDxGvaBCpKY1de+JZl8
zjYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAxez4vLtBCBNM
l6AQghViNAR9iiwYMxUwKwlU+uZftRGnT+6dXgfTR3PV6LCfMMmtuNs0JTGy0ff8
erbzfZxExvHfIFXCwepwTWawQhvRRn9GHOJXIzESDRRhsXoJDzd0JVOx0wWxp1cz
EUts+ZbKLoC+kIhsOGY+0a+sopeV2rMO5bUMpA8P0mKZlGynEGMLzKxz65E/IA9h
EQKpJjpvYfN+7eUkF6ZRXNV2LI/8BCoG6mOVoOMEXnloPwwBtOevoCB43U3sT9Er
WQWgZdbeI4gEyEqgMTibNogZZF0KW+5as3iv7avDd8pCgONvD0iwKSlvi9RNjiw8
p6bwNmBcuA==
-----END CERTIFICATE-----

View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDkzCCAnugAwIBAgIUIo+5l07ZrQR/LxEEmUbnn4yxCwIwDQYJKoZIhvcNAQEL
BQAwWTELMAkGA1UEBhMCVUExDTALBgNVBAgMBEtZSVYxDTALBgNVBAcMBEtZSVYx
CzAJBgNVBAoMAlRCMQswCQYDVQQLDAJUQjESMBAGA1UEAwwJbG9jYWxob3N0MB4X
DTIzMTAxNjIxMDMxNVoXDTI0MTAxNTIxMDMxNVowWTELMAkGA1UEBhMCVUExDTAL
BgNVBAgMBEtZSVYxDTALBgNVBAcMBEtZSVYxCzAJBgNVBAoMAlRCMQswCQYDVQQL
DAJUQjESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEA87nLliszEWml8QvyAC+H80NZCxf4TcG826NBOp0AUPJ8xQBHCzc1
t1ohVm2/fn2VJZAYXG2xSVcHyXjjjv3iGLE2AIDbXh06/yFg4TVjlbrWrAHFehyN
FwrK8ez36oGLa3ZVq+mx1fLfBQw5mStbh09NXmKTzqP6m9ggKtt63cUwoWdUTemT
qrjryJd69LiJi+MVqtbKO2j30/lgAZmaHtbojl9EcvWfeXLb20TnXRIctaIS1VGo
SluzjbNQErdN/VRW4RAOP6UFsK0xID2EuLODBmAWnI49fXO/OS+u3Kd3suABE0o9
slfDXqNTp0r5N0OoSAFcc4EsV3+9Gf+mqwIDAQABo1MwUTAdBgNVHQ4EFgQUhS5K
XQDxGvaBCpKY1de+JZl8zjYwHwYDVR0jBBgwFoAUhS5KXQDxGvaBCpKY1de+JZl8
zjYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAiUTgjnsVIg90
Dm+XSlscIPbZEj/mJanoFFfAfbVJz1DadygG9viVUMf3jVQBcsJGeBDckR2b3OHY
82cQVpdu3Heqld+gnfsyi8QBi7EdK4i0q8NVqFgpw83KxNm9xt7xrgHtxhE0kWfW
dpTgeIu0hFf0qLUObw/g8+0awBuxNY2crLtLXQM/dRgtv5Zt/DilW3jMLAE5wke+
/HM4/emOJO6DSI9BC8iUsmNpIpq45267jcjpczNBo3ap7Bad+jM/paRDng9Uavvr
VCsaJFaL5HG6TtNXN60npBouOWnivPzUeuTI4PnjGRgdp3lgb0IuXbuwxIW6FVG/
73RHc0gGOA==
-----END CERTIFICATE-----