Skip to content

Commit b51c25b

Browse files
committed
Add the ability to set trusted certificates store for getting missing certificates
DEVSIX-7934
1 parent 9925228 commit b51c25b

File tree

12 files changed

+339
-18
lines changed

12 files changed

+339
-18
lines changed

sign/src/main/java/com/itextpdf/signatures/DefaultIssuingCertificateRetriever.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ This file is part of the iText (R) project.
2424

2525
import java.security.cert.CRL;
2626
import java.security.cert.Certificate;
27+
import java.util.Collection;
2728

2829
/**
2930
* Empty {@link IIssuingCertificateRetriever} implementation for compatibility with the older code.
@@ -58,4 +59,14 @@ public Certificate[] retrieveMissingCertificates(Certificate[] chain) {
5859
public Certificate[] getCrlIssuerCertificates(CRL crl) {
5960
return new Certificate[0];
6061
}
62+
63+
/**
64+
* {@inheritDoc}
65+
*
66+
* @param certificates {@inheritDoc}
67+
*/
68+
@Override
69+
public void setTrustedCertificates(Collection<Certificate> certificates) {
70+
// Do nothing.
71+
}
6172
}

sign/src/main/java/com/itextpdf/signatures/IIssuingCertificateRetriever.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ This file is part of the iText (R) project.
2424

2525
import java.security.cert.CRL;
2626
import java.security.cert.Certificate;
27+
import java.util.Collection;
2728

2829
/**
2930
* Interface helper to support retrieving CAIssuers certificates from Authority Information Access (AIA) Extension in
@@ -49,4 +50,12 @@ public interface IIssuingCertificateRetriever {
4950
* @return certificates retrieved from CRL AIA extension or an empty list in case certificates cannot be retrieved.
5051
*/
5152
Certificate[] getCrlIssuerCertificates(CRL crl);
53+
54+
/**
55+
* Sets trusted certificate list to be used for the missing certificates retrieving by the issuer name.
56+
*
57+
* @param certificates certificate list for getting missing certificates in chain
58+
* or CRL response issuer certificates.
59+
*/
60+
void setTrustedCertificates(Collection<Certificate> certificates);
5261
}

sign/src/main/java/com/itextpdf/signatures/IssuingCertificateRetriever.java

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,25 @@ This file is part of the iText (R) project.
3030
import java.security.cert.CRL;
3131
import java.security.cert.Certificate;
3232
import java.security.cert.CertificateException;
33+
import java.security.cert.X509CRL;
3334
import java.security.cert.X509Certificate;
3435
import java.util.ArrayList;
3536
import java.util.Collection;
37+
import java.util.HashMap;
3638
import java.util.List;
39+
import java.util.Map;
3740
import org.slf4j.Logger;
3841
import org.slf4j.LoggerFactory;
3942

