fix: adds pfx as a recognized extension (#26876)
closes #24661 Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
parent
5a2b145e4e
commit
3a04acab51
8 changed files with 48 additions and 38 deletions
|
@ -28,6 +28,7 @@ import java.security.KeyStore;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,22 +39,30 @@ public class KeystoreUtil {
|
||||||
|
|
||||||
public enum KeystoreFormat {
|
public enum KeystoreFormat {
|
||||||
JKS("jks"),
|
JKS("jks"),
|
||||||
PKCS12("p12"),
|
PKCS12("p12", "pfx"),
|
||||||
BCFKS("bcfks");
|
BCFKS("bcfks");
|
||||||
|
|
||||||
// Typical file extension for this keystore format
|
// Typical file extension for this keystore format
|
||||||
private final String fileExtension;
|
private final List<String> fileExtensions;
|
||||||
KeystoreFormat(String extension) {
|
KeystoreFormat(String... extensions) {
|
||||||
this.fileExtension = extension;
|
this.fileExtensions = Arrays.asList(extensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFileExtension() {
|
public List<String> getFileExtensions() {
|
||||||
return fileExtension;
|
return fileExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrimaryExtension() {
|
||||||
|
return fileExtensions.get(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static KeyStore loadKeyStore(String filename, String password) throws Exception {
|
public static KeyStore loadKeyStore(String filename, String password) throws Exception {
|
||||||
String keystoreType = getKeystoreType(null, filename, KeyStore.getDefaultType());
|
return loadKeyStore(filename, password, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static KeyStore loadKeyStore(String filename, String password, String preferedType) throws Exception {
|
||||||
|
String keystoreType = getKeystoreType(preferedType, filename, KeyStore.getDefaultType());
|
||||||
KeyStore trustStore = KeyStore.getInstance(keystoreType);
|
KeyStore trustStore = KeyStore.getInstance(keystoreType);
|
||||||
InputStream trustStream = null;
|
InputStream trustStream = null;
|
||||||
if (filename.startsWith(GenericConstants.PROTOCOL_CLASSPATH)) {
|
if (filename.startsWith(GenericConstants.PROTOCOL_CLASSPATH)) {
|
||||||
|
@ -71,7 +80,7 @@ public class KeystoreUtil {
|
||||||
trustStream = new FileInputStream(new File(filename));
|
trustStream = new FileInputStream(new File(filename));
|
||||||
}
|
}
|
||||||
try (InputStream is = trustStream) {
|
try (InputStream is = trustStream) {
|
||||||
trustStore.load(is, password.toCharArray());
|
trustStore.load(is, password == null ? null : password.toCharArray());
|
||||||
}
|
}
|
||||||
return trustStore;
|
return trustStore;
|
||||||
}
|
}
|
||||||
|
@ -115,7 +124,7 @@ public class KeystoreUtil {
|
||||||
if (lastDotIndex > -1) {
|
if (lastDotIndex > -1) {
|
||||||
String ext = path.substring(lastDotIndex + 1).toLowerCase();
|
String ext = path.substring(lastDotIndex + 1).toLowerCase();
|
||||||
Optional<KeystoreFormat> detectedType = Arrays.stream(KeystoreUtil.KeystoreFormat.values())
|
Optional<KeystoreFormat> detectedType = Arrays.stream(KeystoreUtil.KeystoreFormat.values())
|
||||||
.filter(ksFormat -> ksFormat.getFileExtension().equals(ext))
|
.filter(ksFormat -> ksFormat.getFileExtensions().contains(ext))
|
||||||
.findFirst();
|
.findFirst();
|
||||||
if (detectedType.isPresent()) return detectedType.get().toString();
|
if (detectedType.isPresent()) return detectedType.get().toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class KeystoreUtilTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetType() {
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -27,8 +27,6 @@ import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
|
@ -100,12 +98,12 @@ public class FileTruststoreProviderFactory implements TruststoreProviderFactory
|
||||||
}
|
}
|
||||||
String type = KeystoreUtil.getKeystoreType(configuredType, storepath, KeyStore.getDefaultType());
|
String type = KeystoreUtil.getKeystoreType(configuredType, storepath, KeyStore.getDefaultType());
|
||||||
try {
|
try {
|
||||||
truststore = loadStore(storepath, type, pass == null ? null :pass.toCharArray());
|
truststore = KeystoreUtil.loadKeyStore(storepath, pass, type);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// in fips mode the default truststore type can be pkcs12, but the cacerts file will still be jks
|
// in fips mode the default truststore type can be pkcs12, but the cacerts file will still be jks
|
||||||
if (system && !"jks".equalsIgnoreCase(type)) {
|
if (system && !"jks".equalsIgnoreCase(type)) {
|
||||||
try {
|
try {
|
||||||
truststore = loadStore(storepath, "jks", pass == null ? null :pass.toCharArray());
|
truststore = KeystoreUtil.loadKeyStore(storepath, pass, "jks");
|
||||||
} catch (Exception e1) {
|
} catch (Exception e1) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,14 +128,6 @@ public class FileTruststoreProviderFactory implements TruststoreProviderFactory
|
||||||
log.debugf("File truststore provider initialized: %s, Truststore type: %s", new File(storepath).getAbsolutePath(), type);
|
log.debugf("File truststore provider initialized: %s, Truststore type: %s", new File(storepath).getAbsolutePath(), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private KeyStore loadStore(String path, String type, char[] password) throws Exception {
|
|
||||||
KeyStore ks = KeyStore.getInstance(type);
|
|
||||||
try (InputStream is = new FileInputStream(path)) {
|
|
||||||
ks.load(is, password);
|
|
||||||
return ks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postInit(KeycloakSessionFactory factory) {
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
package org.keycloak.truststore;
|
package org.keycloak.truststore;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.common.util.KeystoreUtil;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
|
@ -30,7 +30,6 @@ import java.security.cert.CertificateException;
|
||||||
import java.security.cert.CertificateFactory;
|
import java.security.cert.CertificateFactory;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -155,8 +154,7 @@ public class TruststoreBuilder {
|
||||||
|
|
||||||
if (defaultTrustStore.exists()) {
|
if (defaultTrustStore.exists()) {
|
||||||
String path = defaultTrustStore.getAbsolutePath();
|
String path = defaultTrustStore.getAbsolutePath();
|
||||||
mergeTrustStore(truststore, path,
|
mergeTrustStore(truststore, path, loadStore(path, type, password));
|
||||||
loadStore(path, type, Optional.ofNullable(password).map(String::toCharArray).orElse(null)));
|
|
||||||
} else {
|
} else {
|
||||||
LOGGER.warnf("Default truststore was to be included, but could not be found at: %s", defaultTrustStore);
|
LOGGER.warnf("Default truststore was to be included, but could not be found at: %s", defaultTrustStore);
|
||||||
}
|
}
|
||||||
|
@ -173,11 +171,9 @@ public class TruststoreBuilder {
|
||||||
return new File(securityDirectory, "cacerts");
|
return new File(securityDirectory, "cacerts");
|
||||||
}
|
}
|
||||||
|
|
||||||
static KeyStore loadStore(String path, String type, char[] password) {
|
static KeyStore loadStore(String path, String type, String password) {
|
||||||
try (InputStream is = new FileInputStream(path)) {
|
try {
|
||||||
KeyStore ks = KeyStore.getInstance(type);
|
return KeystoreUtil.loadKeyStore(path, password, type);
|
||||||
ks.load(is, password);
|
|
||||||
return ks;
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
"Failed to initialize truststore: " + new File(path).getAbsolutePath() + ", type: " + type, e);
|
"Failed to initialize truststore: " + new File(path).getAbsolutePath() + ", type: " + type, e);
|
||||||
|
|
|
@ -46,10 +46,9 @@ public class TruststoreBuilderTest {
|
||||||
assertTrue(storeWithDefaultsAliases.containsAll(storeWithoutDefaultsAliases));
|
assertTrue(storeWithDefaultsAliases.containsAll(storeWithoutDefaultsAliases));
|
||||||
|
|
||||||
// saving / loading should provide the certs even without a password
|
// saving / loading should provide the certs even without a password
|
||||||
char[] password = null;
|
File saved = TruststoreBuilder.saveTruststore(storeWithDefaults, "target", null);
|
||||||
File saved = TruststoreBuilder.saveTruststore(storeWithDefaults, "target", password);
|
|
||||||
|
|
||||||
KeyStore savedLoaded = TruststoreBuilder.loadStore(saved.getAbsolutePath(), TruststoreBuilder.PKCS12, password);
|
KeyStore savedLoaded = TruststoreBuilder.loadStore(saved.getAbsolutePath(), TruststoreBuilder.PKCS12, null);
|
||||||
assertEquals(certs, Collections.list(savedLoaded.aliases()).size());
|
assertEquals(certs, Collections.list(savedLoaded.aliases()).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class KeystoreUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static KeystoreInfo generateKeystore(TemporaryFolder folder, KeystoreUtil.KeystoreFormat keystoreType, String subject, String keystorePassword, String keyPassword) throws Exception {
|
public static KeystoreInfo generateKeystore(TemporaryFolder folder, KeystoreUtil.KeystoreFormat keystoreType, String subject, String keystorePassword, String keyPassword) throws Exception {
|
||||||
String fileName = "keystore." + keystoreType.getFileExtension();
|
String fileName = "keystore." + keystoreType.getPrimaryExtension();
|
||||||
|
|
||||||
KeyPair keyPair = KeyUtils.generateRsaKeyPair(2048);
|
KeyPair keyPair = KeyUtils.generateRsaKeyPair(2048);
|
||||||
X509Certificate certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, subject);
|
X509Certificate certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, subject);
|
||||||
|
|
|
@ -513,13 +513,13 @@ public class KcAdmTest extends AbstractAdmCliTest {
|
||||||
@Test
|
@Test
|
||||||
public void testCRUDWithOnTheFlyUserAuthWithSignedJwtClient_JKSKeystore() throws IOException {
|
public void testCRUDWithOnTheFlyUserAuthWithSignedJwtClient_JKSKeystore() throws IOException {
|
||||||
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreUtil.KeystoreFormat.JKS);
|
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreUtil.KeystoreFormat.JKS);
|
||||||
testCRUDWithOnTheFlyUserAuthWithSignedJwtClient(KeystoreUtil.KeystoreFormat.JKS.getFileExtension());
|
testCRUDWithOnTheFlyUserAuthWithSignedJwtClient(KeystoreUtil.KeystoreFormat.JKS.getPrimaryExtension());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCRUDWithOnTheFlyUserAuthWithSignedJwtClient_PKCS12Keystore() throws IOException {
|
public void testCRUDWithOnTheFlyUserAuthWithSignedJwtClient_PKCS12Keystore() throws IOException {
|
||||||
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreUtil.KeystoreFormat.PKCS12);
|
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreUtil.KeystoreFormat.PKCS12);
|
||||||
testCRUDWithOnTheFlyUserAuthWithSignedJwtClient(KeystoreUtil.KeystoreFormat.PKCS12.getFileExtension());
|
testCRUDWithOnTheFlyUserAuthWithSignedJwtClient(KeystoreUtil.KeystoreFormat.PKCS12.getPrimaryExtension());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testCRUDWithOnTheFlyUserAuthWithSignedJwtClient(String keystoreFileExtension) throws IOException {
|
private void testCRUDWithOnTheFlyUserAuthWithSignedJwtClient(String keystoreFileExtension) throws IOException {
|
||||||
|
|
|
@ -506,13 +506,13 @@ public class KcRegTest extends AbstractRegCliTest {
|
||||||
@Test
|
@Test
|
||||||
public void testCRUDWithOnTheFlyUserAuthWithSignedJwtClient_JKSKeystore() throws IOException {
|
public void testCRUDWithOnTheFlyUserAuthWithSignedJwtClient_JKSKeystore() throws IOException {
|
||||||
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreUtil.KeystoreFormat.JKS);
|
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreUtil.KeystoreFormat.JKS);
|
||||||
testCRUDWithOnTheFlyUserAuthWithSignedJwtClient(KeystoreUtil.KeystoreFormat.JKS.getFileExtension());
|
testCRUDWithOnTheFlyUserAuthWithSignedJwtClient(KeystoreUtil.KeystoreFormat.JKS.getPrimaryExtension());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCRUDWithOnTheFlyUserAuthWithSignedJwtClient_PKCS12Keystore() throws IOException {
|
public void testCRUDWithOnTheFlyUserAuthWithSignedJwtClient_PKCS12Keystore() throws IOException {
|
||||||
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreUtil.KeystoreFormat.PKCS12);
|
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreUtil.KeystoreFormat.PKCS12);
|
||||||
testCRUDWithOnTheFlyUserAuthWithSignedJwtClient(KeystoreUtil.KeystoreFormat.PKCS12.getFileExtension());
|
testCRUDWithOnTheFlyUserAuthWithSignedJwtClient(KeystoreUtil.KeystoreFormat.PKCS12.getPrimaryExtension());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testCRUDWithOnTheFlyUserAuthWithSignedJwtClient(String keystoreFileExtension) throws IOException {
|
private void testCRUDWithOnTheFlyUserAuthWithSignedJwtClient(String keystoreFileExtension) throws IOException {
|
||||||
|
|
Loading…
Reference in a new issue