feat: Implement support for importing certificates

chore: Remove obsolete model
wip: Create first working test case for cert import
wip: Generate basic certRequest from given cert files
wip: Start work on being able to import certificates
chore: Update gitignore
This commit is contained in:
Magnus Leßmann (@MarkL4YG) 2024-11-22 17:43:02 +01:00
parent b39242baba
commit 463328f04f
Signed by: Mark.TwoFive
GPG key ID: 5B5EBCBE331F1E6F
15 changed files with 518 additions and 33 deletions

4
.gitattributes vendored
View file

@ -2,4 +2,6 @@
*.bat text eol=crlf
*.jar binary
*.java text eol=lf
*.config text eol=lf
*.config text eol=lf
*.pem text eol=lf
*.srl text eol=lf

5
.gitignore vendored
View file

@ -37,5 +37,6 @@ out/
### VS Code ###
.vscode/
### SQLite DB ###
sqLiteDb.db
### Test files ###
sqLiteDb.db
dev/

View file

@ -8,7 +8,6 @@ import lombok.Data;
public class CertificateRequest {
private RequestType type;
private String commonName;
private String trustingAuthority;
@Builder.Default
@ -18,6 +17,7 @@ public class CertificateRequest {
private int requestedValidityDays = 365;
private CertificateSubject subject;
private CertificateSubject issuer;
private CertificateRequestExtension extension;
public enum RequestType {
@ -33,6 +33,11 @@ public class CertificateRequest {
return this;
}
public CertificateRequestBuilder issuer(CertificateSubject.CertificateSubjectBuilder builder) {
this.issuer = builder.build();
return this;
}
public CertificateRequestBuilder extension(
CertificateRequestExtension.CertificateRequestExtensionBuilder builder
) {

View file

@ -1,6 +1,9 @@
package de.mlessmann.certassist.openssl;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
import lombok.Builder;
import lombok.Getter;
@ -13,7 +16,12 @@ public class CertificateRequestExtension {
public static class CertificateRequestExtensionBuilder {
public CertificateRequestExtensionBuilder alternativeNames(String... altNames) {
this.alternativeNames = List.of(altNames);
Objects.requireNonNull(altNames, "Alternative names must not be null (but can be empty)");
this.alternativeNames = Stream.of(altNames)
.filter(Objects::nonNull)
.map(String::trim)
.map(name -> name.replaceAll("^DNS:", ""))
.toList();
return this;
}
}

View file

@ -7,10 +7,13 @@ import lombok.Getter;
@Builder
public class CertificateSubject {
private String commonName;
private String emailAddress;
private String organization;
private String organizationalUnit;
private String country;
private String state;
private String locality;
public static class CertificateSubjectBuilder {}
}

View file

@ -7,6 +7,7 @@ import de.mlessmann.certassist.ExecutableResolver;
import de.mlessmann.certassist.except.CommandLineOperationException;
import de.mlessmann.certassist.except.UnresolvableCLIDependency;
import de.mlessmann.certassist.openssl.CertificateRequest.RequestType;
import de.mlessmann.certassist.openssl.CertificateSubject.CertificateSubjectBuilder;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@ -59,7 +60,7 @@ public class OpenSSLCertificateCreator {
.replace("STATE", request.getSubject().getState())
.replace("LOCALITY", request.getSubject().getLocality())
.replace("ORGANIZATION", request.getSubject().getOrganization())
.replace("COMMON-NAME", request.getCommonName());
.replace("COMMON-NAME", request.getSubject().getCommonName());
if (StringUtils.isNotBlank(request.getSubject().getOrganizationalUnit())) {
certSubject += "/OU=" + request.getSubject().getOrganizationalUnit();
@ -351,7 +352,7 @@ public class OpenSSLCertificateCreator {
return outFile;
}
private String getCertificateFingerprint(Path certificate)
public String getCertificateFingerprint(Path certificate)
throws CommandLineOperationException, InterruptedException {
try {
StartedProcess fingerprintProc = new ProcessExecutor()
@ -361,6 +362,16 @@ public class OpenSSLCertificateCreator {
.start();
var fingerprintResult = fingerprintProc.getFuture().get();
String output = fingerprintResult.getOutput().getUTF8();
if (fingerprintResult.getExitValue() != 0) {
log.debug("Fingerprint command output:\n{}", output);
throw new CommandLineOperationException(
"Failed to get fingerprint of certificate. Exit code: %d".formatted(
fingerprintResult.getExitValue()
)
);
}
Matcher matcher = FINGERPRINT_EXTRACTOR.matcher(output);
if (!matcher.find()) {
log.debug(output);
@ -381,6 +392,49 @@ public class OpenSSLCertificateCreator {
}
}
public CertificateRequest getCertificateInfo(Path path) throws CommandLineOperationException, InterruptedException {
try {
StartedProcess infoProc = new ProcessExecutor()
.command(
resolveOpenSSL(),
"x509",
"-in",
path.toString(),
"-noout",
"-dateopt",
"iso_8601",
"-fingerprint",
"-subject",
"-issuer",
"-serial",
"-dates",
"-alias",
"-email",
"-purpose",
"-ext",
"subjectAltName",
"-nameopt",
"sep_multiline",
"-nameopt",
"lname"
)
.readOutput(true)
.redirectError(Slf4jStream.ofCaller().asError())
.start();
var infoResult = infoProc.getFuture().get();
String output = infoResult.getOutput().getUTF8();
if (infoResult.getExitValue() != 0) {
log.debug("Certificate info command output:\n{}", output);
throw new CommandLineOperationException(
"Failed to get info of path. Exit code: %d".formatted(infoResult.getExitValue())
);
}
return getCertificateInfo(output.lines().toArray(String[]::new));
} catch (IOException | ExecutionException e) {
throw new RuntimeException(e);
}
}
private String resolveOpenSSL() throws CommandLineOperationException {
try {
return executableResolver.getOpenSSLPath();
@ -388,4 +442,59 @@ public class OpenSSLCertificateCreator {
throw new CommandLineOperationException(e);
}
}
private CertificateRequest getCertificateInfo(String[] lines) {
var builder = CertificateRequest.builder();
boolean hasIssuer = false;
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
if (line.startsWith("subject=")) {
CertificateSubjectBuilder subjectBuilder = CertificateSubject.builder();
line = lines[++i];
while (line.startsWith(" ")) {
subjectBuilder = readSubjectInfo(line, subjectBuilder);
line = lines[++i];
}
builder = builder.subject(subjectBuilder);
} else if (line.startsWith("issuer=")) {
hasIssuer = true;
CertificateSubjectBuilder issuerBuilder = CertificateSubject.builder();
line = lines[++i];
while (line.startsWith(" ")) {
issuerBuilder = readSubjectInfo(line, issuerBuilder);
line = lines[++i];
}
builder = builder.issuer(issuerBuilder);
} else if (line.startsWith("X509v3 Subject Alternative Name")) {
String[] altNames = lines[++i].split(",");
builder = builder.extension(CertificateRequestExtension.builder().alternativeNames(altNames));
}
}
builder = builder.type(hasIssuer ? RequestType.NORMAL_CERTIFICATE : RequestType.STANDALONE_CERTIFICATE);
return builder.build();
}
private CertificateSubjectBuilder readSubjectInfo(String line, CertificateSubjectBuilder builder) {
String[] parts = line.split("=", 2);
if (parts.length != 2) {
return builder;
}
String key = parts[0];
String value = parts[1];
return switch (key.trim()) {
case "countryName" -> builder.country(value);
case "stateOrProvinceName" -> builder.state(value);
case "localityName" -> builder.locality(value);
case "organizationName" -> builder.organization(value);
case "organizationalUnitName" -> builder.organizationalUnit(value);
case "commonName" -> builder.commonName(value);
case "emailAddress" -> builder.emailAddress(value);
default -> throw new IllegalStateException("Unexpected subject key: %s in line: %s".formatted(key, line));
};
}
}

View file

@ -8,8 +8,10 @@ import de.mlessmann.certassist.openssl.*;
import de.mlessmann.certassist.repositories.CertificateRepository;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
@Service
@ -18,29 +20,10 @@ public class CertificateCreationService {
private final CertificateRepository certificateRepository;
private final OpenSSLCertificateCreator openSSLCertificateCreator;
private final PassphraseService passphraseService;
public Certificate createCertificate(final CertificateRequest certificateRequest) {
final Certificate certificate = new Certificate();
certificate.setType(mapCertificateRequestType(certificateRequest.getType()));
certificate.setCommonName(certificateRequest.getCommonName());
certificate.setTrustingAuthority(certificateRequest.getTrustingAuthority());
certificate.setRequestedKeyLength(certificateRequest.getRequestedKeyLength());
certificate.setRequestedValidityDays(certificateRequest.getRequestedValidityDays());
final CertificateSubject certificateSubject = certificateRequest.getSubject();
certificate.setSubjectEmailAddress(certificateSubject.getEmailAddress());
certificate.setSubjectOrganization(certificateSubject.getOrganization());
certificate.setSubjectOrganizationalUnit(certificateSubject.getOrganizationalUnit());
certificate.setSubjectCountry(certificateSubject.getCountry());
certificate.setSubjectState(certificateSubject.getState());
certificate.setSubjectLocality(certificateSubject.getLocality());
final CertificateRequestExtension extension = certificateRequest.getExtension();
if (extension != null) {
final CertificateExtension certificateExtension = new CertificateExtension();
certificateExtension.setIdentifier("alternativeNames");
certificateExtension.setValue(String.join(",", extension.getAlternativeNames()));
certificate.setCertificateExtension(List.of(certificateExtension));
}
final Certificate certificate = createEntityFromRequest(certificateRequest);
try (
OpenSSLCertificateResult certificateCreatorResult = openSSLCertificateCreator.createCertificate(
@ -60,6 +43,49 @@ public class CertificateCreationService {
return certificate;
}
private Certificate createEntityFromRequest(CertificateRequest certificateRequest) {
final Certificate certificate = new Certificate();
certificate.setType(mapCertificateRequestType(certificateRequest.getType()));
certificate.setCommonName(certificateRequest.getSubject().getCommonName());
certificate.setTrustingAuthority(certificateRequest.getTrustingAuthority());
certificate.setRequestedKeyLength(certificateRequest.getRequestedKeyLength());
certificate.setRequestedValidityDays(certificateRequest.getRequestedValidityDays());
final CertificateSubject certificateSubject = certificateRequest.getSubject();
certificate.setSubjectEmailAddress(certificateSubject.getEmailAddress());
certificate.setSubjectOrganization(certificateSubject.getOrganization());
certificate.setSubjectOrganizationalUnit(certificateSubject.getOrganizationalUnit());
certificate.setSubjectCountry(certificateSubject.getCountry());
certificate.setSubjectState(certificateSubject.getState());
certificate.setSubjectLocality(certificateSubject.getLocality());
final CertificateRequestExtension extension = certificateRequest.getExtension();
if (extension != null) {
final CertificateExtension certificateExtension = new CertificateExtension();
certificateExtension.setIdentifier("alternativeNames");
certificateExtension.setValue(String.join(",", extension.getAlternativeNames()));
certificate.setCertificateExtension(List.of(certificateExtension));
}
return certificate;
}
public Certificate importCertificate(Path certificate, Path keyFile, String passphrase) {
try {
String fingerprint = openSSLCertificateCreator.getCertificateFingerprint(certificate);
var generatedRequest = openSSLCertificateCreator.getCertificateInfo(certificate);
Certificate entity = createEntityFromRequest(generatedRequest);
entity.setCert(Files.readAllBytes(certificate));
entity.setPrivateKey(Files.readAllBytes(keyFile));
if (StringUtils.isNotBlank(passphrase)) {
passphraseService.storePassphrase("cert:" + fingerprint, passphrase);
}
return certificateRepository.save(entity);
} catch (CommandLineOperationException | IOException e) {
throw new RuntimeException("Unable to import certificate", e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private CertificateType mapCertificateRequestType(CertificateRequest.RequestType requestType) {
return switch (requestType) {
case ROOT_AUTHORITY -> CertificateType.ROOT_CA;

View file

@ -0,0 +1,48 @@
package de.mlessmann.certassist;
import de.mlessmann.certassist.openssl.CertificateRequest;
import de.mlessmann.certassist.openssl.CertificateRequestExtension;
import de.mlessmann.certassist.openssl.CertificateSubject;
import de.mlessmann.certassist.service.CertificateCreationService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
public class CertificateServiceTest {
@Autowired
private CertificateCreationService certificateService;
@Test
void testCanCreateCertificate() {
var request = CertificateRequest.builder()
.type(CertificateRequest.RequestType.STANDALONE_CERTIFICATE)
.subject(
CertificateSubject.builder()
.commonName("cert.creation")
.country("DE")
.state("SH")
.locality("HH")
.organization("Crazy-Cats")
)
.extension(CertificateRequestExtension.builder().alternativeNames("test2.home", "test3.home"))
.build();
var cert = certificateService.createCertificate(request);
assertThat(cert).isNotNull();
assertThat(cert.getId()).isGreaterThan("0");
}
@Test
void testCanImportCertificate() {
Path certDir = TestOpenSSLCertificateCreator.TEST_CERT_PATH;
var importedCert = certificateService.importCertificate(certDir.resolve("x509forImport.pem"),
certDir.resolve("x509forImport.key.pem"), TestOpenSSLCertificateCreator.TEST_CERT_PASSPHRASE);
assertThat(importedCert).isNotNull();
assertThat(importedCert.getId()).isGreaterThan("0");
}
}

View file

@ -12,12 +12,15 @@ import org.junit.jupiter.api.Test;
class TestOpenSSLCertificateCreator {
public static final String TEST_CERT_PASSPHRASE = "ABC-123";
public static final Path TEST_CERT_PATH = Path.of("src/test/resources/openssl");
private CertificatePasswordProvider passwordProvider;
@BeforeEach
void setUp() {
passwordProvider = mock(CertificatePasswordProvider.class);
when(passwordProvider.generateNewPassword()).thenReturn("ABC-123");
when(passwordProvider.generateNewPassword()).thenReturn(TEST_CERT_PASSPHRASE);
when(passwordProvider.getPasswordFor(anyString())).thenReturn(TEST_CERT_PASSPHRASE);
}
@Test
@ -32,9 +35,16 @@ class TestOpenSSLCertificateCreator {
CertificateRequest certRequest = CertificateRequest
.builder()
.commonName("test.home")
.type(RequestType.STANDALONE_CERTIFICATE)
.subject(CertificateSubject.builder().country("DE").state("SH").locality("HH").organization("Crazy-Cats"))
.subject(
CertificateSubject
.builder()
.commonName("test.home")
.country("DE")
.state("SH")
.locality("HH")
.organization("Crazy-Cats")
)
.extension(CertificateRequestExtension.builder().alternativeNames("test2.home", "test3.home"))
.build();
@ -45,12 +55,21 @@ class TestOpenSSLCertificateCreator {
CertificateRequest childRequest = CertificateRequest
.builder()
.commonName("test.local")
.type(RequestType.NORMAL_CERTIFICATE)
.trustingAuthority(cert.fingerprint())
.subject(
CertificateSubject.builder().country("DE").state("SH").locality("HH").organization("Crazy-Cats")
CertificateSubject
.builder()
.commonName("test.local")
.country("DE")
.state("SH")
.locality("HH")
.organization("Crazy-Cats")
)
.extension(
CertificateRequestExtension.builder()
.alternativeNames("test2.local", "test3.local")
)
.build();
var spiedCert = spy(cert);
@ -66,4 +85,33 @@ class TestOpenSSLCertificateCreator {
}
}
}
@Test
void testCertificateImport() throws Exception {
CertificateProvider certificateProvider = mock(CertificateProvider.class);
ExecutableResolver executableResolver = new ExecutableResolver();
var certificateCreator = new OpenSSLCertificateCreator(
executableResolver,
passwordProvider,
certificateProvider
);
var request = certificateCreator.getCertificateInfo(TEST_CERT_PATH.resolve("x509forImportCA.pem"));
assertThat(request).isNotNull();
assertThat(request.getSubject().getCommonName()).isEqualTo("test.home");
assertThat(request.getSubject().getCountry()).isEqualTo("DE");
assertThat(request.getSubject().getState()).isEqualTo("SH");
assertThat(request.getSubject().getLocality()).isEqualTo("HH");
assertThat(request.getSubject().getOrganization()).isEqualTo("Crazy-Cats");
assertThat(request.getExtension()).isNull();
request = certificateCreator.getCertificateInfo(TEST_CERT_PATH.resolve("x509forImport.pem"));
assertThat(request).isNotNull();
assertThat(request.getSubject().getCommonName()).isEqualTo("test.local");
assertThat(request.getSubject().getCountry()).isEqualTo("DE");
assertThat(request.getSubject().getState()).isEqualTo("SH");
assertThat(request.getSubject().getLocality()).isEqualTo("HH");
assertThat(request.getSubject().getOrganization()).isEqualTo("Crazy-Cats");
assertThat(request.getExtension().getAlternativeNames()).containsExactly("test2.local", "test3.local");
}
}

View file

@ -0,0 +1,65 @@
-----BEGIN CERTIFICATE-----
MIIFgTCCA2mgAwIBAgIUVTm2kFBiacDG3Om6JFaZKvJ6CScwDQYJKoZIhvcNAQEL
BQAwUDELMAkGA1UEBhMCREUxCzAJBgNVBAgMAlNIMQswCQYDVQQHDAJISDETMBEG
A1UECgwKQ3JhenktQ2F0czESMBAGA1UEAwwJdGVzdC5ob21lMB4XDTI0MTEyMjE4
NTc0MFoXDTI1MTEyMjE4NTc0MFowUDELMAkGA1UEBhMCREUxCzAJBgNVBAgMAlNI
MQswCQYDVQQHDAJISDETMBEGA1UECgwKQ3JhenktQ2F0czESMBAGA1UEAwwJdGVz
dC5ob21lMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnnYaTE8TKKTu
IE2hcHwHm0RnM+4VwPnNT6q6b4oYSJJeGJCbQYt8CAAkBxvY3j1H0xP6imD7ULUK
ymx2fQiHK+bRAdXyoguLHaPYWPInfUyHp9Y2w2AglKCG/U2paMni4xL0LH4N3V1r
x5hQG9ORwJzVH5wMiYoETzgbd1ED7G0tVuKYrH84Ma8znEXVZ4XfAlDfYEGKPNDN
dZDkFEQFYHb5RSPB6ym3vrbZJfLkNy01m/Cpdj3/GqJ460zo7x/apzVPNj/khW1v
fME8c8sz1LqEbQBVdUU9xN8DfTjT/z1NHA5S/1O7Yb9Z0t1tjvs3u/iyCjBNMbd5
7FNqMVjXFjENav21zrrr0LAiUfBcbokQZD72/j053gXz9LkcTjLvkPN3i1oVfNIA
b/Ce4hHzpWk2kvZuFyqfKj8Yc8oTfjr88sGxe1JCgbhnCvkw/5d7hy7OJVG4mTIH
WliOY92R7xPwEBhqc+A4VYqhAIrp+Qzen+XGrBih+PneEsSWVfM3PyjKt/qURK4j
gWCGFIEI80xyFaHhwHpeKszZcMOVAHjV3Ik91wutL/HoZ0r5uPYvwqQb6QZ7ubqX
FWiuNUf970TFS7eIA33Xr0IojPBziinU3/uYnJBODR4Sl2npijzwqsRrGsrSDUF7
cxxhsyv64ri2p3u4wnP4faLf1Dtk3kUCAwEAAaNTMFEwHQYDVR0OBBYEFICs29k3
J5Pnza2xgkgycaxBxAcSMB8GA1UdIwQYMBaAFICs29k3J5Pnza2xgkgycaxBxAcS
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAFiqNg1yXlajI1ZJ
l4qXADvi1xpFLEn17162jN+zb6DgRRohqwrsP71fAqyuCticYELk3gBHt8KizEBj
xPSggahX+ZIGG5/Tnb5cQ61y0GMZHWyABHjgiOGi26Gxar5wi7ET6W4D6w09u8U3
wd8q8U164aSj6Rh153S0r8SJJOhqtiZhcYllOMB8SNgjxodlco+YvCGoqkhPdJef
H2S02mgbjxMlo8P4ivoirD7boLirlXkoNidmaWvC/hD4ZwsTWMrRnHL1S5DEhKef
WSgtX3tUEvHrEyqsGcSn49l4CNE6Xbx5wWo+4c9bs642f7u34OoUnitYmGZDzT1d
zQOyBirn9k6fB/SX7Ug+PF6+KbJCXEdWffgNqe5HAG8Lo2EOEnocp3kncRMSq3zY
qzbTBqaWI512cqN2RA+Q0NPzVH01jPG8yKaSxVzf4Sp1Iqjl7fe9WZL9/L4DJZ7o
QWRPZvwH5Rz8utGUylfe9LxSisX7xZMNoGLqsQfanowqZCiS1M1GRoHsILnFuFNw
3ONu4qp0Gr3+PxsKoE6NBstqD6Lkrm0uf/IhC7fwIbdq3qqe0E4xZou9W9nkmapw
0iKhdhCtnp3HsF6f2Kc0ipyPxVXDnP5TCx4NLst2RvoYryUBwqIKfwU8X3yOBmhq
HowJo/ZfrlzZf8753cfF8Kza6NUA
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFrjCCA5agAwIBAgIUMhyzffHXKaQnfX/ckbx3KZ9AVXswDQYJKoZIhvcNAQEL
BQAwUDELMAkGA1UEBhMCREUxCzAJBgNVBAgMAlNIMQswCQYDVQQHDAJISDETMBEG
A1UECgwKQ3JhenktQ2F0czESMBAGA1UEAwwJdGVzdC5ob21lMB4XDTI0MTEyMjE4
NTc0M1oXDTI1MTEyMjE4NTc0M1owUTELMAkGA1UEBhMCREUxCzAJBgNVBAgMAlNI
MQswCQYDVQQHDAJISDETMBEGA1UECgwKQ3JhenktQ2F0czETMBEGA1UEAwwKdGVz
dC5sb2NhbDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALXcSLHkvB99
OBpf57gJTYwppQrsV1yUWJp1qKXCD164GlIul0YnBRLaaxZUfWIJaxEaWz5DFR4B
zR/MlaxUKx0A7fASyRNVA4NMGclt1WJR5fglHih8p+SIE1feVkMdeUftJnEJXVY1
DlZ9Cw7+nrkAmh5s8QEcVgak4kB4/6sFcMhrfa6tNcnhuM7Yq3USZf2a9CDpj78m
8NvIlXN4+bk9fgCSN9vOLzRuMMYT8mRy0fqXdNQcEE0i+SJ6epbsRvXmRdugd3a8
aaLE2Bc5huyaoc//ZneJY5iVyTldSo3eyj88/NvdhApExzeLb8T9Hey6DP/chDfu
KKASiisRL9kPKB++PTt3Suxy+Ary+MJ1uLJLIberb3qWFYyU7i1o3Ex7mBFIpCdW
SXw321cNYapoxMApeyMUWxn9j8GuHRAcARj2FDKIQasY6AqmQ8vL+MGFLqKaxqcD
QDu+8le4EUwNGxkVIINTYzZttsQF2zJ/DPhErbT0rz5TUn5TKzHeReUv+lFhre7E
2bIcocTumbxBuBMNE+1b09IPmdPZCMS8bRd4vQ+4QRHweyFHCHOQHjWN10uLpZhm
Mugq9spxIatQ4jsmuQHSM76f91CH6BvVx+gUNaMcFLkDjPUyrn6gov6SbcBQMuUO
gw4H4moFF+mtiivhvnWYWXRvlqPd9bR9AgMBAAGjfzB9MB8GA1UdIwQYMBaAFICs
29k3J5Pnza2xgkgycaxBxAcSMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgTwMCMGA1Ud
EQQcMBqCC3Rlc3QyLmxvY2Fsggt0ZXN0My5sb2NhbDAdBgNVHQ4EFgQUr4a7BDH8
d4ZlrR3qcOMvczkWEqAwDQYJKoZIhvcNAQELBQADggIBAEwk+wOrXsYbPl+3IgJi
izJAapA5BI/DoX5KUxRgXByNEZeIYlmn0GAj2odxxV8+AnjsIFQGp+76e/s6LpJG
ImipyyZ+XCXSqYoyEaUn0KaJPMN0GQbOv/a/kM0BEYUGLkaJLzlGS3Hg0hhqhlYi
dx6xr92rZWW7HGSliv3QghGsXmQYBJ8rSq9HEpv/RAIm95H6BHltQ1FBMRssDnBB
Ur+WTQTzj5gVqxVKvPOzXxc+V3cs9Nozk+wzuW6KUiBPcfQcxdnlm5wToj/k75i9
iRW5E2Qqe+skmLQDxiTOJmgoZxtjGtVMC20J/XYF/mBEJqr+bND1W/8Tp/pUKzpF
z4SIzP/m2TpPH8Agnvie0Z6nVYgAZpiuBEd7JrPm0kWtuKgL3ER+67M/PjpYYmNx
A/gjmQgz7+7uAk8PhloWKbBOC/2wxk0jkFazRBk6e9RveUR6PBvBpo+nyQSgDQkD
dGyzvIAYcFTFXznSv8cu9wzizwGXJ0EdxSo+oWcsGzq3rhjzD+tROCqS7X1eVF4j
USrygPv8w4UT3SFoiABp1ThDjCheROLISAo/SIag8XUw58bmmiDRuVPLZDQfYBYu
KgSLJpFB99QMzvhkNBhImEpmWRrKAFSmwNG4/l/yR19udd4AxpjH62LT8mUZOf23
dQ6pRi1hNlM1v6ZvFDdq8Rgw
-----END CERTIFICATE-----

View file

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC13Eix5LwffTga
X+e4CU2MKaUK7FdclFiadailwg9euBpSLpdGJwUS2msWVH1iCWsRGls+QxUeAc0f
zJWsVCsdAO3wEskTVQODTBnJbdViUeX4JR4ofKfkiBNX3lZDHXlH7SZxCV1WNQ5W
fQsO/p65AJoebPEBHFYGpOJAeP+rBXDIa32urTXJ4bjO2Kt1EmX9mvQg6Y+/JvDb
yJVzePm5PX4Akjfbzi80bjDGE/JkctH6l3TUHBBNIvkienqW7Eb15kXboHd2vGmi
xNgXOYbsmqHP/2Z3iWOYlck5XUqN3so/PPzb3YQKRMc3i2/E/R3sugz/3IQ37iig
EoorES/ZDygfvj07d0rscvgK8vjCdbiySyG3q296lhWMlO4taNxMe5gRSKQnVkl8
N9tXDWGqaMTAKXsjFFsZ/Y/Brh0QHAEY9hQyiEGrGOgKpkPLy/jBhS6imsanA0A7
vvJXuBFMDRsZFSCDU2M2bbbEBdsyfwz4RK209K8+U1J+Uysx3kXlL/pRYa3uxNmy
HKHE7pm8QbgTDRPtW9PSD5nT2QjEvG0XeL0PuEER8HshRwhzkB41jddLi6WYZjLo
KvbKcSGrUOI7JrkB0jO+n/dQh+gb1cfoFDWjHBS5A4z1Mq5+oKL+km3AUDLlDoMO
B+JqBRfprYor4b51mFl0b5aj3fW0fQIDAQABAoICAACgnSl+bUh7et8FkLpSwznq
qJPd1N3NFr1a0cw4+mgGZBh5cEsFeVhXHYQIrpYN5zdSvwCBUfqp2x4p60i/Alod
jJw2d20GaOo3bP6WPOSBXt+jtnnHkURU8xYJ8lNhthPg4AuNT5Noj6PpUQBChxe/
uh24wVnPcIyJgmtD2wcW324Rk+nNyXVWXUE8ee7C/LnBilWwcn5RL7Ghp1Sf/IEj
GMp42zyq5EnYrxdYOqcucUYOI+YvDUSttviaVjw30cqpSaofvCtMIA7ijdJjpGrz
BgATy5VeXtQRRAfhfV6kHlBoZOJbAv+CQvMEZIMVFZwC5H8QhisbOnhFMHwTpZQh
MzvZOEXGib9MvOr48/MDTu+5RFXFdXnef3Cugik+PuRLZGeJfhC97zVvtpsLTcPj
ICjvTitedJOEy7QDX0WnaiU+CUGKjIx9sAwICIk4VgP4WEtjzfE9IJMaBVymKi6R
o3QgDPmJy+VVajdQ6X5MDVkSMOvQJtfSK397A3KuhkUMz9QfTt31v0gDlzFvQlKd
oiroYQuoiBrHDYUGQ5GW+EH2Tdx7b8iZZNLdB28vuPxW5LW1FVWgzPNdCipcIhvl
dah43dQreZbPMJhTbUB1ns3t6r9PuxdCcyAf8eq1GqDFeLoR5e4m3/4PZQdL6FPQ
GbQYWU3S5c/nIx7TSAGRAoIBAQDuFbeQOpelOTvylIm+H9CYxRTXRGUOpQoLlwyB
1DpLyk/yxTh6Zu9NDIlczhhGsuyP6ETy54gxsqgfgTcjmGQj3PhP/1BfF4fsS+5Q
z8A8BoJAtVaxkceBRP48n4CnbKwTFxQPD2efNy9rf6GfjZ1qrOJAHu5LEg5z8sVH
g/26pHnCZPmu/VAWAJldDIeYiRN7X1kKVhoW/z2vxzRzm7FNdamrARpjHYnv3o0k
sk+gSEvswLEFFlOoyKCJhBMRTdPfHhafJyMj0bDNJmeA7/PIzc53zoSrWeJmkc7B
YiAmS7wkZcm2+hbwmN/tWKuV/PfudphrbjtlE002sXK9WHRTAoIBAQDDi4Ede3li
5Hl+2SgsXPkse7hlkFbV7sk1+pOmor/4eCQ4PAMV99TUBgSuyrdUiiGOZt91Rb1V
BmX2Oup7lWkWxxO39QLymygwvtP+U8kC5q+L/gSgvibem3B81pxKsBZd9AEOT5Ql
EZ9MDYuOJe6O2KrrqBzLRTmCVCYibu96wx1RMs1aVNGIWO+J6o/3A4mNYq6AGToV
OFqqihHyO9ptFyfUZ374UMgEZG3Nx8UiUrcVcLhqGQfgPM2uKrck2aGViF8jDOwW
EdOSyiA2yXwlGlA4pOtTDRLEHtcM21/NXw6OaRo7MI2VH+bDk6MP/UhkgOo1wO9Z
pOVySn3GRxnvAoIBABxY05lFkKaocN3KF7heW1zFIl0bFJkwx0hn/KI8nZBGj0xN
dRMFzHo1Aunc1AEe7pGXyxXMwWeBaQkPPnxreYjXl20W7f/UcmeNBtvyUGYVNE72
CFNuEv3uNAMNzqighMBlIAtG/0Rysp+u+5RerR51ZR7TT/X6OWROxHRmX8BoMuMV
WLEgEff8rEXq5v4fXAV1bcmQRNz2GOcj5bfaErsxRjlJEVY5vyGjMtTqshZYZUnU
C3+aj+1prcuRwR9vT8mh8HmUlFeAcPeMGgi9CR6genPDUAKC2jTyKVbXExLcM5N1
1xWFI1QUL5030H40N2TaE/2+iy3WpXvclte6f2cCggEBALSLs7Ti8lrKlRr3qc38
MdxOhxzPPj3ccU8zguxSTGk/lEGPt1GrN4hj3iL87HRT7VOrzBpdP8inVbEZCLL+
ar8rJBuvTv9tHpiTOO0Tsv7Iu8DH0sIPj5ftcNjy1e9n0d8BkZADDceEE86Gmxmu
ECs4Bef7mkpcKhMZWvRj9R0l1YXJIC2NLsFzTAfM2Qu66l/ugyunHyfEloHrZVKA
zNX4kT/eJy9idqsHhxJ+ppdgQ8a6AquDXPA/c7Cho+OURUrpVN3p6nkeEHjxwviS
49rmBremHdjasz79MpWeX0AhyjDW/e80jyTnBYwCXoY+135kQNtMvjHIvEjgWmyd
7WUCggEAIDBVsoME2myGduyZx0QrmlpNJ3yvvUakKv4Ot07dCQmQByx+2FQZU4a6
Ikk6KuM+Jre/6AFFZxNYn3bLpW+Kht9LdwsL7SztxwtXDg6rhAYrUBROyOn9t7RP
MEIdcpeGGf9KYcnnUFWtUiO9A7nP+75hMHCzmYkVgtkG7cKrYrOtLJXJ8ZIPllon
+ZnZsPpYo9FL76yk0Pf3A9ntn80jaNkEZPsWyJrMXYQTzMER0GnxiNQwoncub3Mj
ELPPSzN6Uuo4UuoTc8y/B/HNcaHAa25FXcb3GmwsOCl/towOYc1944cLcMXYb4KY
BuqnHJNiSeD/vUZiKMmb5KHHBTQHQw==
-----END PRIVATE KEY-----

View file

@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFrjCCA5agAwIBAgIUMhyzffHXKaQnfX/ckbx3KZ9AVXswDQYJKoZIhvcNAQEL
BQAwUDELMAkGA1UEBhMCREUxCzAJBgNVBAgMAlNIMQswCQYDVQQHDAJISDETMBEG
A1UECgwKQ3JhenktQ2F0czESMBAGA1UEAwwJdGVzdC5ob21lMB4XDTI0MTEyMjE4
NTc0M1oXDTI1MTEyMjE4NTc0M1owUTELMAkGA1UEBhMCREUxCzAJBgNVBAgMAlNI
MQswCQYDVQQHDAJISDETMBEGA1UECgwKQ3JhenktQ2F0czETMBEGA1UEAwwKdGVz
dC5sb2NhbDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALXcSLHkvB99
OBpf57gJTYwppQrsV1yUWJp1qKXCD164GlIul0YnBRLaaxZUfWIJaxEaWz5DFR4B
zR/MlaxUKx0A7fASyRNVA4NMGclt1WJR5fglHih8p+SIE1feVkMdeUftJnEJXVY1
DlZ9Cw7+nrkAmh5s8QEcVgak4kB4/6sFcMhrfa6tNcnhuM7Yq3USZf2a9CDpj78m
8NvIlXN4+bk9fgCSN9vOLzRuMMYT8mRy0fqXdNQcEE0i+SJ6epbsRvXmRdugd3a8
aaLE2Bc5huyaoc//ZneJY5iVyTldSo3eyj88/NvdhApExzeLb8T9Hey6DP/chDfu
KKASiisRL9kPKB++PTt3Suxy+Ary+MJ1uLJLIberb3qWFYyU7i1o3Ex7mBFIpCdW
SXw321cNYapoxMApeyMUWxn9j8GuHRAcARj2FDKIQasY6AqmQ8vL+MGFLqKaxqcD
QDu+8le4EUwNGxkVIINTYzZttsQF2zJ/DPhErbT0rz5TUn5TKzHeReUv+lFhre7E
2bIcocTumbxBuBMNE+1b09IPmdPZCMS8bRd4vQ+4QRHweyFHCHOQHjWN10uLpZhm
Mugq9spxIatQ4jsmuQHSM76f91CH6BvVx+gUNaMcFLkDjPUyrn6gov6SbcBQMuUO
gw4H4moFF+mtiivhvnWYWXRvlqPd9bR9AgMBAAGjfzB9MB8GA1UdIwQYMBaAFICs
29k3J5Pnza2xgkgycaxBxAcSMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgTwMCMGA1Ud
EQQcMBqCC3Rlc3QyLmxvY2Fsggt0ZXN0My5sb2NhbDAdBgNVHQ4EFgQUr4a7BDH8
d4ZlrR3qcOMvczkWEqAwDQYJKoZIhvcNAQELBQADggIBAEwk+wOrXsYbPl+3IgJi
izJAapA5BI/DoX5KUxRgXByNEZeIYlmn0GAj2odxxV8+AnjsIFQGp+76e/s6LpJG
ImipyyZ+XCXSqYoyEaUn0KaJPMN0GQbOv/a/kM0BEYUGLkaJLzlGS3Hg0hhqhlYi
dx6xr92rZWW7HGSliv3QghGsXmQYBJ8rSq9HEpv/RAIm95H6BHltQ1FBMRssDnBB
Ur+WTQTzj5gVqxVKvPOzXxc+V3cs9Nozk+wzuW6KUiBPcfQcxdnlm5wToj/k75i9
iRW5E2Qqe+skmLQDxiTOJmgoZxtjGtVMC20J/XYF/mBEJqr+bND1W/8Tp/pUKzpF
z4SIzP/m2TpPH8Agnvie0Z6nVYgAZpiuBEd7JrPm0kWtuKgL3ER+67M/PjpYYmNx
A/gjmQgz7+7uAk8PhloWKbBOC/2wxk0jkFazRBk6e9RveUR6PBvBpo+nyQSgDQkD
dGyzvIAYcFTFXznSv8cu9wzizwGXJ0EdxSo+oWcsGzq3rhjzD+tROCqS7X1eVF4j
USrygPv8w4UT3SFoiABp1ThDjCheROLISAo/SIag8XUw58bmmiDRuVPLZDQfYBYu
KgSLJpFB99QMzvhkNBhImEpmWRrKAFSmwNG4/l/yR19udd4AxpjH62LT8mUZOf23
dQ6pRi1hNlM1v6ZvFDdq8Rgw
-----END CERTIFICATE-----

View file

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
uG1j1Nb+oUNaeAMG8+ojFs1tLXHtStI=
-----END PRIVATE KEY-----

View file

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFgTCCA2mgAwIBAgIUVTm2kFBiacDG3Om6JFaZKvJ6CScwDQYJKoZIhvcNAQEL
BQAwUDELMAkGA1UEBhMCREUxCzAJBgNVBAgMAlNIMQswCQYDVQQHDAJISDETMBEG
A1UECgwKQ3JhenktQ2F0czESMBAGA1UEAwwJdGVzdC5ob21lMB4XDTI0MTEyMjE4
NTc0MFoXDTI1MTEyMjE4NTc0MFowUDELMAkGA1UEBhMCREUxCzAJBgNVBAgMAlNI
MQswCQYDVQQHDAJISDETMBEGA1UECgwKQ3JhenktQ2F0czESMBAGA1UEAwwJdGVz
dC5ob21lMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnnYaTE8TKKTu
IE2hcHwHm0RnM+4VwPnNT6q6b4oYSJJeGJCbQYt8CAAkBxvY3j1H0xP6imD7ULUK
ymx2fQiHK+bRAdXyoguLHaPYWPInfUyHp9Y2w2AglKCG/U2paMni4xL0LH4N3V1r
x5hQG9ORwJzVH5wMiYoETzgbd1ED7G0tVuKYrH84Ma8znEXVZ4XfAlDfYEGKPNDN
dZDkFEQFYHb5RSPB6ym3vrbZJfLkNy01m/Cpdj3/GqJ460zo7x/apzVPNj/khW1v
fME8c8sz1LqEbQBVdUU9xN8DfTjT/z1NHA5S/1O7Yb9Z0t1tjvs3u/iyCjBNMbd5
7FNqMVjXFjENav21zrrr0LAiUfBcbokQZD72/j053gXz9LkcTjLvkPN3i1oVfNIA
b/Ce4hHzpWk2kvZuFyqfKj8Yc8oTfjr88sGxe1JCgbhnCvkw/5d7hy7OJVG4mTIH
WliOY92R7xPwEBhqc+A4VYqhAIrp+Qzen+XGrBih+PneEsSWVfM3PyjKt/qURK4j
gWCGFIEI80xyFaHhwHpeKszZcMOVAHjV3Ik91wutL/HoZ0r5uPYvwqQb6QZ7ubqX
FWiuNUf970TFS7eIA33Xr0IojPBziinU3/uYnJBODR4Sl2npijzwqsRrGsrSDUF7
cxxhsyv64ri2p3u4wnP4faLf1Dtk3kUCAwEAAaNTMFEwHQYDVR0OBBYEFICs29k3
J5Pnza2xgkgycaxBxAcSMB8GA1UdIwQYMBaAFICs29k3J5Pnza2xgkgycaxBxAcS
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAFiqNg1yXlajI1ZJ
l4qXADvi1xpFLEn17162jN+zb6DgRRohqwrsP71fAqyuCticYELk3gBHt8KizEBj
xPSggahX+ZIGG5/Tnb5cQ61y0GMZHWyABHjgiOGi26Gxar5wi7ET6W4D6w09u8U3
wd8q8U164aSj6Rh153S0r8SJJOhqtiZhcYllOMB8SNgjxodlco+YvCGoqkhPdJef
H2S02mgbjxMlo8P4ivoirD7boLirlXkoNidmaWvC/hD4ZwsTWMrRnHL1S5DEhKef
WSgtX3tUEvHrEyqsGcSn49l4CNE6Xbx5wWo+4c9bs642f7u34OoUnitYmGZDzT1d
zQOyBirn9k6fB/SX7Ug+PF6+KbJCXEdWffgNqe5HAG8Lo2EOEnocp3kncRMSq3zY
qzbTBqaWI512cqN2RA+Q0NPzVH01jPG8yKaSxVzf4Sp1Iqjl7fe9WZL9/L4DJZ7o
QWRPZvwH5Rz8utGUylfe9LxSisX7xZMNoGLqsQfanowqZCiS1M1GRoHsILnFuFNw
3ONu4qp0Gr3+PxsKoE6NBstqD6Lkrm0uf/IhC7fwIbdq3qqe0E4xZou9W9nkmapw
0iKhdhCtnp3HsF6f2Kc0ipyPxVXDnP5TCx4NLst2RvoYryUBwqIKfwU8X3yOBmhq
HowJo/ZfrlzZf8753cfF8Kza6NUA
-----END CERTIFICATE-----

View file

@ -0,0 +1 @@
321CB37DF1D729A4277D7FDC91BC77299F40557B