Make https-trust-store-type set to bcfks by default in strict-mode

Closes #17119
This commit is contained in:
mposolda 2023-02-14 18:57:18 +01:00 committed by Pedro Igor
parent e76418e3de
commit 4f068fcdcc
20 changed files with 72 additions and 50 deletions

View file

@ -80,7 +80,6 @@ public class CryptoIntegration {
.append(" Default keystore type: " + KeyStore.getDefaultType() + "\n") .append(" Default keystore type: " + KeyStore.getDefaultType() + "\n")
.append(" KeyManagerFactory.getDefaultAlgorithm(): " + KeyManagerFactory.getDefaultAlgorithm() + "\n") .append(" KeyManagerFactory.getDefaultAlgorithm(): " + KeyManagerFactory.getDefaultAlgorithm() + "\n")
.append(" TrustManagerFactory.getDefaultAlgorithm(): " + TrustManagerFactory.getDefaultAlgorithm() + "\n") .append(" TrustManagerFactory.getDefaultAlgorithm(): " + TrustManagerFactory.getDefaultAlgorithm() + "\n")
.append(" Default keystore type: " + KeyStore.getDefaultType() + "\n")
.append(" keystore.type.compat: " + Security.getProperty("keystore.type.compat") + "\n"); .append(" keystore.type.compat: " + Security.getProperty("keystore.type.compat") + "\n");
Stream.of("javax.net.ssl.trustStoreType", "javax.net.ssl.trustStore", "javax.net.ssl.trustStoreProvider", Stream.of("javax.net.ssl.trustStoreType", "javax.net.ssl.trustStore", "javax.net.ssl.trustStoreProvider",
"javax.net.ssl.keyStoreType", "javax.net.ssl.keyStore", "javax.net.ssl.keyStoreProvider") "javax.net.ssl.keyStoreType", "javax.net.ssl.keyStore", "javax.net.ssl.keyStoreProvider")

View file

@ -23,7 +23,7 @@ public class KeycloakFipsSecurityProvider extends Provider {
super("KC(" + super("KC(" +
bcFipsProvider.toString() + bcFipsProvider.toString() +
(isInApprovedOnlyMode() ? " Approved Mode" : "") + (isInApprovedOnlyMode() ? " Approved Mode" : "") +
(isSystemFipsEnabled() ? " FIPS-enabled JVM" : "") + ", FIPS-JVM: " + isSystemFipsEnabled() +
")", 1, "Keycloak pseudo provider"); ")", 1, "Keycloak pseudo provider");
this.bcFipsProvider = bcFipsProvider; this.bcFipsProvider = bcFipsProvider;
} }
@ -39,22 +39,22 @@ public class KeycloakFipsSecurityProvider extends Provider {
} }
} }
private static boolean isSystemFipsEnabled() { public static String isSystemFipsEnabled() {
Method isSystemFipsEnabled = null; Method isSystemFipsEnabled = null;
try { try {
Class<?> securityConfigurator = KeycloakFipsSecurityProvider.class.getClassLoader().loadClass("java.security.SystemConfigurator"); Class<?> securityConfigurator = KeycloakFipsSecurityProvider.class.getClassLoader().loadClass("java.security.SystemConfigurator");
isSystemFipsEnabled = securityConfigurator.getDeclaredMethod("isSystemFipsEnabled"); isSystemFipsEnabled = securityConfigurator.getDeclaredMethod("isSystemFipsEnabled");
isSystemFipsEnabled.setAccessible(true); isSystemFipsEnabled.setAccessible(true);
return (boolean) isSystemFipsEnabled.invoke(null); boolean isEnabled = (boolean) isSystemFipsEnabled.invoke(null);
return isEnabled ? "enabled" : "disabled";
} catch (Throwable ignore) { } catch (Throwable ignore) {
logger.debug("Could not detect if FIPS is enabled from the host"); logger.debug("Could not detect if FIPS is enabled from the host", ignore);
return "unknown";
} finally { } finally {
if (isSystemFipsEnabled != null) { if (isSystemFipsEnabled != null) {
isSystemFipsEnabled.setAccessible(false); isSystemFipsEnabled.setAccessible(false);
} }
} }
return false;
} }
} }

