fix: refining https file type detection (#33703)

also making common trustore logic align

closes: #33649

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
Steven Hawkins 2024-10-22 13:05:56 -04:00 committed by GitHub
parent e3936a9b38
commit af1a5ea2a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 164 additions and 97 deletions

View file

@ -39,7 +39,7 @@ public class KeystoreUtil {
public enum KeystoreFormat {
JKS("jks"),
PKCS12("p12", "pfx"),
PKCS12("p12", "pfx", "pkcs12"),
BCFKS("bcfks");
// Typical file extension for this keystore format
@ -106,6 +106,17 @@ public class KeystoreUtil {
}
}
public static Optional<KeystoreFormat> getKeystoreFormat(String path) {
int lastDotIndex = path.lastIndexOf('.');
if (lastDotIndex > -1) {
String ext = path.substring(lastDotIndex + 1).toLowerCase();
return Arrays.stream(KeystoreUtil.KeystoreFormat.values())
.filter(ksFormat -> ksFormat.getFileExtensions().contains(ext))
.findFirst();
}
return Optional.empty();
}
/**
* Try to return supported keystore type
@ -120,13 +131,9 @@ public class KeystoreUtil {
if (preferredType != null) return preferredType;
// Fallback to path
int lastDotIndex = path.lastIndexOf('.');
if (lastDotIndex > -1) {
String ext = path.substring(lastDotIndex + 1).toLowerCase();
Optional<KeystoreFormat> detectedType = Arrays.stream(KeystoreUtil.KeystoreFormat.values())
.filter(ksFormat -> ksFormat.getFileExtensions().contains(ext))
.findFirst();
if (detectedType.isPresent()) return detectedType.get().toString();
Optional<KeystoreFormat> format = getKeystoreFormat(path);
if (format.isPresent()) {
return format.get().toString();
}
// Fallback to default

View file

@ -1,8 +1,9 @@
package org.keycloak.common.util;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import org.junit.Test;
public class KeystoreUtilTest {
@ -11,6 +12,15 @@ public class KeystoreUtilTest {
assertEquals("x", KeystoreUtil.getKeystoreType("x", "y", "z"));
assertEquals("z", KeystoreUtil.getKeystoreType(null, "y", "z"));
assertEquals(KeystoreUtil.KeystoreFormat.PKCS12.name(), KeystoreUtil.getKeystoreType(null, "y.pfx", "z"));
assertEquals(KeystoreUtil.KeystoreFormat.PKCS12.name(), KeystoreUtil.getKeystoreType(null, "y.pkcs12", "z"));
}
@Test
public void testGetFormat() {
assertFalse(KeystoreUtil.getKeystoreFormat("some.file").isPresent());
assertFalse(KeystoreUtil.getKeystoreFormat("somepfx").isPresent());
assertEquals(KeystoreUtil.KeystoreFormat.PKCS12, KeystoreUtil.getKeystoreFormat("file.pfx").get());
assertEquals(KeystoreUtil.KeystoreFormat.JKS, KeystoreUtil.getKeystoreFormat("file.jks").get());
}
}

View file

@ -275,7 +275,7 @@ To ensure proper TLS configuration, use the `tlsSecret` and `truststores` fields
If you need to provide trusted certificates, the Keycloak CR provides a top level feature for configuring the server's truststore as discussed in <@links.server id="keycloak-truststore"/>.
Use the truststores stanza of the Keycloak spec to specify Secrets containing PEM encoded files, or PKCS12 files with extension `.p12` or `.pfx`, for example:
Use the truststores stanza of the Keycloak spec to specify Secrets containing PEM encoded files, or PKCS12 files with extension `.p12`, `.pfx`, or `.pkcs12`, for example:
[source,yaml]
----

View file

@ -22,12 +22,20 @@ When you use a pair of matching certificate and private key files in PEM format,
{project_name} creates a keystore out of these files in memory and uses this keystore afterwards.
== Providing a Java Keystore
== Providing a Keystore
When no keystore file is explicitly configured, but `http-enabled` is set to false, {project_name} looks for a `conf/server.keystore` file.
As an alternative, you can use an existing keystore by running the following command:
<@kc.start parameters="--https-key-store-file=/path/to/existing-keystore-file"/>
Recognized file extensions for a keystore:
* `.p12`, `.pkcs12`, and `.pfx` for a pkcs12 file
* `.jks`, and `.keystore` for a jks file
* `.key`, `.crt`, and `.pem` for a pem file
If your keystore does not have an extension matching its file type, you will also need to set the `https-key-store-type` option.
=== Setting the Keystore password
You can set a secure password for your keystore using the `https-key-store-password` option:
<@kc.start parameters="--https-key-store-password=<value>"/>

View file

@ -12,7 +12,7 @@ The certificates of these clients or servers, or the CA that signed these certif
== Configuring the System Truststore
The existing Java default truststore certs will always be trusted. If you need additional certificates, which will be the case if you have self-signed or internal certificate authorities that are not recognized by the JRE, they can be included in the `conf/truststores` directory or subdirectories. The certs may be in PEM files, or PKCS12 files with extension `.p12` or `.pfx`. If in PKCS12, the certs must be unencrypted - meaning no password is expected.
The existing Java default truststore certs will always be trusted. If you need additional certificates, which will be the case if you have self-signed or internal certificate authorities that are not recognized by the JRE, they can be included in the `conf/truststores` directory or subdirectories. The certs may be in PEM files, or PKCS12 files with extension `.p12`, `.pfx`, or `.pkcs12`. If in PKCS12, the certs must be unencrypted - meaning no password is expected.
If you need an alternative path, use the `--truststore-paths` option to specify additional files or directories where PEM or PKCS12 files are located. Paths are relative to where you launched {project_name}, so absolute paths are recommended instead. If a directory is specified, it will be recursively scanned for truststore files.

View file

@ -35,6 +35,14 @@ By default, {project_name} uses the System Truststore to validate certificates.
If you need to use a dedicated truststore for mTLS, you can configure the location of this truststore by running the following command:
<@kc.start parameters="--https-trust-store-file=/path/to/file --https-trust-store-password=<value>"/>
Recognized file extensions for a truststore:
* `.p12`, `.pkcs12`, and `.pfx` for a pkcs12 file
* `.jks`, and `.truststore` for a jks file
* `.ca`, `.crt`, and `.pem` for a pem file
If your truststore does not have an extension matching its file type, you will also need to set the `https-key-store-type` option.
== Additional resources
=== Using mTLS for outgoing HTTP requests

View file

@ -9,7 +9,7 @@ public class TruststoreOptions {
public static final Option<List<String>> TRUSTSTORE_PATHS = OptionBuilder.listOptionBuilder("truststore-paths", String.class)
.category(OptionCategory.TRUSTSTORE)
.description("List of pkcs12 (p12 or pfx file extensions), PEM files, or directories containing those files that will be used as a system truststore.")
.description("List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or directories containing those files that will be used as a system truststore.")
.build();
public static final Option<HostnameVerificationPolicy> HOSTNAME_VERIFICATION_POLICY = new OptionBuilder<>("tls-hostname-verifier", HostnameVerificationPolicy.class)

View file

@ -44,6 +44,9 @@ public final class ExecutionExceptionHandler implements CommandLine.IExecutionEx
if (cause instanceof PropertyException) {
PrintWriter writer = cmd.getErr();
writer.println(cmd.getColorScheme().errorText(cause.getMessage()));
if (verbose && cause.getCause() != null) {
dumpException(writer, cause.getCause());
}
return ShortErrorMessageHandler.getInvalidInputExitCode(cause, cmd);
}
error(cmd.getErr(), "Failed to run '" + parseResult.subcommands().stream()

View file

@ -1,5 +1,6 @@
package org.keycloak.quarkus.runtime.configuration.mappers;
import io.quarkus.runtime.util.ClassPathUtils;
import io.quarkus.vertx.http.runtime.CertificateConfig;
import io.quarkus.vertx.http.runtime.options.TlsUtils;
import io.smallrye.config.ConfigSourceInterceptorContext;
@ -14,9 +15,10 @@ import org.keycloak.quarkus.runtime.configuration.Configuration;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
@ -24,6 +26,10 @@ public final class HttpPropertyMappers {
private static final int MIN_MAX_THREADS = 50;
private static final String QUARKUS_HTTPS_CERT_FILES = "quarkus.http.ssl.certificate.files";
private static final String QUARKUS_HTTPS_CERT_KEY_FILES = "quarkus.http.ssl.certificate.key-files";
private static final String QUARKUS_HTTPS_KEY_STORE_FILE = "quarkus.http.ssl.certificate.key-store-file";
private static final String QUARKUS_HTTPS_TRUST_STORE_FILE = "quarkus.http.ssl.certificate.trust-store-file";
private static final String QUARKUS_HTTPS_TRUST_STORE_FILE_TYPE = "quarkus.http.ssl.certificate.trust-store-file-type";
private static final String QUARKUS_HTTPS_KEY_STORE_FILE_TYPE = "quarkus.http.ssl.certificate.key-store-file-type";
private HttpPropertyMappers(){}
@ -72,17 +78,18 @@ public final class HttpPropertyMappers {
.build(),
fromOption(HttpOptions.HTTPS_CERTIFICATE_FILE)
.to(QUARKUS_HTTPS_CERT_FILES)
.transformer(HttpPropertyMappers.validatePath(QUARKUS_HTTPS_CERT_FILES))
.transformer(HttpPropertyMappers::transformPath)
.paramLabel("file")
.build(),
fromOption(HttpOptions.HTTPS_CERTIFICATE_KEY_FILE)
.to(QUARKUS_HTTPS_CERT_KEY_FILES)
.transformer(HttpPropertyMappers.validatePath(QUARKUS_HTTPS_CERT_KEY_FILES))
.transformer(HttpPropertyMappers::transformPath)
.paramLabel("file")
.build(),
fromOption(HttpOptions.HTTPS_KEY_STORE_FILE
.withRuntimeSpecificDefault(getDefaultKeystorePathValue()))
.to("quarkus.http.ssl.certificate.key-store-file")
.to(QUARKUS_HTTPS_KEY_STORE_FILE)
.transformer(HttpPropertyMappers::transformPath)
.paramLabel("file")
.build(),
fromOption(HttpOptions.HTTPS_KEY_STORE_PASSWORD)
@ -91,12 +98,13 @@ public final class HttpPropertyMappers {
.isMasked(true)
.build(),
fromOption(HttpOptions.HTTPS_KEY_STORE_TYPE)
.to("quarkus.http.ssl.certificate.key-store-file-type")
.mapFrom(SecurityOptions.FIPS_MODE, HttpPropertyMappers::resolveKeyStoreType)
.to(QUARKUS_HTTPS_KEY_STORE_FILE_TYPE)
.paramLabel("type")
.build(),
fromOption(HttpOptions.HTTPS_TRUST_STORE_FILE)
.to("quarkus.http.ssl.certificate.trust-store-file")
.to(QUARKUS_HTTPS_TRUST_STORE_FILE)
.transformer(HttpPropertyMappers::transformPath)
.paramLabel("file")
.build(),
fromOption(HttpOptions.HTTPS_TRUST_STORE_PASSWORD)
@ -105,8 +113,9 @@ public final class HttpPropertyMappers {
.isMasked(true)
.build(),
fromOption(HttpOptions.HTTPS_TRUST_STORE_TYPE)
.to("quarkus.http.ssl.certificate.trust-store-file-type")
.mapFrom(SecurityOptions.FIPS_MODE, HttpPropertyMappers::resolveKeyStoreType)
.to(QUARKUS_HTTPS_TRUST_STORE_FILE_TYPE)
.transformer(HttpPropertyMappers::resolveKeyStoreType)
.paramLabel("type")
.build(),
fromOption(HttpOptions.HTTP_MAX_QUEUED_REQUESTS)
@ -130,10 +139,12 @@ public final class HttpPropertyMappers {
public static void validateConfig() {
boolean enabled = isHttpEnabled(Configuration.getOptionalKcValue(HttpOptions.HTTP_ENABLED.getKey()).orElse(null));
boolean trustStoreFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_TRUST_STORE_FILE.getKey()).isPresent();
boolean keyStoreFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_KEY_STORE_FILE.getKey()).isPresent();
Optional<String> certFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_CERTIFICATE_FILE.getKey());
Optional<String> keystoreFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_KEY_STORE_FILE.getKey());
if (!enabled && certFile.isEmpty() && keystoreFile.isEmpty()) {
throw new PropertyException(Messages.httpsConfigurationNotSet());
}
if (trustStoreFile) {
CertificateConfig config = new CertificateConfig();
config.trustStoreFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_TRUST_STORE_FILE.getKey()).map(Paths::get);
@ -143,20 +154,7 @@ public final class HttpPropertyMappers {
config.trustStoreCertAlias = Configuration.getOptionalValue("quarkus.http.ssl.certificate.trust-store-cert-alias");
config.trustStoreFiles = Optional.empty();
try {
TlsUtils.computeTrustOptions(config, config.trustStorePassword);
} catch (IOException e) {
throw new PropertyException("Failed to load 'https-trust-store' material.", e);
} catch (IllegalArgumentException e) {
throw new PropertyException("Unable to determine 'https-trust-store-type' automatically. " +
"Adjust the file extension or specify the property.", e);
}
}
if (keyStoreFile) {
CertificateConfig config = new CertificateConfig();
config.keyStoreFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_KEY_STORE_FILE.getKey()).map(Paths::get);
config.keyStoreFile = keystoreFile.map(Paths::get);
config.keyStorePassword = Configuration.getOptionalKcValue(HttpOptions.HTTPS_KEY_STORE_PASSWORD.getKey());
config.keyStoreFileType = Configuration.getOptionalKcValue(HttpOptions.HTTPS_KEY_STORE_TYPE.getKey());
config.keyStoreProvider = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-provider");
@ -164,34 +162,37 @@ public final class HttpPropertyMappers {
config.keyStoreAliasPassword = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-alias-password");
config.keyStoreAliasPasswordKey = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-alias-password-key");
config.keyStoreKeyAlias = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-key-alias");
config.keyFiles = Optional.empty();
config.files = Optional.empty();
config.keyFiles = Configuration.getOptionalKcValue(HttpOptions.HTTPS_CERTIFICATE_KEY_FILE.getKey()).map(Paths::get).map(List::of);
config.files = certFile.map(Paths::get).map(List::of);
try {
TlsUtils.computeTrustOptions(config, config.trustStorePassword);
} catch (IOException e) {
throw new PropertyException("Failed to load 'https-trust-store' material: " + e.getClass().getSimpleName() + " " + e.getMessage(), e);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains(QUARKUS_HTTPS_TRUST_STORE_FILE_TYPE)) {
throw new PropertyException("Unable to determine 'https-trust-store-type' automatically. " +
"Adjust the file extension or specify the property.", e);
}
throw new PropertyException(e.getMessage(), e);
}
try {
TlsUtils.computeKeyStoreOptions(config, config.keyStorePassword, config.keyStoreAliasPassword);
} catch (IOException e) {
throw new PropertyException("Failed to load 'https-key-store' material.", e);
throw new PropertyException("Failed to load 'https-key-' material: " + e.getClass().getSimpleName() + " " + e.getMessage(), e);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains(QUARKUS_HTTPS_KEY_STORE_FILE_TYPE)) {
throw new PropertyException("Unable to determine 'https-key-store-type' automatically. " +
"Adjust the file extension or specify the property.", e);
}
}
if (!enabled) {
Optional<String> value = Configuration.getOptionalKcValue(HttpOptions.HTTPS_CERTIFICATE_FILE.getKey());
if (value.isEmpty()) {
value = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-file");
}
if (value.isEmpty()) {
throw new PropertyException(Messages.httpsConfigurationNotSet());
}
throw new PropertyException(e.getMessage(), e);
}
}
private static BiFunction<String, ConfigSourceInterceptorContext, String> validatePath(String key) {
return (value, context) -> Environment.isWindows() && value != null && value.equals(context.proceed(key).getValue()) ? value.replace("\\", "/") : value;
private static String transformPath(String value, ConfigSourceInterceptorContext context) {
return value == null ? value : ClassPathUtils.toResourceName(Path.of(value));
}
private static String getHttpEnabledTransformer(String value, ConfigSourceInterceptorContext context) {

View file

@ -288,6 +288,8 @@ public class PropertyMapper<T> {
* NOTE: This transformer will not apply to the mapFrom value. When using
* {@link #mapFrom} you generally need a transformer specifically for the parent
* value, see {@link #mapFrom(Option, BiFunction)}
* <p>
* The value passed into the transformer may be null if the property has no value set, and no default
*/
public Builder<T> transformer(BiFunction<String, ConfigSourceInterceptorContext, String> mapper) {
this.mapper = mapper;

View file

@ -207,6 +207,21 @@ public class PicocliTest extends AbstractConfigurationTest {
+ "\nPossible solutions: --db-url, --db-url-host, --db-url-database, --db-url-port, --db-url-properties, --db-username, --db-password, --db-schema, --db-pool-initial-size, --db-pool-min-size, --db-pool-max-size, --db-driver, --db"));
}
@Test
public void httpStoreTypeValidation() {
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--https-key-store-file=not-there.ks", "--hostname-strict=false");
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
assertThat(nonRunningPicocli.getErrString(), containsString("Unable to determine 'https-key-store-type' automatically. Adjust the file extension or specify the property"));
nonRunningPicocli = pseudoLaunch("start", "--https-key-store-file=not-there.ks", "--hostname-strict=false", "--https-key-store-type=jdk");
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
assertThat(nonRunningPicocli.getErrString(), containsString("Failed to load 'https-key-' material: NoSuchFileException not-there.ks"));
nonRunningPicocli = pseudoLaunch("start", "--https-trust-store-file=not-there.jks", "--https-key-store-file=not-there.ks", "--hostname-strict=false", "--https-key-store-type=jdk");
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
assertThat(nonRunningPicocli.getErrString(), containsString("No trust store password provided"));
}
@Test
public void failSingleParamWithSpace() {
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--db postgres");

View file

@ -23,6 +23,8 @@ import static org.junit.Assert.assertTrue;
import static org.keycloak.quarkus.runtime.Environment.isWindows;
import static org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource.CLI_ARGS;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
@ -492,6 +494,17 @@ public class ConfigurationTest extends AbstractConfigurationTest {
assertEquals("2h", createConfig().getConfigValue("quarkus.http.ssl.certificate.reload-period").getValue());
}
@Test
public void testHttpsPaths() {
ConfigArgsConfigSource.setCliArgs("--https-certificate-file=\\some\\file");
String expected = "\\some\\file";
if (FileSystems.getDefault().getSeparator().equals("\\")) {
expected = "/some/file";
}
assertEquals(expected, createConfig().getConfigValue("quarkus.http.ssl.certificate.files").getValue());
}
@Test
public void testCacheMaxCount() {
int maxCount = 500;

View file

@ -173,13 +173,13 @@ public class QuarkusPropertiesDistTest {
@Test
@BeforeStartDistribution(ForceRebuild.class)
@DisabledOnOs(value = { OS.WINDOWS }, disabledReason = "Windows uses a different path separator.")
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false",
@Launch({ "start", "--verbose", "--http-enabled=true", "--hostname-strict=false",
"--https-certificate-file=/tmp/kc/bin/../conf/server.crt.pem",
"--https-certificate-key-file=/tmp/kc/bin/../conf/server.key.pem" })
@Order(13)
void testHttpCertsPathTransformer(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
assertThat(cliResult.getOutput(),containsString("ERROR: /tmp/kc/bin/../conf/server.crt.pem"));
assertThat(cliResult.getErrorOutput(),containsString("Failed to load 'https-key-' material: NoSuchFileException /tmp/kc/bin/../conf/server.crt.pem"));
}
@Test
@ -191,7 +191,7 @@ public class QuarkusPropertiesDistTest {
@Order(14)
void testHttpCertsPathTransformerOnWindows(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
assertThat(cliResult.getOutput(),containsString("ERROR: C:/tmp/kc/bin/../conf/server.crt.pem"));
assertThat(cliResult.getErrorOutput(),containsString("Failed to load 'https-key-' material: NoSuchFileException C:/tmp/kc/bin/../conf/server.crt.pem"));
}
public static class AddConsoleHandlerFromQuarkusProps implements Consumer<KeycloakDistribution> {

View file

@ -150,8 +150,8 @@ Truststore:
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
DEFAULT. Default: DEFAULT.
--truststore-paths <truststore-paths>
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
containing those files that will be used as a system truststore.
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
directories containing those files that will be used as a system truststore.
Export:

View file

@ -248,8 +248,8 @@ Truststore:
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
DEFAULT. Default: DEFAULT.
--truststore-paths <truststore-paths>
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
containing those files that will be used as a system truststore.
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
directories containing those files that will be used as a system truststore.
Export:

View file

@ -150,8 +150,8 @@ Truststore:
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
DEFAULT. Default: DEFAULT.
--truststore-paths <truststore-paths>
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
containing those files that will be used as a system truststore.
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
directories containing those files that will be used as a system truststore.
Import:

View file

@ -248,8 +248,8 @@ Truststore:
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
DEFAULT. Default: DEFAULT.
--truststore-paths <truststore-paths>
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
containing those files that will be used as a system truststore.
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
directories containing those files that will be used as a system truststore.
Import:

View file

@ -321,8 +321,8 @@ Truststore:
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
DEFAULT. Default: DEFAULT.
--truststore-paths <truststore-paths>
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
containing those files that will be used as a system truststore.
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
directories containing those files that will be used as a system truststore.
Security:

View file

@ -454,8 +454,8 @@ Truststore:
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
DEFAULT. Default: DEFAULT.
--truststore-paths <truststore-paths>
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
containing those files that will be used as a system truststore.
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
directories containing those files that will be used as a system truststore.
Security:

View file

@ -322,8 +322,8 @@ Truststore:
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
DEFAULT. Default: DEFAULT.
--truststore-paths <truststore-paths>
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
containing those files that will be used as a system truststore.
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
directories containing those files that will be used as a system truststore.
Security:

View file

@ -455,8 +455,8 @@ Truststore:
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
DEFAULT. Default: DEFAULT.
--truststore-paths <truststore-paths>
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
containing those files that will be used as a system truststore.
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
directories containing those files that will be used as a system truststore.
Security:

View file

@ -273,8 +273,8 @@ Truststore:
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
DEFAULT. Default: DEFAULT.
--truststore-paths <truststore-paths>
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
containing those files that will be used as a system truststore.
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
directories containing those files that will be used as a system truststore.
Bootstrap Admin:

View file

@ -395,8 +395,8 @@ Truststore:
Possible values are: ANY, WILDCARD (deprecated), STRICT (deprecated),
DEFAULT. Default: DEFAULT.
--truststore-paths <truststore-paths>
List of pkcs12 (p12 or pfx file extensions), PEM files, or directories
containing those files that will be used as a system truststore.
List of pkcs12 (p12, pfx, or pkcs12 file extensions), PEM files, or
directories containing those files that will be used as a system truststore.
Bootstrap Admin:

View file

@ -19,6 +19,7 @@ package org.keycloak.truststore;
import org.jboss.logging.Logger;
import org.keycloak.common.util.KeystoreUtil;
import org.keycloak.common.util.KeystoreUtil.KeystoreFormat;
import java.io.File;
import java.io.FileInputStream;
@ -101,19 +102,18 @@ public class TruststoreBuilder {
if (f.isDirectory()) {
mergeFiles(Stream.of(f.listFiles()).map(File::getAbsolutePath).toArray(String[]::new), truststore, false, discoveredFiles);
} else {
if (file.endsWith(".p12") || file.endsWith(".pfx")) {
var format = KeystoreUtil.getKeystoreFormat(file).orElse(null);
if (format == KeystoreFormat.PKCS12) {
mergeTrustStore(truststore, file, loadStore(file, PKCS12, null));
if (!topLevel) {
discoveredFiles.add(f.getAbsolutePath());
}
} else {
if (mergePemFile(truststore, file, topLevel) && !topLevel) {
} else if (mergePemFile(truststore, file, topLevel) && !topLevel) {
discoveredFiles.add(f.getAbsolutePath());
}
}
}
}
}
static KeyStore createPkcs12KeyStore() {
try {