Skip to content

Commit c49f598

Browse files
committed
Add support for multiple certificates in ca.pem file
A single PEM file can contain a number of certificates. Usually keys and certificates are split into different files, but ca.pem still can contain several certificates in some cases.
1 parent b465546 commit c49f598

File tree

4 files changed

+129
-7
lines changed

4 files changed

+129
-7
lines changed

src/main/java/com/github/dockerjava/core/util/CertificateUtils.java

+14-7
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
import java.util.List;
1919

2020
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
21-
2221
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
2322
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
2423
import org.bouncycastle.cert.X509CertificateHolder;
2524
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
25+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
2626
import org.bouncycastle.openssl.PEMKeyPair;
2727
import org.bouncycastle.openssl.PEMParser;
2828
import org.slf4j.Logger;
@@ -88,7 +88,8 @@ public static List<Certificate> loadCertificates(final Reader reader) throws IOE
8888
try (PEMParser pemParser = new PEMParser(reader)) {
8989
List<Certificate> certificates = new ArrayList<>();
9090

91-
JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter().setProvider("BC");
91+
JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter()
92+
.setProvider(BouncyCastleProvider.PROVIDER_NAME);
9293
Object certObj = pemParser.readObject();
9394

9495
if (certObj instanceof X509CertificateHolder) {
@@ -181,14 +182,20 @@ public static KeyStore createTrustStore(String capem) throws IOException, Certif
181182
public static KeyStore createTrustStore(final Reader certReader) throws IOException, CertificateException,
182183
KeyStoreException, NoSuchAlgorithmException {
183184
try (PEMParser pemParser = new PEMParser(certReader)) {
184-
X509CertificateHolder certificateHolder = (X509CertificateHolder) pemParser.readObject();
185-
Certificate caCertificate = new JcaX509CertificateConverter()
186-
.setProvider("BC")
187-
.getCertificate(certificateHolder);
188185

189186
KeyStore trustStore = KeyStore.getInstance("JKS");
190187
trustStore.load(null);
191-
trustStore.setCertificateEntry("ca", caCertificate);
188+
189+
int index = 1;
190+
Object pemCert;
191+
192+
while ((pemCert = pemParser.readObject()) != null) {
193+
Certificate caCertificate = new JcaX509CertificateConverter()
194+
.setProvider(BouncyCastleProvider.PROVIDER_NAME)
195+
.getCertificate((X509CertificateHolder) pemCert);
196+
trustStore.setCertificateEntry("ca-" + index, caCertificate);
197+
index++;
198+
}
192199

193200
return trustStore;
194201
}

src/test/java/com/github/dockerjava/core/util/CertificateUtilsTest.java

+40
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
package com.github.dockerjava.core.util;
22

3+
import java.io.IOException;
4+
import java.nio.file.Files;
5+
import java.nio.file.Paths;
6+
import java.security.KeyStore;
7+
import java.security.Security;
8+
9+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
10+
import org.testng.annotations.AfterClass;
11+
import org.testng.annotations.BeforeClass;
312
import org.testng.annotations.Test;
413

514
import static org.hamcrest.MatcherAssert.assertThat;
@@ -9,6 +18,16 @@ public class CertificateUtilsTest {
918
private static final String baseDir = CertificateUtilsTest.class.getResource(
1019
CertificateUtilsTest.class.getSimpleName() + "/").getFile();
1120

21+
@BeforeClass
22+
public static void init() {
23+
Security.addProvider(new BouncyCastleProvider());
24+
}
25+
26+
@AfterClass
27+
public static void tearDown() {
28+
Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
29+
}
30+
1231
@Test
1332
public void allFilesExist() {
1433
assertThat(CertificateUtils.verifyCertificatesExist(baseDir + "allFilesExist"), is(true));
@@ -48,4 +67,25 @@ public void certMissing() {
4867
public void keyMissing() {
4968
assertThat(CertificateUtils.verifyCertificatesExist(baseDir + "keyMissing"), is(false));
5069
}
70+
71+
@Test
72+
public void readCaCert() throws Exception {
73+
String capem = readFileAsString("caTest/single_ca.pem");
74+
KeyStore keyStore = CertificateUtils.createTrustStore(capem);
75+
assertThat(keyStore.size(), is(1));
76+
assertThat(keyStore.isCertificateEntry("ca-1"), is(true));
77+
}
78+
79+
@Test
80+
public void readMultipleCaCerts() throws Exception {
81+
String capem = readFileAsString("caTest/multiple_ca.pem");
82+
KeyStore keyStore = CertificateUtils.createTrustStore(capem);
83+
assertThat(keyStore.size(), is(2));
84+
assertThat(keyStore.isCertificateEntry("ca-1"), is(true));
85+
assertThat(keyStore.isCertificateEntry("ca-2"), is(true));
86+
}
87+
88+
private String readFileAsString(String path) throws IOException {
89+
return new String(Files.readAllBytes(Paths.get(baseDir + path)));
90+
}
5191
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIENjCCAx6gAwIBAgIJALOOFdRswGmDMA0GCSqGSIb3DQEBBQUAMG8xCzAJBgNV
3+
BAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
4+
aWRnaXRzIFB0eSBMdGQxDDAKBgNVBAMTA0NBMTEaMBgGCSqGSIb3DQEJARYLY2FA
5+
dGVzdC5jb20wHhcNMTcwMTI1MTc0ODE5WhcNMjcwMTIzMTc0ODE5WjBvMQswCQYD
6+
VQQGEwJVUzETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQg
7+
V2lkZ2l0cyBQdHkgTHRkMQwwCgYDVQQDEwNDQTExGjAYBgkqhkiG9w0BCQEWC2Nh
8+
QHRlc3QuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA346h3pA5
9+
5Uu6t7I8oNsiwF9PNgY7FKL/fXAiGAi1GMcPvil5Sf+GOYenQNsh4unfMRu8C+Xn
10+
0NSnQNRkgJ2eIR5w3qDDK+UFj34rLdBK+wNze3gMGOaTFiSZLeoxrXs6fGbJ7Jmd
11+
GaW7u8+P1+1Iej2k7lz61SXRL7+fn5lo3Lpfi+7s44zzOHoPkbHYNyl3md5Wgsoc
12+
U+VGcABl1cq956ycQU1/jWZgclaTL3sQjNvLJ+dnN4+IwbFiPE9tY+cJtdNn2YXd
13+
jLqxkfr/MZXoxArASanzPa4S7sKl8nnGrYM8cnppMeFk2a/p1YT+507V6VEmLM1A
14+
IlqtzOAWpZ4vSQIDAQABo4HUMIHRMB0GA1UdDgQWBBRTvtGtQ298/2Ukc6ncXNUI
15+
plkkxjCBoQYDVR0jBIGZMIGWgBRTvtGtQ298/2Ukc6ncXNUIplkkxqFzpHEwbzEL
16+
MAkGA1UEBhMCVVMxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVy
17+
bmV0IFdpZGdpdHMgUHR5IEx0ZDEMMAoGA1UEAxMDQ0ExMRowGAYJKoZIhvcNAQkB
18+
FgtjYUB0ZXN0LmNvbYIJALOOFdRswGmDMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN
19+
AQEFBQADggEBAIs8PjXPWvTWVDQ8xsZHLosD5BzCqM6ac+W7pjKEVNaiIo+UT3st
20+
bl1rZS2U924M9xtOkmAOPCQx1ozMxqKk2SVYztll3/Nw7p3qbP2/7L1tOTjoNZqT
21+
o2dd4Jf8txj9AaDus9FW8gmR6TLSgm7xeNb/kEa1b5ZW6l0zYRSBNT4I38d9wCa4
22+
QeohD8t1T1QOSc82dTHqccSiCSRiOX883I/fK+cTo8o1gdyUjYoCQlsJESr1rUun
23+
C3zMTZ4Lkt72vM88Hf0OyK/+sw7sqVEf+95VMx+a/UaYYkJY3Bg6JTJb9vukbtAl
24+
5GrDZOGM1AgW8iZyG/jSJlwkjiB9vy8RRnI=
25+
-----END CERTIFICATE-----
26+
-----BEGIN CERTIFICATE-----
27+
MIIENjCCAx6gAwIBAgIJAL4WwpOexcT8MA0GCSqGSIb3DQEBBQUAMG8xCzAJBgNV
28+
BAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
29+
aWRnaXRzIFB0eSBMdGQxDDAKBgNVBAMTA0NBMjEaMBgGCSqGSIb3DQEJARYLY2FA
30+
dGVzdC5jb20wHhcNMTcwMTI1MTc0ODQ4WhcNMjcwMTIzMTc0ODQ4WjBvMQswCQYD
31+
VQQGEwJVUzETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQg
32+
V2lkZ2l0cyBQdHkgTHRkMQwwCgYDVQQDEwNDQTIxGjAYBgkqhkiG9w0BCQEWC2Nh
33+
QHRlc3QuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA346h3pA5
34+
5Uu6t7I8oNsiwF9PNgY7FKL/fXAiGAi1GMcPvil5Sf+GOYenQNsh4unfMRu8C+Xn
35+
0NSnQNRkgJ2eIR5w3qDDK+UFj34rLdBK+wNze3gMGOaTFiSZLeoxrXs6fGbJ7Jmd
36+
GaW7u8+P1+1Iej2k7lz61SXRL7+fn5lo3Lpfi+7s44zzOHoPkbHYNyl3md5Wgsoc
37+
U+VGcABl1cq956ycQU1/jWZgclaTL3sQjNvLJ+dnN4+IwbFiPE9tY+cJtdNn2YXd
38+
jLqxkfr/MZXoxArASanzPa4S7sKl8nnGrYM8cnppMeFk2a/p1YT+507V6VEmLM1A
39+
IlqtzOAWpZ4vSQIDAQABo4HUMIHRMB0GA1UdDgQWBBRTvtGtQ298/2Ukc6ncXNUI
40+
plkkxjCBoQYDVR0jBIGZMIGWgBRTvtGtQ298/2Ukc6ncXNUIplkkxqFzpHEwbzEL
41+
MAkGA1UEBhMCVVMxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVy
42+
bmV0IFdpZGdpdHMgUHR5IEx0ZDEMMAoGA1UEAxMDQ0EyMRowGAYJKoZIhvcNAQkB
43+
FgtjYUB0ZXN0LmNvbYIJAL4WwpOexcT8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN
44+
AQEFBQADggEBAKVIxgBQ76JQYfejcaK946J6VglSKaHuPZ8bDVM9e2KB1HFwTzAA
45+
oRtMBIJaSO1+CSopTnzvzv/j8XeXbAUt6UHWiejGcIke1wW1CSVHeDQM7KUzXYtu
46+
2jWzlGHXtnni4EeSwdE628Kfk82r/NU3+3+zo/3ASkYcSGBEYgIdvLBmbvQ8sviJ
47+
rQZ1HCDP/TgP/ExBDi2QWBHe3XSTD0pBK153FC2YT1Q0AG5mxsIeq7C85m22aNX9
48+
fUmG5ORnVMkT/ktY+jg5aP+50o58MX24ZG1mh59vZtuBbvD74qZ2eNauStt1ji34
49+
V43XhW5Bigsp/z7GyFb2VzejH9n+VvPmTnU=
50+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIENjCCAx6gAwIBAgIJAL4WwpOexcT8MA0GCSqGSIb3DQEBBQUAMG8xCzAJBgNV
3+
BAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
4+
aWRnaXRzIFB0eSBMdGQxDDAKBgNVBAMTA0NBMjEaMBgGCSqGSIb3DQEJARYLY2FA
5+
dGVzdC5jb20wHhcNMTcwMTI1MTc0ODQ4WhcNMjcwMTIzMTc0ODQ4WjBvMQswCQYD
6+
VQQGEwJVUzETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQg
7+
V2lkZ2l0cyBQdHkgTHRkMQwwCgYDVQQDEwNDQTIxGjAYBgkqhkiG9w0BCQEWC2Nh
8+
QHRlc3QuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA346h3pA5
9+
5Uu6t7I8oNsiwF9PNgY7FKL/fXAiGAi1GMcPvil5Sf+GOYenQNsh4unfMRu8C+Xn
10+
0NSnQNRkgJ2eIR5w3qDDK+UFj34rLdBK+wNze3gMGOaTFiSZLeoxrXs6fGbJ7Jmd
11+
GaW7u8+P1+1Iej2k7lz61SXRL7+fn5lo3Lpfi+7s44zzOHoPkbHYNyl3md5Wgsoc
12+
U+VGcABl1cq956ycQU1/jWZgclaTL3sQjNvLJ+dnN4+IwbFiPE9tY+cJtdNn2YXd
13+
jLqxkfr/MZXoxArASanzPa4S7sKl8nnGrYM8cnppMeFk2a/p1YT+507V6VEmLM1A
14+
IlqtzOAWpZ4vSQIDAQABo4HUMIHRMB0GA1UdDgQWBBRTvtGtQ298/2Ukc6ncXNUI
15+
plkkxjCBoQYDVR0jBIGZMIGWgBRTvtGtQ298/2Ukc6ncXNUIplkkxqFzpHEwbzEL
16+
MAkGA1UEBhMCVVMxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVy
17+
bmV0IFdpZGdpdHMgUHR5IEx0ZDEMMAoGA1UEAxMDQ0EyMRowGAYJKoZIhvcNAQkB
18+
FgtjYUB0ZXN0LmNvbYIJAL4WwpOexcT8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN
19+
AQEFBQADggEBAKVIxgBQ76JQYfejcaK946J6VglSKaHuPZ8bDVM9e2KB1HFwTzAA
20+
oRtMBIJaSO1+CSopTnzvzv/j8XeXbAUt6UHWiejGcIke1wW1CSVHeDQM7KUzXYtu
21+
2jWzlGHXtnni4EeSwdE628Kfk82r/NU3+3+zo/3ASkYcSGBEYgIdvLBmbvQ8sviJ
22+
rQZ1HCDP/TgP/ExBDi2QWBHe3XSTD0pBK153FC2YT1Q0AG5mxsIeq7C85m22aNX9
23+
fUmG5ORnVMkT/ktY+jg5aP+50o58MX24ZG1mh59vZtuBbvD74qZ2eNauStt1ji34
24+
V43XhW5Bigsp/z7GyFb2VzejH9n+VvPmTnU=
25+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)