8000 Backport: use configured crypto provider to read generated truststores (26.8) by shawkins · Pull Request #54468 · keycloak/keycloak · GitHub
[go: up one dir, main page]

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
Expand Up @@ -97,8 +97,14 @@ public static KeyStore loadKeyStore(String filename, String password) throws Exc
}

public static KeyStore loadKeyStore(String filename, String password, String preferedType) throws Exception {
return loadKeyStore(filename, password, preferedType, null);
}

public static KeyStore loadKeyStore(String filename, String password, String preferedType, String provider) throws Exception {
String keystoreType = getKeystoreType(preferedType, filename, KeyStore.getDefaultType());
KeyStore trustStore = KeyStore.getInstance(keystoreType);
KeyStore trustStore = provider == null || provider.isEmpty()
? KeyStore.getInstance(keystoreType)
: KeyStore.getInstance(keystoreType, provider);
InputStream trustStream = null;
if (filename.startsWith(GenericConstants.PROTOCOL_CLASSPATH)) {
String resourcePath = filename.replace(GenericConstants.PROTOCOL_CLASSPATH, "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@
import java.io.OutputStream;
import java.nio.file.Files;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Collection;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

import org.keycloak.Config;
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.FileTruststoreProviderFactory;
import org.keycloak.truststore.TruststoreBuilder;

import org.bouncycastle.crypto.CryptoServicesRegistrar;
Expand All @@ -24,7 +30,8 @@
import org.junit.rules.TemporaryFolder;

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

/**
Expand Down Expand Up @@ -82,9 +89,8 @@ public void testTruststoreFormatsInApprovedMode() {
}

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

public void generatedTruststoreIsReadWithItsWriterProvider() throws Exception {
// given
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" }) {
Expand All @@ -96,34 +102,29 @@ public void testMultiCertificatePemTruststoreInApprovedMode() throws Exception {
}
}

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);
Collection<? extends Certificate> certificates;
try (InputStream input = Files.newInputStream(bundle.toPath())) {
certificates = CertificateFactory.getInstance("X.509").generateCertificates(input);
}
FileTruststoreProviderFactory factory = new FileTruststoreProviderFactory();
Properties originalProperties = (Properties) System.getProperties().clone();
try {
// when
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());
factory.init(new Config.SystemPropertiesScope("keycloak.truststore.file."));
KeyStore truststore = factory.create(null).getTruststore();

// then
assertThat(System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_PROVIDER_KEY), equalTo("BCFIPS"));
assertThat(truststore.getProvider().getName(), equalTo("BCFIPS"));
assertThat(truststore.getType(), equalTo(CryptoIntegration.getProvider().getPreferredGeneratedTrustStoreType().name()));
assertThat(truststore.size(), equalTo(certificates.size()));
for (Certificate certificate : certificates) {
assertThat(truststore.getCertificateAlias(certificate), notNullValue());
}
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);
System.setProperties(originalProperties);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public void init(Config.Scope config) {
HostnameVerificationPolicy verificationPolicy = null;
KeyStore truststore = null;
boolean system = false;
String providerName = null;
if (storepath == null) {
storepath = System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_KEY);
if (storepath == null) {
Expand All @@ -95,14 +96,16 @@ public void init(Config.Scope config) {
}
storepath = defaultTrustStore.getAbsolutePath();
system = true;
} else {
providerName = System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_PROVIDER_KEY);
}
// should there be an exception if pass / type are configured for the spi-truststore
pass = System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_PASSWORD_KEY, system ? "changeit" : null);
configuredType = System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_TYPE_KEY);
}
String type = KeystoreUtil.getKeystoreType(configuredType, storepath, KeyStore.getDefaultType());
try {
truststore = KeystoreUtil.loadKeyStore(storepath, pass, type);
truststore = KeystoreUtil.loadKeyStore(storepath, pass, type, providerName);
} catch (Exception e) {
// in fips mode the default truststore type can be pkcs12, but the cacerts file will still be jks
if (system && !"jks".equalsIgnoreCase(type)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class TruststoreBuilder {
public static final String SYSTEM_TRUSTSTORE_KEY = "javax.net.ssl.trustStore";
public static final String SYSTEM_TRUSTSTORE_PASSWORD_KEY = "javax.net.ssl.trustStorePassword";
public static final String SYSTEM_TRUSTSTORE_TYPE_KEY = "javax.net.ssl.trustStoreType";
public static final String SYSTEM_TRUSTSTORE_PROVIDER_KEY = "javax.net.ssl.trustStoreProvider";
private static final String CERT_PROTECTION_ALGORITHM_KEY = "keystore.pkcs12.certProtectionAlgorithm";
public static final String DUMMY_PASSWORD = "keycloakchangeit"; // fips length compliant dummy password
static final String PKCS12 = "PKCS12";
Expand Down Expand Up @@ -78,6 +79,7 @@ public static void setSystemTruststore(String[] truststores,
System.setProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_KEY, file.getAbsolutePath());
System.setProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_TYPE_KEY, truststoreType.name());
System.setProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_PASSWORD_KEY, DUMMY_PASSWORD);
System.setProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_PROVIDER_KEY, truststore.getProvider().getName());
}

/**
Expand Down Expand Up @@ -211,10 +213,12 @@ static void includeDefaultTruststore(KeyStore truststore) {
String originalTruststoreKey = TruststoreBuilder.SYSTEM_TRUSTSTORE_KEY + ".orig";
String originalTruststoreTypeKey = TruststoreBuilder.SYSTEM_TRUSTSTORE_TYPE_KEY + ".orig";
String originalTruststorePasswordKey = TruststoreBuilder.SYSTEM_TRUSTSTORE_PASSWORD_KEY + ".orig";
String originalTruststoreProviderKey = TruststoreBuilder.SYSTEM_TRUSTSTORE_PROVIDER_KEY + ".orig";

String trustStorePath = System.getProperty(originalTruststoreKey);
String type = PKCS12;
String password = null;
String provider = null;
File defaultTrustStore = null;
if (trustStorePath == null) {
trustStorePath = System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_KEY);
Expand All @@ -223,9 +227,15 @@ static void includeDefaultTruststore(KeyStore truststore) {
} else {
type = System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_TYPE_KEY, KeyStore.getDefaultType());
password = System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_PASSWORD_KEY);
provider = System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_PROVIDER_KEY);
// save the original information
System.setProperty(originalTruststoreKey, trustStorePath);
System.setProperty(originalTruststoreTypeKey, type);
if (provider == null) {
System.clearProperty(originalTruststoreProviderKey);
} else {
System.setProperty(originalTruststoreProviderKey, provider);
}
if (password == null) {
System.getProperties().remove(originalTruststorePasswordKey);
} else {
Expand All @@ -236,12 +246,13 @@ static void includeDefaultTruststore(KeyStore truststore) {
} else {
type = System.getProperty(originalTruststoreTypeKey);
password = System.getProperty(originalTruststorePasswordKey);
provider = System.getProperty(originalTruststoreProviderKey);
defaultTrustStore = new File(trustStorePath);
}

if (defaultTrustStore.exists()) {
String path = defaultTrustStore.getAbsolutePath();
mergeTrustStore(truststore, path, loadStore(path, type, password));
mergeTrustStore(truststore, path, loadStore(path, type, password, provider));
} else {
LOGGER.warnf("Default truststore was to be included, but could not be found at: %s", defaultTrustStore);
}
Expand All @@ -259,8 +270,12 @@ static File getJRETruststore() {
}

static KeyStore loadStore(String path, String type, String password) {
return loadStore(path, type, password, null);
}

static KeyStore loadStore(String path, String type, String password, String provider) {
try {
return KeystoreUtil.loadKeyStore(path, password, type);
return KeystoreUtil.loadKeyStore(path, password, type, provider);
} catch (Exception e) {
throw new RuntimeException(
"Failed to initialize truststore: " + new File(path).getAbsolutePath() + ", type: " + type, e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -41,6 +45,9 @@
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
Expand All @@ -51,19 +58,23 @@ public class FileTruststoreProviderFactoryTest {
TruststoreBuilder.SYSTEM_TRUSTSTORE_KEY,
TruststoreBuilder.SYSTEM_TRUSTSTORE_PASSWORD_KEY,
TruststoreBuilder.SYSTEM_TRUSTSTORE_TYPE_KEY,
TruststoreBuilder.SYSTEM_TRUSTSTORE_PROVIDER_KEY,
TruststoreBuilder.SYSTEM_TRUSTSTORE_KEY + ".orig",
TruststoreBuilder.SYSTEM_TRUSTSTORE_PASSWORD_KEY + ".orig",
TruststoreBuilder.SYSTEM_TRUSTSTORE_TYPE_KEY + ".orig"
TruststoreBuilder.SYSTEM_TRUSTSTORE_TYPE_KEY + ".orig",
TruststoreBuilder.SYSTEM_TRUSTSTORE_PROVIDER_KEY + ".orig"
};

private final Map<String, String> originalSystemProperties = new HashMap<>();
private CryptoProvider originalCryptoProvider;
private TruststoreProvider originalTruststoreProvider;

@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();

@Before
public void before() {
originalTruststoreProvider = TruststoreProviderSingleton.get();
originalCryptoProvider = CryptoIntegration.isInitialised() ? CryptoIntegration.getProvider() : null;
CryptoIntegration.setProvider(new DefaultCryptoProvider());
for (String key : SYSTEM_PROPERTY_KEYS) {
Expand All @@ -81,6 +92,7 @@ public void after() {
System.setProperty(key, value);
}
}
TruststoreProviderSingleton.set(originalTruststoreProvider);
CryptoIntegration.setProvider(originalCryptoProvider);
}

Expand Down Expand Up @@ -125,6 +137,33 @@ public void testLoadGeneratedBcfksSystemTruststore() throws Exception {
assertFalse(provider.getRootCertificates().isEmpty());
}

@Test
public void generatedTruststoreIsReadWithItsWriterProvider() throws Exception {
// given
URL pem = getClass().getResource("/truststores/keycloak.pem");
Collection<? extends Certificate> certificates;
try (InputStream input = pem.openStream()) {
certificates = CertificateFactory.getInstance("X.509").generateCertificates(input);
}

FileTruststoreProviderFactory factory = new FileTruststoreProviderFactory();

// when
TruststoreBuilder.setSystemTruststore(new String[] { new File(pem.toURI()).getAbsolutePath() }, false,
temporaryFolder.getRoot().getAbsolutePath());
factory.init(rootScope(Map.of()));
KeyStore truststore = factory.create(null).getTruststore();

// then
assertThat(System.getProperty(TruststoreBuilder.SYSTEM_TRUSTSTORE_PROVIDER_KEY), equalTo("BC"));
assertThat(truststore.getProvider().getName(), equalTo("BC"));
assertThat(truststore.getType(), equalTo(TruststoreFormat.PKCS12.name()));
assertThat(truststore.size(), equalTo(certificates.size()));
for (Certificate certificate : certificates) {
assertThat(truststore.getCertificateAlias(certificate), notNullValue());
}
}

@Test
public void testLoadPemHttpsTruststoreUsesPreferredTruststoreType() throws Exception {
CryptoIntegration.setProvider(new DefaultCryptoProvider() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@ public class TruststoreBuilderTest {
TruststoreBuilder.SYSTEM_TRUSTSTORE_KEY,
TruststoreBuilder.SYSTEM_TRUSTSTORE_PASSWORD_KEY,
TruststoreBuilder.SYSTEM_TRUSTSTORE_TYPE_KEY,
TruststoreBuilder.SYSTEM_TRUSTSTORE_PROVIDER_KEY,
TruststoreBuilder.SYSTEM_TRUSTSTORE_KEY + ".orig",
TruststoreBuilder.SYSTEM_TRUSTSTORE_PASSWORD_KEY + ".orig",
TruststoreBuilder.SYSTEM_TRUSTSTORE_TYPE_KEY + ".orig",
TruststoreBuilder.SYSTEM_TRUSTSTORE_PROVIDER_KEY + ".orig",
CERT_PROTECTION_ALGORITHM_KEY
};

Expand Down
Loading
0