Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
package org.keycloak.crypto.fips.test;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.security.KeyStore;
import java.util.Set;
import java.util.stream.Collectors;

import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.util.Environment;
import org.keycloak.common.util.KeystoreUtil;
import org.keycloak.rule.CryptoInitRule;
import org.keycloak.truststore.TruststoreBuilder;

import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.hamcrest.Matchers;
import org.junit.Assume;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
Expand All @@ -25,6 +35,9 @@ public class FIPS1402KeystoreTypesTest {
@ClassRule
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();

@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();

@Before
public void before() {
// Run this test just if java is in FIPS mode
Expand Down Expand Up @@ -67,4 +80,50 @@ public void testTruststoreFormatsInApprovedMode() {
KeystoreUtil.TruststoreFormat.BCFKS));
assertThat(CryptoIntegration.getProvider().getPreferredGeneratedTrustStoreType(), Matchers.equalTo(KeystoreUtil.TruststoreFormat.BCFKS));
}

@Test
public void testMultiCertificatePemTruststoreInApprovedMode() throws Exception {
Assume.assumeTrue(CryptoServicesRegistrar.isInApprovedOnlyMode());

File bundle = temporaryFolder.newFile("bundle.pem");
try (OutputStream output = Files.newOutputStream(bundle.toPath())) {
for (String resource : new String[] { "/certs/UPN-cert.pem", "/certs/ANS-cert.pem" }) {
try (InputStream input = getClass().getResourceAsStream(resource)) {
assertNotNull(resource, input);
input.transferTo(output);
output.write('\n');
}
}
}

String truststoreKey = TruststoreBuilder.SYSTEM_TRUSTSTORE_KEY;
String typeKey = TruststoreBuilder.SYSTEM_TRUSTSTORE_TYPE_KEY;
String passwordKey = TruststoreBuilder.SYSTEM_TRUSTSTORE_PASSWORD_KEY;
String originalTruststore = System.getProperty(truststoreKey);
String originalType = System.getProperty(typeKey);
String originalPassword = System.getProperty(passwordKey);
try {
TruststoreBuilder.setSystemTruststore(new String[] { bundle.getAbsolutePath() }, false,
temporaryFolder.getRoot().getAbsolutePath());

assertEquals(KeystoreUtil.TruststoreFormat.BCFKS.name(), System.getProperty(typeKey));
KeyStore truststore = KeyStore.getInstance("BCFKS", "BCFIPS");
try (InputStream input = Files.newInputStream(new File(System.getProperty(truststoreKey)).toPath())) {
truststore.load(input, TruststoreBuilder.DUMMY_PASSWORD.toCharArray());
}
assertEquals(2, truststore.size());
} finally {
restoreProperty(truststoreKey, originalTruststore);
restoreProperty(typeKey, originalType);
restoreProperty(passwordKey, originalPassword);
}
}

private static void restoreProperty(String key, String value) {
if (value == null) {
System.clearProperty(key);
} else {
System.setProperty(key, value);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
Expand Down Expand Up @@ -269,32 +270,22 @@ static KeyStore loadStore(String path, String type, String password) {
static boolean mergePemFile(KeyStore truststore, String file, boolean isPem) {
try (FileInputStream pemInputStream = new FileInputStream(file)) {
CertificateFactory certFactory = CertificateFactory.getInstance("X509");
boolean loadedAny = false;
while (pemInputStream.available() > 0) {
X509Certificate cert;
try {
cert = (X509Certificate) certFactory.generateCertificate(pemInputStream);
loadedAny = true;
} catch (CertificateException e) {
if (pemInputStream.available() > 0 || !loadedAny) {
// any remaining input means there is an actual problem with the key contents or
// file format
if (isPem || loadedAny) {
throw e;
}
LOGGER.debugf(e,
"The file %s may not be in PEM format, it will not be used to create the merged truststore",
new File(file).getAbsolutePath());
continue;
}
LOGGER.debugf(e,
"The trailing entry for %s generated a certificate exception, assuming instead that the file ends with comments",
new File(file).getAbsolutePath());
continue;
Collection<? extends Certificate> certs;
try {
certs = certFactory.generateCertificates(pemInputStream);
} catch (CertificateException e) {
if (isPem || KeystoreUtil.getTruststoreFormat(file).orElse(null) == TruststoreFormat.PEM) {
throw e;
}
LOGGER.debugf(e,
"The file %s may not be in PEM format, it will not be used to create the merged truststore",
new File(file).getAbsolutePath());
return false;
}
for (Certificate cert : certs) {
setCertificateEntry(truststore, cert);
}
return loadedAny;
return !certs.isEmpty();
} catch (Exception e) {
throw new RuntimeException(
"Failed to initialize truststore, could not merge: " + new File(file).getAbsolutePath(), e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

import java.io.File;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -172,6 +174,19 @@ public void testFailsWithInvalidFile() throws Exception {
.createMergedTruststore(new String[] { new File(url.getPath()).getAbsolutePath() }, false));
}

@Test
public void testFailsWithMalformedPemInDirectory() throws Exception {
URL url = TruststoreBuilderTest.class.getResource("/truststores/keycloak.pem");
File directory = temporaryFolder.newFolder();
File bundle = new File(directory, "bundle.pem");
Files.copy(new File(url.toURI()).toPath(), bundle.toPath());
Files.writeString(bundle.toPath(), "\n-----BEGIN CERTIFICATE-----\nnot-base64\n-----END CERTIFICATE-----\n",
StandardOpenOption.APPEND);

assertThrows(RuntimeException.class, () -> TruststoreBuilder
.createMergedTruststore(new String[] { directory.getAbsolutePath() }, false));
}

@Test
public void testRejectsPemAsGeneratedTruststoreFormat() {
assertThrows(IllegalArgumentException.class, () -> TruststoreBuilder.createTrustStore(TruststoreFormat.PEM));
Expand Down
Loading