View file

@ -97,10 +97,11 @@ public class HttpOptions {
.description("The password of the trust store file.") .description("The password of the trust store file.")
.build(); .build();
public static final Option HTTPS_TRUST_STORE_TYPE = new OptionBuilder<>("https-trust-store-type", File.class) public static final Option<String> HTTPS_TRUST_STORE_TYPE = new OptionBuilder<>("https-trust-store-type", String.class)
.category(OptionCategory.HTTP) .category(OptionCategory.HTTP)
.description("The type of the trust store file. " + .description("The type of the trust store file. " +
"If not given, the type is automatically detected based on the file name.") "If not given, the type is automatically detected based on the file name. " +
"If '" + SecurityOptions.FIPS_MODE.getKey() + "' is set to '" + FipsMode.strict.name() + "' and no value is set, it defaults to 'BCFKS'.")
.build(); .build();
public static final Option<Boolean> HTTP_SERVER_ENABLED = new OptionBuilder<>("http-server-enabled", Boolean.class) public static final Option<Boolean> HTTP_SERVER_ENABLED = new OptionBuilder<>("http-server-enabled", Boolean.class)

View file

@ -98,6 +98,8 @@ final class HttpPropertyMappers {
.build(), .build(),
fromOption(HttpOptions.HTTPS_TRUST_STORE_TYPE) fromOption(HttpOptions.HTTPS_TRUST_STORE_TYPE)
.to("quarkus.http.ssl.certificate.trust-store-file-type") .to("quarkus.http.ssl.certificate.trust-store-file-type")
.mapFrom(SecurityOptions.FIPS_MODE.getKey())
.transformer(HttpPropertyMappers::resolveKeyStoreType)
.paramLabel("type") .paramLabel("type")
.build() .build()
}; };

View file

