Skip to content

Commit 61bc84b

Browse files
author
Eugene Bochilo
committed
Fix BasicConstraints extension validation
DEVSIX-8420
1 parent 4837237 commit 61bc84b

File tree

10 files changed

+301
-12
lines changed

10 files changed

+301
-12
lines changed

sign/pom.xml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212

1313
<name>iText - sign</name>
1414
<url>https://itextpdf.com/</url>
15-
<properties>
16-
<sonar.coverage.exclusions>**/validation/v1/**/*,**/cms/SignerInfo.java</sonar.coverage.exclusions>
17-
</properties>
15+
1816
<dependencies>
1917
<dependency>
2018
<groupId>com.itextpdf</groupId>

sign/src/main/java/com/itextpdf/signatures/validation/v1/CRLValidator.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ This file is part of the iText (R) project.
3838
import com.itextpdf.signatures.validation.v1.context.CertificateSource;
3939
import com.itextpdf.signatures.validation.v1.context.ValidationContext;
4040
import com.itextpdf.signatures.validation.v1.context.ValidatorContext;
41-
import com.itextpdf.signatures.validation.v1.extensions.BasicConstraintsExtension;
41+
import com.itextpdf.signatures.validation.v1.extensions.DynamicBasicConstraintsExtension;
4242
import com.itextpdf.signatures.validation.v1.report.CertificateReportItem;
4343
import com.itextpdf.signatures.validation.v1.report.ReportItem;
4444
import com.itextpdf.signatures.validation.v1.report.ReportItem.ReportItemStatus;
@@ -174,7 +174,8 @@ public void validate(ValidationReport report, ValidationContext context, X509Cer
174174
IDistributionPoint distributionPoint = null;
175175
if (!issuingDistPoint.isNull()) {
176176
// Verify that certificate is in the CRL scope using IDP extension.
177-
boolean basicConstraintsCaAsserted = new BasicConstraintsExtension(true).existsInCertificate(certificate);
177+
boolean basicConstraintsCaAsserted = new DynamicBasicConstraintsExtension().withCertificateChainSize(1)
178+
.existsInCertificate(certificate);
178179
if ((issuingDistPoint.onlyContainsUserCerts() && basicConstraintsCaAsserted) ||
179180
(issuingDistPoint.onlyContainsCACerts() && !basicConstraintsCaAsserted)) {
180181
report.addReportItem(new CertificateReportItem(certificate, CRL_CHECK,

sign/src/main/java/com/itextpdf/signatures/validation/v1/CertificateChainValidator.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ This file is part of the iText (R) project.
3030
import com.itextpdf.signatures.validation.v1.context.ValidationContext;
3131
import com.itextpdf.signatures.validation.v1.context.ValidatorContext;
3232
import com.itextpdf.signatures.validation.v1.extensions.CertificateExtension;
33+
import com.itextpdf.signatures.validation.v1.extensions.DynamicCertificateExtension;
3334
import com.itextpdf.signatures.validation.v1.report.CertificateReportItem;
3435
import com.itextpdf.signatures.validation.v1.report.ValidationReport;
3536
import com.itextpdf.signatures.validation.v1.report.ReportItem.ReportItemStatus;
@@ -152,9 +153,14 @@ public ValidationReport validateCertificate(ValidationContext context, X509Certi
152153
*/
153154
public ValidationReport validate(ValidationReport result, ValidationContext context, X509Certificate certificate,
154155
Date validationDate) {
156+
return validate(result, context, certificate, validationDate, 0);
157+
}
158+
159+
private ValidationReport validate(ValidationReport result, ValidationContext context, X509Certificate certificate,
160+
Date validationDate, int certificateChainSize) {
155161
ValidationContext localContext = context.setValidatorContext(ValidatorContext.CERTIFICATE_CHAIN_VALIDATOR);
156162
validateValidityPeriod(result, certificate, validationDate);
157-
validateRequiredExtensions(result, localContext, certificate);
163+
validateRequiredExtensions(result, localContext, certificate, certificateChainSize);
158164
if (stopValidation(result, localContext)) {
159165
return result;
160166
}
@@ -168,7 +174,7 @@ public ValidationReport validate(ValidationReport result, ValidationContext cont
168174
if (stopValidation(result, localContext)) {
169175
return result;
170176
}
171-
validateChain(result, localContext, certificate, validationDate);
177+
validateChain(result, localContext, certificate, validationDate, certificateChainSize);
172178
return result;
173179
}
174180

@@ -263,10 +269,13 @@ private void validateValidityPeriod(ValidationReport result, X509Certificate cer
263269
}
264270

265271
private void validateRequiredExtensions(ValidationReport result, ValidationContext context,
266-
X509Certificate certificate) {
272+
X509Certificate certificate, int certificateChainSize) {
267273
List<CertificateExtension> requiredExtensions = properties.getRequiredExtensions(context);
268274
if (requiredExtensions != null) {
269275
for (CertificateExtension requiredExtension : requiredExtensions) {
276+
if (requiredExtension instanceof DynamicCertificateExtension) {
277+
((DynamicCertificateExtension) requiredExtension).withCertificateChainSize(certificateChainSize);
278+
}
270279
if (!requiredExtension.existsInCertificate(certificate)) {
271280
result.addReportItem(new CertificateReportItem(certificate, EXTENSIONS_CHECK,
272281
MessageFormatUtil.format(EXTENSION_MISSING, requiredExtension.getExtensionOid()),
@@ -285,7 +294,7 @@ private void validateRevocationData(ValidationReport report, ValidationContext c
285294
}
286295

287296
private void validateChain(ValidationReport result, ValidationContext context, X509Certificate certificate,
288-
Date validationDate) {
297+
Date validationDate, int certificateChainSize) {
289298
X509Certificate issuerCertificate = null;
290299
try {
291300
issuerCertificate =
@@ -314,6 +323,6 @@ private void validateChain(ValidationReport result, ValidationContext context, X
314323
return;
315324
}
316325
this.validate(result, context.setCertificateSource(CertificateSource.CERT_ISSUER),
317-
issuerCertificate, validationDate);
326+
issuerCertificate, validationDate, certificateChainSize + 1);
318327
}
319328
}

sign/src/main/java/com/itextpdf/signatures/validation/v1/SignatureValidationProperties.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ This file is part of the iText (R) project.
3636
import com.itextpdf.signatures.validation.v1.context.ValidationContext;
3737
import com.itextpdf.signatures.validation.v1.context.ValidatorContext;
3838
import com.itextpdf.signatures.validation.v1.context.ValidatorContexts;
39-
import com.itextpdf.signatures.validation.v1.extensions.BasicConstraintsExtension;
4039
import com.itextpdf.signatures.validation.v1.extensions.CertificateExtension;
40+
import com.itextpdf.signatures.validation.v1.extensions.DynamicBasicConstraintsExtension;
4141
import com.itextpdf.signatures.validation.v1.extensions.ExtendedKeyUsageExtension;
4242
import com.itextpdf.signatures.validation.v1.extensions.KeyUsage;
4343
import com.itextpdf.signatures.validation.v1.extensions.KeyUsageExtension;
@@ -86,7 +86,7 @@ public SignatureValidationProperties() {
8686
Collections.<CertificateExtension>singletonList(new KeyUsageExtension(KeyUsage.NON_REPUDIATION)));
8787
List<CertificateExtension> certIssuerRequiredExtensions = new ArrayList<>();
8888
certIssuerRequiredExtensions.add(new KeyUsageExtension(KeyUsage.KEY_CERT_SIGN));
89-
certIssuerRequiredExtensions.add(new BasicConstraintsExtension(true));
89+
certIssuerRequiredExtensions.add(new DynamicBasicConstraintsExtension());
9090
setRequiredExtensions(CertificateSources.of(CertificateSource.CERT_ISSUER), certIssuerRequiredExtensions);
9191
setRequiredExtensions(CertificateSources.of(CertificateSource.TIMESTAMP),
9292
Collections.<CertificateExtension>singletonList(new ExtendedKeyUsageExtension(

sign/src/main/java/com/itextpdf/signatures/validation/v1/extensions/BasicConstraintsExtension.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ This file is part of the iText (R) project.
3232

3333
/**
3434
* Class representing "Basic Constraints" certificate extension.
35+
*
36+
* @deprecated since 8.0.5. To be removed.
3537
*/
38+
@Deprecated
3639
public class BasicConstraintsExtension extends CertificateExtension {
3740
private static final IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.getFactory();
3841

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.itextpdf.signatures.validation.v1.extensions;
2+
3+
import com.itextpdf.bouncycastleconnector.BouncyCastleFactoryCreator;
4+
import com.itextpdf.commons.bouncycastle.IBouncyCastleFactory;
5+
import com.itextpdf.signatures.CertificateUtil;
6+
import com.itextpdf.signatures.OID;
7+
8+
import java.io.IOException;
9+
import java.security.cert.X509Certificate;
10+
11+
/**
12+
* Class representing "Basic Constraints" certificate extension,
13+
* which uses provided amount of certificates in chain during the comparison.
14+
*/
15+
public class DynamicBasicConstraintsExtension extends DynamicCertificateExtension {
16+
private static final IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.getFactory();
17+
18+
/**
19+
* Create new instance of {@link DynamicBasicConstraintsExtension}.
20+
*/
21+
public DynamicBasicConstraintsExtension() {
22+
super(OID.X509Extensions.BASIC_CONSTRAINTS, FACTORY.createBasicConstraints(true).toASN1Primitive());
23+
}
24+
25+
/**
26+
* Check if this extension is present in the provided certificate.
27+
* In case of {@link DynamicBasicConstraintsExtension}, check if path length for this extension is less or equal
28+
* to the path length, specified in the certificate.
29+
*
30+
* @param certificate {@link X509Certificate} in which this extension shall be present
31+
*
32+
* @return {@code true} if this path length is less or equal to a one from the certificate, {@code false} otherwise
33+
*/
34+
@Override
35+
public boolean existsInCertificate(X509Certificate certificate) {
36+
try {
37+
if (CertificateUtil.getExtensionValue(certificate, OID.X509Extensions.BASIC_CONSTRAINTS) == null) {
38+
return false;
39+
}
40+
} catch (IOException e) {
41+
return false;
42+
}
43+
return certificate.getBasicConstraints() >= getCertificateChainSize();
44+
}
45+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.itextpdf.signatures.validation.v1.extensions;
2+
3+
import com.itextpdf.commons.bouncycastle.asn1.IASN1Primitive;
4+
5+
/**
6+
* Certificate extension which is populated with additional dynamically changing validation related information.
7+
*/
8+
public class DynamicCertificateExtension extends CertificateExtension {
9+
10+
private int certificateChainSize;
11+
12+
/**
13+
* Create new instance of {@link CertificateExtension} using provided extension OID and value.
14+
*
15+
* @param extensionOid {@link String}, which represents extension OID
16+
* @param extensionValue {@link IASN1Primitive}, which represents extension value
17+
*/
18+
public DynamicCertificateExtension(String extensionOid, IASN1Primitive extensionValue) {
19+
super(extensionOid, extensionValue);
20+
}
21+
22+
/**
23+
* Sets amount of certificates currently present in the chain.
24+
*
25+
* @param certificateChainSize amount of certificates currently present in the chain
26+
*
27+
* @return this {@link DynamicCertificateExtension} instance
28+
*/
29+
public DynamicCertificateExtension withCertificateChainSize(int certificateChainSize) {
30+
this.certificateChainSize = certificateChainSize;
31+
return this;
32+
}
33+
34+
/**
35+
* Gets amount of certificates currently present in the chain.
36+
*
37+
* @return amount of certificates currently present in the chain
38+
*/
39+
public int getCertificateChainSize() {
40+
return certificateChainSize;
41+
}
42+
}

sign/src/test/java/com/itextpdf/signatures/validation/v1/CertificateChainValidatorTest.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,71 @@ public void validChainTest() throws CertificateException, IOException {
110110
));
111111
}
112112

113+
@Test
114+
public void validNumericBasicConstraintsTest() throws CertificateException, IOException {
115+
String chainName = CERTS_SRC + "signChainWithValidNumericBasicConstraints.pem";
116+
Certificate[] certificateChain = PemFileHelper.readFirstChain(chainName);
117+
X509Certificate signingCert = (X509Certificate) certificateChain[0];
118+
X509Certificate intermediateCert = (X509Certificate) certificateChain[1];
119+
X509Certificate rootCert = (X509Certificate) certificateChain[2];
120+
121+
CertificateChainValidator validator = validatorChainBuilder.buildCertificateChainValidator();
122+
certificateRetriever.addKnownCertificates(Collections.<Certificate>singletonList(intermediateCert));
123+
certificateRetriever.setTrustedCertificates(Collections.<Certificate>singletonList(rootCert));
124+
125+
ValidationReport report =
126+
validator.validateCertificate(baseContext, signingCert, TimeTestUtil.TEST_DATE_TIME);
127+
AssertValidationReport.assertThat(report, a -> a
128+
.hasStatus(ValidationResult.VALID)
129+
.hasNumberOfFailures(0)
130+
.hasNumberOfLogs(1)
131+
.hasLogItem(la -> la
132+
.withCheckName(CertificateChainValidator.CERTIFICATE_CHECK)
133+
.withMessage("Certificate {0} is trusted, revocation data checks are not required.",
134+
l -> rootCert.getSubjectX500Principal())
135+
.withCertificate(rootCert)
136+
));
137+
}
138+
139+
@Test
140+
public void invalidNumericBasicConstraintsTest() throws CertificateException, IOException {
141+
String chainName = CERTS_SRC + "signChainWithInvalidNumericBasicConstraints.pem";
142+
Certificate[] certificateChain = PemFileHelper.readFirstChain(chainName);
143+
X509Certificate signingCert = (X509Certificate) certificateChain[0];
144+
X509Certificate intermediateCert = (X509Certificate) certificateChain[1];
145+
X509Certificate rootCert = (X509Certificate) certificateChain[2];
146+
147+
CertificateChainValidator validator = validatorChainBuilder.buildCertificateChainValidator();
148+
certificateRetriever.addKnownCertificates(Collections.<Certificate>singletonList(intermediateCert));
149+
certificateRetriever.setTrustedCertificates(Collections.<Certificate>singletonList(rootCert));
150+
151+
ValidationReport report =
152+
validator.validateCertificate(baseContext, signingCert, TimeTestUtil.TEST_DATE_TIME);
153+
AssertValidationReport.assertThat(report, a -> a
154+
.hasStatus(ValidationResult.INVALID)
155+
.hasNumberOfFailures(2)
156+
.hasNumberOfLogs(3)
157+
.hasLogItem(la -> la
158+
.withCheckName(CertificateChainValidator.CERTIFICATE_CHECK)
159+
.withMessage("Certificate {0} is trusted, revocation data checks are not required.",
160+
l -> rootCert.getSubjectX500Principal())
161+
.withCertificate(rootCert)
162+
)
163+
.hasLogItem(la -> la
164+
.withCheckName(CertificateChainValidator.EXTENSIONS_CHECK)
165+
.withMessage(CertificateChainValidator.EXTENSION_MISSING,
166+
l -> "2.5.29.19")
167+
.withCertificate(rootCert)
168+
)
169+
.hasLogItem(la -> la
170+
.withCheckName(CertificateChainValidator.EXTENSIONS_CHECK)
171+
.withMessage(CertificateChainValidator.EXTENSION_MISSING,
172+
l -> "2.5.29.19")
173+
.withCertificate(intermediateCert)
174+
)
175+
);
176+
}
177+
113178
@Test
114179
public void revocationValidationCallTest() throws CertificateException, IOException {
115180
String chainName = CERTS_SRC + "chain.pem";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDczCCAlugAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwVDELMAkGA1UEBhMCQkUx
3+
DjAMBgNVBAoMBWlUZXh0MTUwMwYDVQQDDCxpVGV4dFRlc3RJbnZhbGlkQmFzaWND
4+
b25zdHJhaW50c0ludGVybWVkaWF0ZTAgFw0wMDAxMDEwMDAwMDBaGA8yNTAwMDEw
5+
MTAwMDAwMFowTzELMAkGA1UEBhMCQkUxDjAMBgNVBAoMBWlUZXh0MTAwLgYDVQQD
6+
DCdpVGV4dFRlc3RJbnZhbGlkQmFzaWNDb25zdHJhaW50c1NpZ25pbmcwggEiMA0G
7+
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDk0UKW8iOW/YPocIBpaICH96wbdhwd
8+
9O1spY/7LA6JHLVZgSRIB3if5sU3tNclN3uO2NPiQhZXCMDJP+ISdQGCCSeRCo0n
9+
OXu46984qB4+YrlT3ScpAQ0uZY943PYTcBVnUYMYA+nFRZhtJ8aLJpBl5ND08ye0
10+
TlEWTU0RmXNXhUbhAuwWdAHaDTDYGyJ+Bmr3ob/ktILtHwzS8rkGw+FRY+bty4BD
11+
mJTGiSNR/c2MdUikXOVzoybRX7BqvvHlU78XZTtGxwIJ17oorZBgk/8AUMSCe+xN
12+
2Zr13ucerbTnRLNBo1lbHIb4jWH5aq7BLdah9qEPOMTTc2aGow3yR9U1AgMBAAGj
13+
UjBQMB0GA1UdDgQWBBQkLmgOAQOWlcnrtVUv57Rj05TsRTAfBgNVHSMEGDAWgBQ3
14+
AGQi1plfpKj08MVTNx54NFkCczAOBgNVHQ8BAf8EBAMCBkAwDQYJKoZIhvcNAQEL
15+
BQADggEBADHWfy2/K8cd1P1ijelp/KOj92ESC1i1seo4tGfRreM5+VskkIbix2Dl
16+
8/Gpy1/Igw9jMoOdPyyqj72mBO2aiEikNL8+PQgKjK7WVF7MLdWPtE2h77fiQN3o
17+
J1ybtlrasOSmlSKxixyFHL2caBjLC/YiYU28wuAGkDqSb3bug6JKz0U+KP5bQr1Y
18+
+irR91kcpiIAfdeDmN1NLa+IHcwAHzZNtc334map1vXy4AVbox1oee+pICkgyAgM
19+
XGuUMqts5sfBVCeZ4YXmtFEFhypo9sZgQ82yj8D/n53oyOfGok9sZtQO5d/KL523
20+
6UISiCz3koNIaqnFJejITnmHrE7SgvE=
21+
-----END CERTIFICATE-----
22+
-----BEGIN CERTIFICATE-----
23+
MIIDhDCCAmygAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwTDELMAkGA1UEBhMCQkUx
24+
DjAMBgNVBAoMBWlUZXh0MS0wKwYDVQQDDCRpVGV4dFRlc3RJbnZhbGlkQmFzaWND
25+
b25zdHJhaW50c1Jvb3QwIBcNMDAwMTAxMDAwMDAwWhgPMjUwMDAxMDEwMDAwMDBa
26+
MFQxCzAJBgNVBAYTAkJFMQ4wDAYDVQQKDAVpVGV4dDE1MDMGA1UEAwwsaVRleHRU
27+
ZXN0SW52YWxpZEJhc2ljQ29uc3RyYWludHNJbnRlcm1lZGlhdGUwggEiMA0GCSqG
28+
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCf1b92W/Yeitn6iwuZHmfF7cw740VaqHGO
29+
AavE6DhwVCrfVlWDvamF+Sewnf2KuIxeWaYciy5TQ/nIa8CcD3qDJsYjYUK6L+Ro
30+
E+Lb5YMKOEUpc2p4XZvLXmOIOl3AEe2AnIDxREk1gfu6BYFyzmju8rHLSy7ua/I5
31+
672qude/EPGG+jyb+ajIbyNSsL65hkOrJUcJtlRIhJQVCZ39hcNdA8lRBrVzq1NW
32+
gnExyFsqlIYpAuD1IsnS/r1kMOzjPai/AhPOqpnB6ogZp+Dr99wYY57Dv9BdfvKt
33+
guX608D5m0Rp3yrEQ/X5m9frTvp09MCHKMU+X9+14LZiaod3AtQVAgMBAAGjZjBk
34+
MB0GA1UdDgQWBBQ3AGQi1plfpKj08MVTNx54NFkCczAfBgNVHSMEGDAWgBTvssgF
35+
UYUg7luFw9zAr9tZuQouvDASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQE
36+
AwICBDANBgkqhkiG9w0BAQsFAAOCAQEANzdFjm4iasZpvPwPEeoulmGr33Ofp0L+
37+
IQ03ZYjgUDSx3xuUhfpZHgmEcoovQz6IGw0wlp4W7HJ8rqaj3aLEte7ZXF2mpaTQ
38+
vGU3suY3SXTLTZq2TinA0KEToaObX9XLVTJTe6GNMcNf1uAQnQVzOgm3kNpnqcLp
39+
QyNZrUH+q4q6gO9EcdMAq4oFXvqPkxYy01B9eHChrASWV3/CsbuyLUDnY1ZduGGZ
40+
yXF8/NiAgsYQk12cryo5sbkHzpbH62zSwdvzf4s/dp95Tub9bxdta7NYVJL0eOe7
41+
nTMcY6TMT7FXFIkpZ3nr2zEonGMf+zM5qG2crqtteHGbWqmp3f8R/Q==
42+
-----END CERTIFICATE-----
43+
-----BEGIN CERTIFICATE-----
44+
MIIDfDCCAmSgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwTDELMAkGA1UEBhMCQkUx
45+
DjAMBgNVBAoMBWlUZXh0MS0wKwYDVQQDDCRpVGV4dFRlc3RJbnZhbGlkQmFzaWND
46+
b25zdHJhaW50c1Jvb3QwIBcNMDAwMTAxMDAwMDAwWhgPMjUwMDAxMDEwMDAwMDBa
47+
MEwxCzAJBgNVBAYTAkJFMQ4wDAYDVQQKDAVpVGV4dDEtMCsGA1UEAwwkaVRleHRU
48+
ZXN0SW52YWxpZEJhc2ljQ29uc3RyYWludHNSb290MIIBIjANBgkqhkiG9w0BAQEF
49+
AAOCAQ8AMIIBCgKCAQEA3ktft1LujBsmfphuvEC2LpRfz41pleSyi2NRQKK6wQZE
50+
SDMAn94fVS43+sJkiSGrJuc+5VGe0Cy17RU8SVVnXy4/AVBvr/YrLjGfNTuCW+A/
51+
pBJEyLJ/8dSkmT2jBX8KpcUyrjHi8Q7OIUae3Hu5WBz4hcmTYoXfcEQwamoiuqx4
52+
nKtH+CEOZ4rtG2zxDSRhw7HishUYc0U886txYVtZJ+PfCzblntecsNNaucJCCHZB
53+
H0aF9yHsleumVL2i24ELHY5izNHH75rM4kpWFPUXD9dMwU7UUDpL9RoW92HpG7X/
54+
CTDPZGzHRt8srUtFH4S2b0cI0lCW0eW7dkmIYleWQwIDAQABo2YwZDAdBgNVHQ4E
55+
FgQU77LIBVGFIO5bhcPcwK/bWbkKLrwwHwYDVR0jBBgwFoAU77LIBVGFIO5bhcPc
56+
wK/bWbkKLrwwEgYDVR0TAQH/BAgwBgEB/wIBATAOBgNVHQ8BAf8EBAMCAQYwDQYJ
57+
KoZIhvcNAQELBQADggEBAA3Nyit5cNUHE9hVpEZ4mkpYoHDiw7NfHqmuhRC4Gnsy
58+
oVaVgQmDJH52EwWLqpZkQCSr3gwJv+OZZ1oR+LW1qRSyXqIcoLzyuxj3sBclbwzE
59+
XCknvNT1D2Kbd2mubz37GwwdzN4T59lWO371aIT4uoYWaTIdkYFCiONydfsKzBsS
60+
uEGb1z94pZ71Kt00jwrG8CP841Cdw5RRsb39AAI9k8Gy4+g24HZeLI+Z4/ZlmsFG
61+
XNgx5PDZpNZ5W+xlEc5W7ksL675AoD51yoW9d/J7DXg7NI5ur5MxOUf+T0wQh/W5
62+
9uwx924xwDeeHdLBxdZHsN9v4eVIxELr9huFrN9SbRQ=
63+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)