Skip to content

Commit dd457f8

Browse files
shawkinsmanusa
authored andcommitted
fix: adds support for read-only system truststores
closes #5316
1 parent 03b84b2 commit dd457f8

File tree

3 files changed

+56
-9
lines changed

3 files changed

+56
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* Fix #5423: OkHttpClientImpl supports setting request method for empty payload requests
1313

1414
#### Improvements
15+
* Fix #5316: support read-only system KeyStores with Kube CA Certs
1516
* Fix #5327: added proactive shutdown of informers on client close
1617
* Fix #5432: [java-generator] Add the possibility to always emit `additionalProperties` on generated POJOs
1718
* Fix #5410: [crd-generator] added support for `default`

kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/internal/CertUtils.java

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.io.ByteArrayInputStream;
2828
import java.io.File;
2929
import java.io.FileInputStream;
30+
import java.io.FileNotFoundException;
3031
import java.io.IOException;
3132
import java.io.InputStream;
3233
import java.io.InputStreamReader;
@@ -48,6 +49,7 @@
4849
import java.security.spec.RSAPrivateCrtKeySpec;
4950
import java.util.Base64;
5051
import java.util.Collection;
52+
import java.util.Collections;
5153
import java.util.concurrent.Callable;
5254
import java.util.stream.Collectors;
5355

@@ -73,15 +75,16 @@ public static ByteArrayInputStream getInputStreamFromDataOrFile(String data, Str
7375
public static KeyStore createTrustStore(String caCertData, String caCertFile, String trustStoreFile,
7476
String trustStorePassphrase) throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
7577
ByteArrayInputStream pemInputStream = getInputStreamFromDataOrFile(caCertData, caCertFile);
76-
return createTrustStore(pemInputStream, trustStoreFile,
78+
79+
KeyStore trustStore = loadTrustStore(trustStoreFile,
7780
getPassphrase(TRUST_STORE_PASSWORD_SYSTEM_PROPERTY, trustStorePassphrase));
78-
}
7981

80-
private static KeyStore createTrustStore(ByteArrayInputStream pemInputStream, String trustStoreFile,
81-
char[] trustStorePassphrase)
82-
throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
82+
return mergePemCertsIntoTrustStore(pemInputStream, trustStore, true);
83+
}
8384

84-
final String trustStoreType = System.getProperty(TRUST_STORE_TYPE_SYSTEM_PROPERTY, KeyStore.getDefaultType());
85+
static KeyStore loadTrustStore(String trustStoreFile, char[] trustStorePassphrase)
86+
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, FileNotFoundException {
87+
String trustStoreType = System.getProperty(TRUST_STORE_TYPE_SYSTEM_PROPERTY, KeyStore.getDefaultType());
8588
KeyStore trustStore = KeyStore.getInstance(trustStoreType);
8689

8790
if (Utils.isNotNullOrEmpty(trustStoreFile)) {
@@ -91,19 +94,45 @@ private static KeyStore createTrustStore(ByteArrayInputStream pemInputStream, St
9194
} else {
9295
loadDefaultTrustStoreFile(trustStore, trustStorePassphrase);
9396
}
97+
return trustStore;
98+
}
9499

100+
static KeyStore mergePemCertsIntoTrustStore(ByteArrayInputStream pemInputStream, KeyStore trustStore, boolean first)
101+
throws CertificateException, KeyStoreException {
95102
CertificateFactory certFactory = CertificateFactory.getInstance("X509");
96103
while (pemInputStream.available() > 0) {
104+
X509Certificate cert;
97105
try {
98-
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(pemInputStream);
99-
String alias = cert.getSubjectX500Principal().getName() + "_" + cert.getSerialNumber().toString(16);
100-
trustStore.setCertificateEntry(alias, cert);
106+
cert = (X509Certificate) certFactory.generateCertificate(pemInputStream);
101107
} catch (CertificateException e) {
102108
if (pemInputStream.available() > 0) {
103109
// any remaining input means there is an actual problem with the key contents or file format
104110
throw e;
105111
}
106112
LOG.debug("The trailing entry generated a certificate exception. More than likely the contents end with comments.", e);
113+
break;
114+
}
115+
try {
116+
String alias = cert.getSubjectX500Principal().getName() + "_" + cert.getSerialNumber().toString(16);
117+
trustStore.setCertificateEntry(alias, cert);
118+
first = false;
119+
} catch (KeyStoreException e) {
120+
if (first) {
121+
// could be that the store type is not writable, rather than some elaborate check for read-only
122+
// we'll simply try again with a well supported type
123+
pemInputStream.reset();
124+
KeyStore writableStore = KeyStore.getInstance("PKCS12");
125+
try {
126+
writableStore.load(null, null); // initialize the instance
127+
} catch (NoSuchAlgorithmException | CertificateException | IOException e1) {
128+
throw e; // not usable, just give up
129+
}
130+
for (String alias : Collections.list(trustStore.aliases())) {
131+
writableStore.setCertificateEntry(alias, trustStore.getCertificate(alias));
132+
}
133+
return mergePemCertsIntoTrustStore(pemInputStream, writableStore, false);
134+
}
135+
throw e;
107136
}
108137
}
109138
return trustStore;

kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/internal/CertUtilsTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.junit.jupiter.api.AfterEach;
2121
import org.junit.jupiter.api.BeforeEach;
2222
import org.junit.jupiter.api.Test;
23+
import org.mockito.Mockito;
2324

2425
import java.io.File;
2526
import java.io.IOException;
@@ -36,6 +37,7 @@
3637
import java.util.Properties;
3738

3839
import static org.assertj.core.api.Assertions.assertThat;
40+
import static org.junit.Assert.assertNotSame;
3941
import static org.junit.jupiter.api.Assertions.assertEquals;
4042
import static org.junit.jupiter.api.Assertions.assertNotNull;
4143
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -68,6 +70,21 @@ public void resetSystemPropertiesBack() {
6870
System.setProperties(systemProperties);
6971
}
7072

73+
@Test
74+
void handleReadOnlyJavaTrustStore() throws Exception {
75+
KeyStore system = CertUtils.loadTrustStore(null, "changeit".toCharArray());
76+
KeyStore trustStore = Mockito.spy(system);
77+
Mockito.doThrow(KeyStoreException.class).when(trustStore).setCertificateEntry(Mockito.anyString(), Mockito.any());
78+
KeyStore result = CertUtils.mergePemCertsIntoTrustStore(
79+
CertUtils.getInputStreamFromDataOrFile(null, "src/test/resources/ssl/multiple-certs.pem"), trustStore, true);
80+
81+
assertNotSame(trustStore, result);
82+
assertThat(Collections.list(result.aliases()))
83+
.hasSizeGreaterThanOrEqualTo(2)
84+
.satisfiesOnlyOnce(alias -> assertThat(alias).contains("openshift-signer"))
85+
.satisfiesOnlyOnce(alias -> assertThat(alias).contains("openshift-service-serving-signer"));
86+
}
87+
7188
@Test
7289
void loadingMultipleCertsFromSameFile() throws Exception {
7390
KeyStore ts = CertUtils.createTrustStore(

0 commit comments

Comments
 (0)