@ -19,6 +19,7 @@ package org.keycloak.it.cli.dist;
import java.nio.file.Path; import java.nio.file.Path;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.keycloak.crypto.fips.KeycloakFipsSecurityProvider;
import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.RawDistOnly; import org.keycloak.it.junit5.extension.RawDistOnly;
@ -38,7 +39,7 @@ public class FipsDistTest {
CLIResult cliResult = dist.run("start", "--fips-mode=enabled"); CLIResult cliResult = dist.run("start", "--fips-mode=enabled");
cliResult.assertStarted(); cliResult.assertStarted();
cliResult.assertMessage("Java security providers: [ \n" cliResult.assertMessage("Java security providers: [ \n"
+ " KC(BCFIPS version 1.000203) version 1.0 - class org.keycloak.crypto.fips.KeycloakFipsSecurityProvider"); + " KC(BCFIPS version 1.000203, FIPS-JVM: " + KeycloakFipsSecurityProvider.isSystemFipsEnabled() + ") version 1.0 - class org.keycloak.crypto.fips.KeycloakFipsSecurityProvider");
}); });
} }
@ -53,7 +54,7 @@ public class FipsDistTest {
cliResult.assertMessage( cliResult.assertMessage(
"org.bouncycastle.crypto.fips.FipsUnapprovedOperationError: password must be at least 112 bits"); "org.bouncycastle.crypto.fips.FipsUnapprovedOperationError: password must be at least 112 bits");
cliResult.assertMessage("Java security providers: [ \n" cliResult.assertMessage("Java security providers: [ \n"
+ " KC(BCFIPS version 1.000203 Approved Mode) version 1.0 - class org.keycloak.crypto.fips.KeycloakFipsSecurityProvider"); + " KC(BCFIPS version 1.000203 Approved Mode, FIPS-JVM: " + KeycloakFipsSecurityProvider.isSystemFipsEnabled() + ") version 1.0 - class org.keycloak.crypto.fips.KeycloakFipsSecurityProvider");
dist.setEnvVar("KEYCLOAK_ADMIN_PASSWORD", "adminadminadmin"); dist.setEnvVar("KEYCLOAK_ADMIN_PASSWORD", "adminadminadmin");
cliResult = dist.run("start", "--fips-mode=strict"); cliResult = dist.run("start", "--fips-mode=strict");
@ -87,6 +88,21 @@ public class FipsDistTest {
}); });
} }
@Test
void testHttpsBcfksTrustStoreInStrictMode(KeycloakDistribution dist) {
runOnFipsEnabledDistribution(dist, () -> {
dist.copyOrReplaceFileFromClasspath("/server.keystore.bcfks", Path.of("conf", "server.keystore"));
RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class);
Path truststorePath = rawDist.getDistPath().resolve("conf").resolve("server.keystore").toAbsolutePath();
// https-trust-store-type should be automatically set to bcfks in fips-mode=strict
CLIResult cliResult = dist.run("--verbose", "start", "--fips-mode=strict", "--https-key-store-password=passwordpassword",
"--https-trust-store-file=" + truststorePath, "--https-trust-store-password=passwordpassword");
cliResult.assertStarted();
});
}
@Test @Test
void testUnsupportedHttpsPkcs12KeyStoreInStrictMode(KeycloakDistribution dist) { void testUnsupportedHttpsPkcs12KeyStoreInStrictMode(KeycloakDistribution dist) {
runOnFipsEnabledDistribution(dist, () -> { runOnFipsEnabledDistribution(dist, () -> {
@ -105,6 +121,21 @@ public class FipsDistTest {
}); });
} }
@Test
void testHttpsPkcs12TrustStoreInNonApprovedMode(KeycloakDistribution dist) {
runOnFipsEnabledDistribution(dist, () -> {
dist.copyOrReplaceFileFromClasspath("/server.keystore.pkcs12", Path.of("conf", "server.keystore"));
RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class);
Path truststorePath = rawDist.getDistPath().resolve("conf").resolve("server.keystore").toAbsolutePath();
// https-trust-store-type should be automatically set to pkcs12 in fips-mode=enabled
CLIResult cliResult = dist.run("--verbose", "start", "--fips-mode=enabled", "--https-key-store-password=passwordpassword",
"--https-trust-store-file=" + truststorePath, "--https-trust-store-password=passwordpassword");
cliResult.assertStarted();
});
}
private void runOnFipsEnabledDistribution(KeycloakDistribution dist, Runnable runnable) { private void runOnFipsEnabledDistribution(KeycloakDistribution dist, Runnable runnable) {
installBcFips(dist); installBcFips(dist);
runnable.run(); runnable.run();

View file

@ -143,7 +143,8 @@ HTTP/TLS:
The password of the trust store file. The password of the trust store file.
--https-trust-store-type <type> --https-trust-store-type <type>
The type of the trust store file. If not given, the type is automatically The type of the trust store file. If not given, the type is automatically
detected based on the file name. detected based on the file name. If 'fips-mode' is set to 'strict' and no
value is set, it defaults to 'BCFKS'.
Health: Health:

View file

@ -143,7 +143,8 @@ HTTP/TLS:
The password of the trust store file. The password of the trust store file.
--https-trust-store-type <type> --https-trust-store-type <type>
The type of the trust store file. If not given, the type is automatically The type of the trust store file. If not given, the type is automatically
detected based on the file name. detected based on the file name. If 'fips-mode' is set to 'strict' and no
value is set, it defaults to 'BCFKS'.
Health: Health:

View file

@ -203,7 +203,8 @@ HTTP/TLS:
The password of the trust store file. The password of the trust store file.
--https-trust-store-type <type> --https-trust-store-type <type>
The type of the trust store file. If not given, the type is automatically The type of the trust store file. If not given, the type is automatically
detected based on the file name. detected based on the file name. If 'fips-mode' is set to 'strict' and no
value is set, it defaults to 'BCFKS'.
Health: Health:

View file

@ -203,7 +203,8 @@ HTTP/TLS:
The password of the trust store file. The password of the trust store file.
--https-trust-store-type <type> --https-trust-store-type <type>
The type of the trust store file. If not given, the type is automatically The type of the trust store file. If not given, the type is automatically
detected based on the file name. detected based on the file name. If 'fips-mode' is set to 'strict' and no
value is set, it defaults to 'BCFKS'.
Health: Health:

View file

@ -149,7 +149,8 @@ HTTP/TLS:
The password of the trust store file. The password of the trust store file.
--https-trust-store-type <type> --https-trust-store-type <type>
The type of the trust store file. If not given, the type is automatically The type of the trust store file. If not given, the type is automatically
detected based on the file name. detected based on the file name. If 'fips-mode' is set to 'strict' and no
value is set, it defaults to 'BCFKS'.
Health: Health:

View file

@ -149,7 +149,8 @@ HTTP/TLS:
The password of the trust store file. The password of the trust store file.
--https-trust-store-type <type> --https-trust-store-type <type>
The type of the trust store file. If not given, the type is automatically The type of the trust store file. If not given, the type is automatically
detected based on the file name. detected based on the file name. If 'fips-mode' is set to 'strict' and no
value is set, it defaults to 'BCFKS'.
Health: Health:

View file

@ -209,7 +209,8 @@ HTTP/TLS:
The password of the trust store file. The password of the trust store file.
--https-trust-store-type <type> --https-trust-store-type <type>
The type of the trust store file. If not given, the type is automatically The type of the trust store file. If not given, the type is automatically
detected based on the file name. detected based on the file name. If 'fips-mode' is set to 'strict' and no
value is set, it defaults to 'BCFKS'.
Health: Health:

View file

@ -209,7 +209,8 @@ HTTP/TLS:
The password of the trust store file. The password of the trust store file.
--https-trust-store-type <type> --https-trust-store-type <type>
The type of the trust store file. If not given, the type is automatically The type of the trust store file. If not given, the type is automatically
detected based on the file name. detected based on the file name. If 'fips-mode' is set to 'strict' and no
value is set, it defaults to 'BCFKS'.
Health: Health:

View file

@ -108,7 +108,8 @@ HTTP/TLS:
The password of the trust store file. The password of the trust store file.
--https-trust-store-type <type> --https-trust-store-type <type>
The type of the trust store file. If not given, the type is automatically The type of the trust store file. If not given, the type is automatically
detected based on the file name. detected based on the file name. If 'fips-mode' is set to 'strict' and no
value is set, it defaults to 'BCFKS'.
Proxy: Proxy:

View file

@ -108,7 +108,8 @@ HTTP/TLS:
The password of the trust store file. The password of the trust store file.
--https-trust-store-type <type> --https-trust-store-type <type>
The type of the trust store file. If not given, the type is automatically The type of the trust store file. If not given, the type is automatically
detected based on the file name. detected based on the file name. If 'fips-mode' is set to 'strict' and no
value is set, it defaults to 'BCFKS'.
Proxy: Proxy:

View file

@ -127,7 +127,8 @@ HTTP/TLS:
The password of the trust store file. The password of the trust store file.
--https-trust-store-type <type> --https-trust-store-type <type>
The type of the trust store file. If not given, the type is automatically The type of the trust store file. If not given, the type is automatically
detected based on the file name. detected based on the file name. If 'fips-mode' is set to 'strict' and no
value is set, it defaults to 'BCFKS'.
Proxy: Proxy:

View file

@ -127,7 +127,8 @@ HTTP/TLS:
The password of the trust store file. The password of the trust store file.
--https-trust-store-type <type> --https-trust-store-type <type>
The type of the trust store file. If not given, the type is automatically The type of the trust store file. If not given, the type is automatically
detected based on the file name. detected based on the file name. If 'fips-mode' is set to 'strict' and no
value is set, it defaults to 'BCFKS'.
Proxy: Proxy:

View file

@ -979,5 +979,5 @@ For running testsuite with server using BCFIPS approved mode, those additional p
``` ```
The log should contain `KeycloakFipsSecurityProvider` mentioning "Approved mode". Something like: The log should contain `KeycloakFipsSecurityProvider` mentioning "Approved mode". Something like:
``` ```
KC(BCFIPS version 1.000203 Approved Mode) version 1.0 - class org.keycloak.crypto.fips.KeycloakFipsSecurityProvider, KC(BCFIPS version 1.000203 Approved Mode, FIPS-JVM: enabled) version 1.0 - class org.keycloak.crypto.fips.KeycloakFipsSecurityProvider,
``` ```

View file

@ -327,18 +327,15 @@ public abstract class AbstractQuarkusDeployableContainer implements DeployableCo
private void addFipsOptions(List<String> commands) { private void addFipsOptions(List<String> commands) {
commands.add("--fips-mode=" + configuration.getFipsMode().toString()); commands.add("--fips-mode=" + configuration.getFipsMode().toString());
log.debugf("Keystore file: %s, keystore type: %s, truststore file: %s, truststore type: %s", log.debugf("Keystore file: %s, truststore file: %s",
configuration.getKeystoreFile(), configuration.getKeystoreType(), configuration.getKeystoreFile(),
configuration.getTruststoreFile(), configuration.getTruststoreType()); configuration.getTruststoreFile());
commands.add("--https-key-store-file=" + configuration.getKeystoreFile()); commands.add("--https-key-store-file=" + configuration.getKeystoreFile());
commands.add("--https-key-store-type=" + configuration.getKeystoreType());
commands.add("--https-key-store-password=" + configuration.getKeystorePassword()); commands.add("--https-key-store-password=" + configuration.getKeystorePassword());
commands.add("--https-trust-store-file=" + configuration.getTruststoreFile()); commands.add("--https-trust-store-file=" + configuration.getTruststoreFile());
commands.add("--https-trust-store-type=" + configuration.getTruststoreType());
commands.add("--https-trust-store-password=" + configuration.getTruststorePassword()); commands.add("--https-trust-store-password=" + configuration.getTruststorePassword());
commands.add("--spi-truststore-file-file=" + configuration.getTruststoreFile()); commands.add("--spi-truststore-file-file=" + configuration.getTruststoreFile());
commands.add("--spi-truststore-file-password=" + configuration.getTruststorePassword()); commands.add("--spi-truststore-file-password=" + configuration.getTruststorePassword());
commands.add("--spi-truststore-file-type=" + configuration.getTruststoreType());
// BCFIPS approved mode requires passwords of at least 112 bits (14 characters) to be used. To bypass this, we use this by default // BCFIPS approved mode requires passwords of at least 112 bits (14 characters) to be used. To bypass this, we use this by default
// as testsuite uses shorter passwords everywhere // as testsuite uses shorter passwords everywhere

View file

@ -29,15 +29,11 @@ public class KeycloakQuarkusConfiguration implements ContainerConfiguration {
private String keystorePassword = System.getProperty("auth.server.keystore.password"); private String keystorePassword = System.getProperty("auth.server.keystore.password");
private String keystoreType = System.getProperty("auth.server.keystore.type");
private String truststoreFile = System.getProperty("auth.server.truststore"); private String truststoreFile = System.getProperty("auth.server.truststore");
private String truststorePassword = System.getProperty("auth.server.truststore.password"); private String truststorePassword = System.getProperty("auth.server.truststore.password");
private String truststoreType = System.getProperty("auth.server.truststore.type");
private int debugPort = -1; private int debugPort = -1;
private Path providersPath = Paths.get(System.getProperty("auth.server.home")); private Path providersPath = Paths.get(System.getProperty("auth.server.home"));
private int startupTimeoutInSeconds = 300; private int startupTimeoutInSeconds = 300;
@ -121,14 +117,6 @@ public class KeycloakQuarkusConfiguration implements ContainerConfiguration {
this.keystorePassword = keystorePassword; this.keystorePassword = keystorePassword;
} }
public String getKeystoreType() {
return keystoreType;
}
public void setKeystoreType(String keystoreType) {
this.keystoreType = keystoreType;
}
public String getTruststoreFile() { public String getTruststoreFile() {
return truststoreFile; return truststoreFile;
} }
@ -145,14 +133,6 @@ public class KeycloakQuarkusConfiguration implements ContainerConfiguration {
this.truststorePassword = truststorePassword; this.truststorePassword = truststorePassword;
} }
public String getTruststoreType() {
return truststoreType;
}
public void setTruststoreType(String truststoreType) {
this.truststoreType = truststoreType;
}
public Path getProvidersPath() { public Path getProvidersPath() {
return providersPath; return providersPath;
} }