4043
/**
4144
* {@link IIssuingCertificateRetriever} default implementation.
4245
*/
4346
public class IssuingCertificateRetriever implements IIssuingCertificateRetriever {
44-
47+
4548
private static final Logger LOGGER = LoggerFactory.getLogger(IssuingCertificateRetriever.class);
4649

50+
private final Map<String, Certificate> certificateMap = new HashMap<>();
51+
4752
/**
4853
* Creates {@link IssuingCertificateRetriever} instance.
4954
*/
@@ -65,28 +70,34 @@ public Certificate[] retrieveMissingCertificates(Certificate[] chain) {
6570
fullChain.add(signingCertificate);
6671

6772
int i = 1;
68-
Certificate lastAddedCert = signingCertificate;
69-
while (!CertificateUtil.isSelfSigned((X509Certificate) lastAddedCert)) {
73+
X509Certificate lastAddedCert = signingCertificate;
74+
while (!CertificateUtil.isSelfSigned(lastAddedCert)) {
7075
// Check if there are any missing certificates with isSignedByNext
7176
if (i < chain.length &&
72-
CertificateUtil.isIssuerCertificate((X509Certificate) lastAddedCert, (X509Certificate) chain[i])) {
77+
CertificateUtil.isIssuerCertificate(lastAddedCert, (X509Certificate) chain[i])) {
7378
fullChain.add(chain[i]);
7479
i++;
7580
} else {
7681
// Get missing certificates using AIA Extensions
77-
String url = CertificateUtil.getIssuerCertURL((X509Certificate) lastAddedCert);
82+
String url = CertificateUtil.getIssuerCertURL(lastAddedCert);
7883
Collection<Certificate> certificatesFromAIA = processCertificatesFromAIA(url);
7984
if (certificatesFromAIA == null || certificatesFromAIA.isEmpty()) {
80-
// Unable to retrieve missing certificates
81-
while (i < chain.length) {
82-
fullChain.add(chain[i]);
83-
i++;
85+
// Retrieve Issuer from the certificate store
86+
Certificate issuer = certificateMap.get(lastAddedCert.getIssuerX500Principal().getName());
87+
if (issuer == null) {
88+
// Unable to retrieve missing certificates
89+
while (i < chain.length) {
90+
fullChain.add(chain[i]);
91+
i++;
92+
}
93+
return fullChain.toArray(new Certificate[0]);
8494
}
85-
return fullChain.toArray(new Certificate[0]);
95+
fullChain.add(issuer);
96+
} else {
97+
fullChain.addAll(certificatesFromAIA);
8698
}
87-
fullChain.addAll(certificatesFromAIA);
8899
}
89-
lastAddedCert = fullChain.get(fullChain.size() - 1);
100+
lastAddedCert = (X509Certificate) fullChain.get(fullChain.size() - 1);
90101
}
91102

92103
return fullChain.toArray(new Certificate[0]);
@@ -110,8 +121,28 @@ public Certificate[] getCrlIssuerCertificates(CRL crl) {
110121
// AIA Extension
111122
String url = CertificateUtil.getIssuerCertURL(crl);
112123
List<Certificate> certificatesFromAIA = (List<Certificate>) processCertificatesFromAIA(url);
113-
return certificatesFromAIA == null ? new Certificate[0] :
114-
retrieveMissingCertificates(certificatesFromAIA.toArray(new Certificate[0]));
124+
if (certificatesFromAIA == null) {
125+
// Retrieve Issuer from the certificate store
126+
Certificate issuer = certificateMap.get(((X509CRL) crl).getIssuerX500Principal().getName());
127+
if (issuer == null) {
128+
// Unable to retrieve CRL issuer
129+
return new Certificate[0];
130+
}
131+
return retrieveMissingCertificates(new Certificate[]{issuer});
132+
}
133+
return retrieveMissingCertificates(certificatesFromAIA.toArray(new Certificate[0]));
134+
}
135+
136+
/**
137+
* {@inheritDoc}
138+
*
139+
* @param certificates {@inheritDoc}
140+
*/
141+
@Override
142+
public void setTrustedCertificates(Collection<Certificate> certificates) {
143+
for (Certificate certificate : certificates) {
144+
certificateMap.put(((X509Certificate) certificate).getSubjectX500Principal().getName(), certificate);
145+
}
115146
}
116147

117148
/**

sign/src/main/java/com/itextpdf/signatures/PdfPadesSigner.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,13 +399,27 @@ public PdfPadesSigner setExternalDigest(IExternalDigest externalDigest) {
399399
*
400400
* @param issuingCertificateRetriever {@link IIssuingCertificateRetriever} instance to be used for getting missing
401401
* certificates in chain or CRL response issuer certificates.
402+
*
402403
* @return same instance of {@link PdfPadesSigner}.
403404
*/
404405
public PdfPadesSigner setIssuingCertificateRetriever(IIssuingCertificateRetriever issuingCertificateRetriever) {
405406
this.issuingCertificateRetriever = issuingCertificateRetriever;
406407
return this;
407408
}
408409

410+
/**
411+
* Set certificate list to be used by the {@link IIssuingCertificateRetriever} to retrieve missing certificates.
412+
*
413+
* @param certificateList certificate list for getting missing certificates in chain
414+
* or CRL response issuer certificates.
415+
*
416+
* @return same instance of {@link PdfPadesSigner}.
417+
*/
418+
public PdfPadesSigner setTrustedCertificates(List<Certificate> certificateList) {
419+
this.issuingCertificateRetriever.setTrustedCertificates(certificateList);
420+
return this;
421+
}
422+
409423
private void performTimestamping(PdfDocument document, OutputStream outputStream, ITSAClient tsaClient)
410424
throws IOException, GeneralSecurityException {
411425
PdfSigner timestampSigner = new PdfSigner(document, outputStream, tempOutputStream, tempFile);

sign/src/test/java/com/itextpdf/signatures/sign/PdfPadesMissingCertificatesTest.java

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,6 @@ This file is part of the iText (R) project.
4242
import com.itextpdf.signatures.testutils.client.TestTsaClient;
4343
import com.itextpdf.test.ExtendedITextTest;
4444
import com.itextpdf.test.annotations.type.IntegrationTest;
45-
46-
import java.util.Collections;
47-
import java.util.List;
4845
import org.junit.BeforeClass;
4946
import org.junit.Test;
5047
import org.junit.experimental.categories.Category;
@@ -59,8 +56,11 @@ This file is part of the iText (R) project.
5956
import java.security.Security;
6057
import java.security.cert.Certificate;
6158
import java.security.cert.X509Certificate;
59+
import java.util.ArrayList;
6260
import java.util.Arrays;
61+
import java.util.Collections;
6362
import java.util.HashMap;
63+
import java.util.List;
6464
import java.util.Map;
6565

6666
@Category(IntegrationTest.class)
@@ -125,7 +125,7 @@ protected InputStream getCrlResponse(X509Certificate cert, URL urlt) throws IOEx
125125
X509Certificate crlIntermediateCert = (X509Certificate) PemFileHelper.readFirstChain(intermediateCrlFileName)[0];
126126
X509Certificate ocspIntermediateCert = (X509Certificate) PemFileHelper.readFirstChain(intermediateOscpFileName)[0];
127127
X509Certificate tsaIntermediateCert = (X509Certificate) PemFileHelper.readFirstChain(intermediateTsaFileName)[0];
128-
X509Certificate intermediateCert = (X509Certificate) PemFileHelper.readFirstChain(intermediateCertFileName)[0];
128+
X509Certificate intermediateCert = (X509Certificate) PemFileHelper.readFirstChain(intermediateCertFileName)[0];
129129

130130
AdvancedTestOcspClient ocspClient = new AdvancedTestOcspClient(null);
131131
ocspClient.addBuilderForCertIssuer(signCert, ocspCert, ocspPrivateKey);
@@ -198,6 +198,69 @@ protected InputStream getIssuerCertByURI(String uri) throws IOException {
198198
expectedNumberOfCrls, expectedNumberOfOcsps, certs);
199199
}
200200

201+
@Test
202+
public void retrieveMissingCertificatesUsingTrustedStoreTest() throws GeneralSecurityException, IOException,
203+
AbstractOperatorCreationException, AbstractPKCSException, AbstractOCSPException {
204+
String srcFileName = sourceFolder + "helloWorldDoc.pdf";
205+
String rootCertFileName = sourceFolder + "root.pem";
206+
String signCertFileName = sourceFolder + "sign.pem";
207+
String rootCrlFileName = sourceFolder + "crlRoot.pem";
208+
String crlCertFileName = sourceFolder + "crlCert.pem";
209+
String tsaCertFileName = sourceFolder + "tsCert.pem";
210+
String crlSignedByCA = sourceFolder + "crlWithRootIssuer.crl";
211+
String crlSignedByCrlCert = sourceFolder + "crlWithCrlIssuer.crl";
212+
213+
X509Certificate signCert = (X509Certificate) PemFileHelper.readFirstChain(signCertFileName)[0];
214+
PrivateKey signPrivateKey = PemFileHelper.readFirstKey(signCertFileName, password);
215+
X509Certificate crlCert = (X509Certificate) PemFileHelper.readFirstChain(crlCertFileName)[0];
216+
X509Certificate tsaCert = (X509Certificate) PemFileHelper.readFirstChain(tsaCertFileName)[0];
217+
PrivateKey tsaPrivateKey = PemFileHelper.readFirstKey(tsaCertFileName, password);
218+
219+
SignerProperties signerProperties = createSignerProperties();
220+
TestTsaClient testTsa = new TestTsaClient(Collections.singletonList(tsaCert), tsaPrivateKey);
221+
222+
CrlClientOnline testCrlClient = new CrlClientOnline() {
223+
@Override
224+
protected InputStream getCrlResponse(X509Certificate cert, URL urlt) throws IOException {
225+
if (urlt.toString().contains("cert-crl")) {
226+
return FileUtil.getInputStreamForFile(crlSignedByCrlCert);
227+
}
228+
return FileUtil.getInputStreamForFile(crlSignedByCA);
229+
}
230+
};
231+
232+
X509Certificate rootCert = (X509Certificate) PemFileHelper.readFirstChain(rootCertFileName)[0];
233+
X509Certificate crlRootCert = (X509Certificate) PemFileHelper.readFirstChain(rootCrlFileName)[0];
234+
235+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
236+
PdfPadesSigner padesSigner =
237+
new PdfPadesSigner(new PdfReader(FileUtil.getInputStreamForFile(srcFileName)), outputStream);
238+
padesSigner.setCrlClient(testCrlClient);
239+
240+
List<Certificate> trustedCertificates = new ArrayList<>();
241+
trustedCertificates.add(rootCert);
242+
trustedCertificates.add(crlRootCert);
243+
trustedCertificates.add(crlCert);
244+
padesSigner.setTrustedCertificates(trustedCertificates);
245+
246+
Certificate[] signChain = new Certificate[]{signCert};
247+
padesSigner.signWithBaselineLTProfile(signerProperties, signChain, signPrivateKey, testTsa);
248+
249+
outputStream.close();
250+
251+
TestSignUtils.basicCheckSignedDoc(new ByteArrayInputStream(outputStream.toByteArray()), "Signature1");
252+
253+
Map<String, Integer> expectedNumberOfCrls = new HashMap<>();
254+
Map<String, Integer> expectedNumberOfOcsps = new HashMap<>();
255+
// It is expected to have two CRL responses, one for signing cert and another for CRL response.
256+
expectedNumberOfCrls.put(crlCert.getSubjectX500Principal().getName(), 1);
257+
expectedNumberOfCrls.put(rootCert.getSubjectX500Principal().getName(), 1);
258+
List<String> certs = Arrays.asList(getCertName(rootCert), getCertName(crlRootCert), getCertName(crlCert),
259+
getCertName(signCert), getCertName(tsaCert));
260+
TestSignUtils.assertDssDict(new ByteArrayInputStream(outputStream.toByteArray()),
261+
expectedNumberOfCrls, expectedNumberOfOcsps, certs);
262+
}
263+
201264
private String getCertName(X509Certificate certificate) {
202265
return certificate.getSubjectX500Principal().getName();
203266
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDvDCCAqSgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCQkUx
3+
DjAMBgNVBAoMBWlUZXh0MTAwLgYDVQQDDCdpVGV4dE1pc3NpbmdDZXJ0aWZpY2F0
4+
ZXNUZXN0Q1JMUm9vdENlcnQwIBcNMjAwMTAxMDAwMDAwWhgPMjQwMDAxMDEwMDAw
5+
MDBaME8xCzAJBgNVBAYTAkJFMQ4wDAYDVQQKDAVpVGV4dDEwMC4GA1UEAwwnaVRl
6+
eHRNaXNzaW5nQ2VydGlmaWNhdGVzVGVzdENSTFNpZ25DZXJ0MIIBIjANBgkqhkiG
7+
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0rgFNDp8zelAj9o73RqZUK6pStgVbBL0Zn9
8+
HhM5O0e3OuWEgJtOm+foMayksOQHESxS6t/AiKdkTQc6nVl8cXncZURnsEi6SpHo
9+
r/WMkOmKUvnYdXda2mPuRmxRopG4TMqW6S0A9N8E29+xdG3LcmV/HP4MU78nRoOH
10+
WGi+Op1I4tyC9i8CfoRJcq7WI51fyh87TrxITS80Cjq9AXoBvmJrx90DgP7u7WUT
11+
NAPvVayapLG3VtnexSDuM/7+73dT15a4QxPdLmFUYbyhSfH77iI11FZd/06i9v/f
12+
uRcVZBobCs8+FzmN1OhSs1Uk7aAj/QkQkuyvVyK6uhrstfT4GwIDAQABo4GfMIGc
13+
MB0GA1UdDgQWBBT3CXRcQ9OYneGbRjM5g/NWB0LT4DAfBgNVHSMEGDAWgBTDTbEm
14+
iGJjiJdOu6soHhD/ha1kGjAOBgNVHQ8BAf8EBAMCBeAwSgYDVR0fBEMwQTA/oD2g
15+
O4Y5aHR0cDovL3Rlc3QuZXhhbXBsZS5jb20vZXhhbXBsZS1jYS9jcmxzL2NhLWNy
16+
bC9sYXRlc3QuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQAhTj5txV1IAyfe7K/bxDlK
17+
nKRJYYLsY8iTCu3dNF7mjSD7eZf7wWvGdfR2a910uS6U0/ZDIYyKSOnxdlRvNrU5
18+
nGxk48JuUZgSlSmzq1RmvdPx6dBLJT3Dctm7hkzQHemYNNAlkvDVX2GMPrEgcoTY
19+
S5gwKOvoQi5hXIYNxX43CaxVGyForNBMBn4502ThTs/M58EKNEZvUaNbYs2iLNkS
20+
CU1M0KZ4fMlihP9xtAhTBeqHUqEubKP45T0KzqWOZPH/WyQPeIrNAnXk2H0gIZP5
21+
ElI5Wm7r+EhCWTDfaBK3OormNoBOYKU0tawzzKVh1MHOg+31BqJud7H+t5REbTck
22+
-----END CERTIFICATE-----
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDfzCCAmegAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCQkUx
3+
DjAMBgNVBAoMBWlUZXh0MTAwLgYDVQQDDCdpVGV4dE1pc3NpbmdDZXJ0aWZpY2F0
4+
ZXNUZXN0Q1JMUm9vdENlcnQwIBcNMDAwMTAxMDAwMDAwWhgPMjUwMDAxMDEwMDAw
5+
MDBaME8xCzAJBgNVBAYTAkJFMQ4wDAYDVQQKDAVpVGV4dDEwMC4GA1UEAwwnaVRl
6+
eHRNaXNzaW5nQ2VydGlmaWNhdGVzVGVzdENSTFJvb3RDZXJ0MIIBIjANBgkqhkiG
7+
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu8ePfGsGJ2JOMKSzy1vSZW0vutjJCdi5mW+Y
8+
vqYJ+kQLOr0+gb3Wu0BonyFzl8wF9ogfaAyBqjzswaKw9uGfhHanzh5vqNwSHNlR
9+
/2aa56I1GidrprnaMSZiNy4lFAGxajFfb0D++UNjdLrkARjVcKenrwauR+GbT5bM
10+
tQw3yJVyMzAA6uy4xGMMNW/ZZwEUmKvWPNXqJHq0BvJICWWlZFQI2dMQo4DR7Npk
11+
D8dVd/Fdwtpp59E6xGz6WDk9/x6NH7wcZGW8xjZTtkrTOoppMG6A7Wrb+VWrXVbT
12+
lKaCo/1DY4whfnkKUwDsNXIk4vn9sU6PRvp0iEZGqk5Z25wQJwIDAQABo2MwYTAd
13+
BgNVHQ4EFgQUw02xJohiY4iXTrurKB4Q/4WtZBowHwYDVR0jBBgwFoAUw02xJohi
14+
Y4iXTrurKB4Q/4WtZBowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAeYw
15+
DQYJKoZIhvcNAQELBQADggEBAC5DclkIZBL7k5uqMA+TwzwwPSE0pCNrN0wLMy8I
16+
qaooT3IKuLLjo7FLH28ZRkrUYBdrg7W60NZfbDNcLoVSEyyOFMrplQNdUDPByrTU
17+
BUERxQ7r+7g+pHuF93o/3DbwvN8fykm4pC7d0JlFwge+LOu7pICHa8Ctj6K+C0kN
18+
lwhoz3Rafg+1tj5GUoGj2ZX82yXwb+rnSIn0hj6HhDpPXYRn2GssOqkOs1MJElw+
19+
GPn8vD7a3wI++4alki52TuR1Ydu41z4lQjfy3JSaFuflsPFf0KjNeCkH/D6PgwZg
20+
KCcD7fbk2IcIj70d5xye3v53wkbc9wZhqYm9CtKYunrKGoM=
21+
-----END CERTIFICATE-----
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDcTCCAlmgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwSDELMAkGA1UEBhMCQkUx
3+
DjAMBgNVBAoMBWlUZXh0MSkwJwYDVQQDDCBpVGV4dE1pc3NpbmdDZXJ0aWZpY2F0
4+
ZXNUZXN0Um9vdDAgFw0wMDAxMDEwMDAwMDBaGA8yNTAwMDEwMTAwMDAwMFowSDEL
5+
MAkGA1UEBhMCQkUxDjAMBgNVBAoMBWlUZXh0MSkwJwYDVQQDDCBpVGV4dE1pc3Np
6+
bmdDZXJ0aWZpY2F0ZXNUZXN0Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
7+
AQoCggEBAKuwJdoMeFrmWbyg8GXSy/bcZbJ6UlcHpc6wn8vxm7UqU8ZXewFTfJMe
8+
gCHHD0TdqR15mJVVD1Vc8HL0+zlQlzVz7aXLJg99O8jAmSCmMY5/6MlsnfvLeygl
9+
XcP4TzaWQJ+KQSeD75hFvFh4YMArThstRAjHtBCvpBqOAQfmsGYqKHfyNJlD4laC
10+
Dgi3NunTbh8ZDaBekrqBHdjp34hE01vbildqX4HRhYHPwJI0VqEXcvxWoLRTHqn7
11+
bDjAJCnSfeFKDZhAaFbGJYzIE2JNh1uIAYtfe5SzWoyjUnmhQ9vYQkZd0ORMDBvt
12+
moEj3oTAA0wf5AWUgn4GOKb3g7Wk8vUCAwEAAaNjMGEwHQYDVR0OBBYEFDxxMpGA
13+
5p+6Ft7i/hg+29HlL/l2MB8GA1UdIwQYMBaAFDxxMpGA5p+6Ft7i/hg+29HlL/l2
14+
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHmMA0GCSqGSIb3DQEBCwUA
15+
A4IBAQCmiT5fzeV9PEQ6d+YG8RpQ98UQp4Xl0fPzMSchyUa1YdV9C18qBqQUSaeb
16+
fcae5banYh9Pv6A68K+fh+qkJ86nVo3XeD8so4Y/+2JdmSZaoLgNNkX1EYpFNm4z
17+
1wS9pYYmsFKGMzZbSJh76QB4GQApgWrqTtYI8ndHtTPHcwwrIPJviu5TxrYtBcQD
18+
sJ7qnOS9xbp9M1/+1zcACF4Ge0jN0/QD99+DmFfxm/op2nICHMvR1HrKEX2F1ivT
19+
N07kGwS5eBRm1LqvHtHl2yroNPe8Dan5gpm/U0Dc2bnwsP3r33yXqHK5XdE0JBKz
20+
55MJ1G/ZYdACX/CIFoHlpsihhJ7T
21+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)