More flexibility in keystore related tests, Make keycloak to notify which keystore types it supports, Support for BCFKS
Closes #14964
This commit is contained in:
parent
5ebb6e9c10
commit
55c514ad56
21 changed files with 635 additions and 152 deletions
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
|
@ -319,7 +319,8 @@ jobs:
|
|||
run: |
|
||||
declare -A PARAMS TESTGROUP
|
||||
PARAMS["bcfips-nonapproved-pkcs12"]="-Pauth-server-quarkus,auth-server-fips140-2"
|
||||
TESTGROUP["group1"]="-Dtest=org.keycloak.testsuite.forms.**" # Tests in the package "forms"
|
||||
# Tests in the package "forms" and some keystore related tests
|
||||
TESTGROUP["group1"]="-Dtest=org.keycloak.testsuite.forms.**,ClientAuthSignedJWTTest,CredentialsTest,JavaKeystoreKeyProviderTest,ServerInfoTest"
|
||||
|
||||
./mvnw clean install -nsu -B ${PARAMS["${{ matrix.server }}"]} ${TESTGROUP["${{ matrix.tests }}"]} -f testsuite/integration-arquillian/tests/base/pom.xml | misc/log/trimmer.sh
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import java.security.cert.CertificateException;
|
|||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.CollectionCertStoreParameters;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
@ -89,6 +90,21 @@ public interface CryptoProvider {
|
|||
|
||||
KeyStore getKeyStore(KeystoreFormat format) throws KeyStoreException, NoSuchProviderException;
|
||||
|
||||
/**
|
||||
* @return Keystore types/algorithms supported by this CryptoProvider
|
||||
*/
|
||||
default Stream<KeystoreFormat> getSupportedKeyStoreTypes() {
|
||||
return Stream.of(KeystoreFormat.values())
|
||||
.filter(format -> {
|
||||
try {
|
||||
getKeyStore(format);
|
||||
return true;
|
||||
} catch (KeyStoreException | NoSuchProviderException ex) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
CertificateFactory getX509CertFactory() throws CertificateException, NoSuchProviderException;
|
||||
|
||||
CertStore getCertStore(CollectionCertStoreParameters collectionCertStoreParameters) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException;
|
||||
|
|
|
@ -27,6 +27,8 @@ import java.security.KeyPair;
|
|||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -35,12 +37,24 @@ import java.security.PublicKey;
|
|||
public class KeystoreUtil {
|
||||
|
||||
public enum KeystoreFormat {
|
||||
JKS,
|
||||
PKCS12
|
||||
JKS("jks"),
|
||||
PKCS12("p12"),
|
||||
BCFKS("bcfks");
|
||||
|
||||
// Typical file extension for this keystore format
|
||||
private final String fileExtension;
|
||||
KeystoreFormat(String extension) {
|
||||
this.fileExtension = extension;
|
||||
}
|
||||
|
||||
public String getFileExtension() {
|
||||
return fileExtension;
|
||||
}
|
||||
}
|
||||
|
||||
public static KeyStore loadKeyStore(String filename, String password) throws Exception {
|
||||
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
String keystoreType = getKeystoreType(null, filename, KeyStore.getDefaultType());
|
||||
KeyStore trustStore = KeyStore.getInstance(keystoreType);
|
||||
InputStream trustStream = null;
|
||||
if (filename.startsWith(GenericConstants.PROTOCOL_CLASSPATH)) {
|
||||
String resourcePath = filename.replace(GenericConstants.PROTOCOL_CLASSPATH, "");
|
||||
|
@ -79,4 +93,31 @@ public class KeystoreUtil {
|
|||
throw new RuntimeException("Failed to load private key: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to return supported keystore type
|
||||
*
|
||||
* @param preferredType The preferred format - usually the one from the configuration. When present, it should be preferred over anything else
|
||||
* @param path Path of the file. We can try to detect keystore type from that (EG. my-keystore.pkcs12 will return "pkcs12") in case that preferredType is not defined
|
||||
* @param defaultType Default format as last fallback when none of the above can be used. Should be non-null
|
||||
* @return format as specified above
|
||||
*/
|
||||
public static String getKeystoreType(String preferredType, String path, String defaultType) {
|
||||
// Configured type has precedence
|
||||
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.getFileExtension().equals(ext))
|
||||
.findFirst();
|
||||
if (detectedType.isPresent()) return detectedType.get().toString();
|
||||
}
|
||||
|
||||
// Fallback to default
|
||||
return defaultType;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2022 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.keycloak.representations.info;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.keycloak.common.crypto.CryptoIntegration;
|
||||
import org.keycloak.common.crypto.CryptoProvider;
|
||||
import org.keycloak.common.util.KeystoreUtil;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class CryptoInfoRepresentation {
|
||||
|
||||
private String cryptoProvider;
|
||||
private List<String> supportedKeystoreTypes;
|
||||
|
||||
public static CryptoInfoRepresentation create() {
|
||||
CryptoInfoRepresentation info = new CryptoInfoRepresentation();
|
||||
|
||||
CryptoProvider cryptoProvider = CryptoIntegration.getProvider();
|
||||
info.cryptoProvider = cryptoProvider.getClass().getSimpleName();
|
||||
info.supportedKeystoreTypes = CryptoIntegration.getProvider().getSupportedKeyStoreTypes()
|
||||
.map(KeystoreUtil.KeystoreFormat::toString)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public String getCryptoProvider() {
|
||||
return cryptoProvider;
|
||||
}
|
||||
|
||||
public void setCryptoProvider(String cryptoProvider) {
|
||||
this.cryptoProvider = cryptoProvider;
|
||||
}
|
||||
|
||||
public List<String> getSupportedKeystoreTypes() {
|
||||
return supportedKeystoreTypes;
|
||||
}
|
||||
|
||||
public void setSupportedKeystoreTypes(List<String> supportedKeystoreTypes) {
|
||||
this.supportedKeystoreTypes = supportedKeystoreTypes;
|
||||
}
|
||||
}
|
|
@ -34,6 +34,8 @@ public class ServerInfoRepresentation {
|
|||
private MemoryInfoRepresentation memoryInfo;
|
||||
private ProfileInfoRepresentation profileInfo;
|
||||
|
||||
private CryptoInfoRepresentation cryptoInfo;
|
||||
|
||||
private Map<String, List<ThemeInfoRepresentation>> themes;
|
||||
|
||||
private List<Map<String, String>> socialProviders;
|
||||
|
@ -75,6 +77,14 @@ public class ServerInfoRepresentation {
|
|||
this.profileInfo = profileInfo;
|
||||
}
|
||||
|
||||
public CryptoInfoRepresentation getCryptoInfo() {
|
||||
return cryptoInfo;
|
||||
}
|
||||
|
||||
public void setCryptoInfo(CryptoInfoRepresentation cryptoInfo) {
|
||||
this.cryptoInfo = cryptoInfo;
|
||||
}
|
||||
|
||||
public Map<String, List<ThemeInfoRepresentation>> getThemes() {
|
||||
return themes;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package org.keycloak.crypto.def.test;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.common.crypto.CryptoIntegration;
|
||||
import org.keycloak.common.util.KeystoreUtil;
|
||||
import org.keycloak.rule.CryptoInitRule;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class DefaultKeyStoreTypesTest {
|
||||
|
||||
@ClassRule
|
||||
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
|
||||
|
||||
@Test
|
||||
public void testKeystoreFormats() {
|
||||
Set<KeystoreUtil.KeystoreFormat> supportedKeystoreFormats = CryptoIntegration.getProvider().getSupportedKeyStoreTypes().collect(Collectors.toSet());
|
||||
Assert.assertThat(supportedKeystoreFormats, Matchers.containsInAnyOrder(
|
||||
KeystoreUtil.KeystoreFormat.JKS,
|
||||
KeystoreUtil.KeystoreFormat.PKCS12,
|
||||
KeystoreUtil.KeystoreFormat.BCFKS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultKeystoreType() {
|
||||
Assert.assertEquals("PKCS12", KeystoreUtil.getKeystoreType("PKCS12", "some/foo.jks", "JKS"));
|
||||
Assert.assertEquals("PKCS12", KeystoreUtil.getKeystoreType("PKCS12", "some/foo.pkcs12", "JKS"));
|
||||
Assert.assertEquals("PKCS12", KeystoreUtil.getKeystoreType("PKCS12", "some/foo.bcfks", "JKS"));
|
||||
Assert.assertEquals("JKS", KeystoreUtil.getKeystoreType(null, "some/foo.jks", "JKS"));
|
||||
Assert.assertEquals("PKCS12", KeystoreUtil.getKeystoreType(null, "some/foo.p12", "JKS"));
|
||||
Assert.assertEquals("BCFKS", KeystoreUtil.getKeystoreType(null, "some/foo.bcfks", "JKS"));
|
||||
Assert.assertEquals("JKS", KeystoreUtil.getKeystoreType(null, "some/foo.bcfksl", "JKS"));
|
||||
}
|
||||
}
|
|
@ -66,6 +66,11 @@
|
|||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2022 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.keycloak.crypto.elytron.test;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.common.crypto.CryptoIntegration;
|
||||
import org.keycloak.common.util.KeystoreUtil;
|
||||
import org.keycloak.rule.CryptoInitRule;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ElytronKeyStoreTypesTest {
|
||||
|
||||
@ClassRule
|
||||
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
|
||||
|
||||
// No BCFKS keystore type supported for elytron
|
||||
@Test
|
||||
public void testKeystoreFormats() {
|
||||
Set<KeystoreUtil.KeystoreFormat> supportedKeystoreFormats = CryptoIntegration.getProvider().getSupportedKeyStoreTypes().collect(Collectors.toSet());
|
||||
Assert.assertThat(supportedKeystoreFormats, Matchers.containsInAnyOrder(
|
||||
KeystoreUtil.KeystoreFormat.JKS,
|
||||
KeystoreUtil.KeystoreFormat.PKCS12
|
||||
));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package org.keycloak.crypto.fips.test;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bouncycastle.crypto.CryptoServicesRegistrar;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.common.crypto.CryptoIntegration;
|
||||
import org.keycloak.common.util.Environment;
|
||||
import org.keycloak.common.util.KeystoreUtil;
|
||||
import org.keycloak.rule.CryptoInitRule;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class FIPS1402KeystoreTypesTest {
|
||||
|
||||
@ClassRule
|
||||
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
// Run this test just if java is in FIPS mode
|
||||
Assume.assumeTrue("Java is not in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeystoreFormatsInNonApprovedMode() {
|
||||
Assume.assumeFalse(CryptoServicesRegistrar.isInApprovedOnlyMode());
|
||||
Set<KeystoreUtil.KeystoreFormat> supportedKeystoreFormats = CryptoIntegration.getProvider().getSupportedKeyStoreTypes().collect(Collectors.toSet());
|
||||
Assert.assertThat(supportedKeystoreFormats, Matchers.containsInAnyOrder(
|
||||
KeystoreUtil.KeystoreFormat.PKCS12,
|
||||
KeystoreUtil.KeystoreFormat.BCFKS));
|
||||
}
|
||||
|
||||
// BCFIPS approved mode supports only BCFKS. No JKS nor PKCS12 support for keystores
|
||||
@Test
|
||||
public void testKeystoreFormatsInApprovedMode() {
|
||||
Assume.assumeTrue(CryptoServicesRegistrar.isInApprovedOnlyMode());
|
||||
Set<KeystoreUtil.KeystoreFormat> supportedKeystoreFormats = CryptoIntegration.getProvider().getSupportedKeyStoreTypes().collect(Collectors.toSet());
|
||||
Assert.assertThat(supportedKeystoreFormats, Matchers.containsInAnyOrder(
|
||||
KeystoreUtil.KeystoreFormat.BCFKS));
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ package org.keycloak.keys;
|
|||
|
||||
import org.keycloak.common.util.CertificateUtils;
|
||||
import org.keycloak.common.util.KeyUtils;
|
||||
import org.keycloak.common.util.KeystoreUtil;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.crypto.KeyUse;
|
||||
import org.keycloak.crypto.KeyWrapper;
|
||||
|
@ -61,8 +62,11 @@ public class JavaKeystoreKeyProvider extends AbstractRsaKeyProvider {
|
|||
|
||||
@Override
|
||||
protected KeyWrapper loadKey(RealmModel realm, ComponentModel model) {
|
||||
try (FileInputStream is = new FileInputStream(model.get(JavaKeystoreKeyProviderFactory.KEYSTORE_KEY))) {
|
||||
KeyStore keyStore = KeyStore.getInstance("JKS");
|
||||
String keystorePath = model.get(JavaKeystoreKeyProviderFactory.KEYSTORE_KEY);
|
||||
try (FileInputStream is = new FileInputStream(keystorePath)) {
|
||||
// Use "JKS" as default type for backwards compatibility
|
||||
String keystoreType = KeystoreUtil.getKeystoreType(model.get(JavaKeystoreKeyProviderFactory.KEYSTORE_TYPE_KEY), keystorePath, "JKS");
|
||||
KeyStore keyStore = KeyStore.getInstance(keystoreType);
|
||||
keyStore.load(is, model.get(JavaKeystoreKeyProviderFactory.KEYSTORE_PASSWORD_KEY).toCharArray());
|
||||
|
||||
String keyAlias = model.get(JavaKeystoreKeyProviderFactory.KEY_ALIAS_KEY);
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
package org.keycloak.keys;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.common.crypto.CryptoIntegration;
|
||||
import org.keycloak.common.util.KeystoreUtil;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.component.ComponentValidationException;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -27,6 +30,7 @@ import org.keycloak.provider.ProviderConfigProperty;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import static org.keycloak.provider.ProviderConfigProperty.LIST_TYPE;
|
||||
import static org.keycloak.provider.ProviderConfigProperty.STRING_TYPE;
|
||||
|
||||
/**
|
||||
|
@ -43,6 +47,11 @@ public class JavaKeystoreKeyProviderFactory extends AbstractRsaKeyProviderFactor
|
|||
public static String KEYSTORE_PASSWORD_KEY = "keystorePassword";
|
||||
public static ProviderConfigProperty KEYSTORE_PASSWORD_PROPERTY = new ProviderConfigProperty(KEYSTORE_PASSWORD_KEY, "Keystore Password", "Password for the keys", STRING_TYPE, null, true);
|
||||
|
||||
public static String KEYSTORE_TYPE_KEY = "keystoreType";
|
||||
|
||||
// Initialization of this property is postponed to "init()" due the CryptoProvider must be set
|
||||
private ProviderConfigProperty keystoreTypeProperty;
|
||||
|
||||
public static String KEY_ALIAS_KEY = "keyAlias";
|
||||
public static ProviderConfigProperty KEY_ALIAS_PROPERTY = new ProviderConfigProperty(KEY_ALIAS_KEY, "Key Alias", "Alias for the private key", STRING_TYPE, null);
|
||||
|
||||
|
@ -51,13 +60,26 @@ public class JavaKeystoreKeyProviderFactory extends AbstractRsaKeyProviderFactor
|
|||
|
||||
private static final String HELP_TEXT = "Loads keys from a Java keys file";
|
||||
|
||||
private static final List<ProviderConfigProperty> CONFIG_PROPERTIES = AbstractRsaKeyProviderFactory.configurationBuilder()
|
||||
.property(KEYSTORE_PROPERTY)
|
||||
.property(KEYSTORE_PASSWORD_PROPERTY)
|
||||
.property(KEY_ALIAS_PROPERTY)
|
||||
.property(KEY_PASSWORD_PROPERTY)
|
||||
.property(Attributes.KEY_USE_PROPERTY)
|
||||
.build();
|
||||
private List<ProviderConfigProperty> configProperties;
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
String[] supportedKeystoreTypes = CryptoIntegration.getProvider().getSupportedKeyStoreTypes()
|
||||
.map(KeystoreUtil.KeystoreFormat::toString)
|
||||
.toArray(String[]::new);
|
||||
this.keystoreTypeProperty = new ProviderConfigProperty(KEYSTORE_TYPE_KEY, "Keystore Type",
|
||||
"Keystore type. This parameter is not mandatory. If omitted, the type will be detected from keystore file or default keystore type will be used", LIST_TYPE,
|
||||
supportedKeystoreTypes.length > 0 ? supportedKeystoreTypes[0] : null, supportedKeystoreTypes);
|
||||
|
||||
configProperties = AbstractRsaKeyProviderFactory.configurationBuilder()
|
||||
.property(KEYSTORE_PROPERTY)
|
||||
.property(KEYSTORE_PASSWORD_PROPERTY)
|
||||
.property(keystoreTypeProperty)
|
||||
.property(KEY_ALIAS_PROPERTY)
|
||||
.property(KEY_PASSWORD_PROPERTY)
|
||||
.property(Attributes.KEY_USE_PROPERTY)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyProvider create(KeycloakSession session, ComponentModel model) {
|
||||
|
@ -71,6 +93,7 @@ public class JavaKeystoreKeyProviderFactory extends AbstractRsaKeyProviderFactor
|
|||
ConfigurationValidationHelper.check(model)
|
||||
.checkSingle(KEYSTORE_PROPERTY, true)
|
||||
.checkSingle(KEYSTORE_PASSWORD_PROPERTY, true)
|
||||
.checkSingle(keystoreTypeProperty, false)
|
||||
.checkSingle(KEY_ALIAS_PROPERTY, true)
|
||||
.checkSingle(KEY_PASSWORD_PROPERTY, true);
|
||||
|
||||
|
@ -89,7 +112,7 @@ public class JavaKeystoreKeyProviderFactory extends AbstractRsaKeyProviderFactor
|
|||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return CONFIG_PROPERTIES;
|
||||
return this.configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -63,6 +63,8 @@ import java.security.cert.Certificate;
|
|||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @resource Client Attribute Certificate
|
||||
|
@ -268,9 +270,7 @@ public class ClientAttributeCertificateResource {
|
|||
public byte[] getKeystore(final KeyStoreConfig config) {
|
||||
auth.clients().requireView(client);
|
||||
|
||||
if (config.getFormat() != null && !config.getFormat().equals("JKS") && !config.getFormat().equals("PKCS12")) {
|
||||
throw new NotAcceptableException("Only support jks or pkcs12 format.");
|
||||
}
|
||||
checkKeystoreFormat(config);
|
||||
|
||||
CertificateRepresentation info = CertificateInfoHelper.getCertificateFromClient(client, attributePrefix);
|
||||
String privatePem = info.getPrivateKey();
|
||||
|
@ -307,9 +307,7 @@ public class ClientAttributeCertificateResource {
|
|||
public byte[] generateAndGetKeystore(final KeyStoreConfig config) {
|
||||
auth.clients().requireConfigure(client);
|
||||
|
||||
if (config.getFormat() != null && !config.getFormat().equals("JKS") && !config.getFormat().equals("PKCS12")) {
|
||||
throw new NotAcceptableException("Only support jks or pkcs12 format.");
|
||||
}
|
||||
checkKeystoreFormat(config);
|
||||
if (config.getKeyPassword() == null) {
|
||||
throw new ErrorResponseException("password-missing", "Need to specify a key password for jks generation and download", Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
@ -369,5 +367,20 @@ public class ClientAttributeCertificateResource {
|
|||
}
|
||||
}
|
||||
|
||||
private void checkKeystoreFormat(KeyStoreConfig config) throws NotAcceptableException {
|
||||
if (config.getFormat() != null) {
|
||||
Set<KeystoreFormat> supportedKeystoreFormats = CryptoIntegration.getProvider().getSupportedKeyStoreTypes()
|
||||
.collect(Collectors.toSet());
|
||||
try {
|
||||
KeystoreFormat format = Enum.valueOf(KeystoreFormat.class, config.getFormat().toUpperCase());
|
||||
if (config.getFormat() != null && !supportedKeystoreFormats.contains(format)) {
|
||||
throw new NotAcceptableException("Not supported keystore format. Supported keystore formats: " + supportedKeystoreFormats);
|
||||
}
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new NotAcceptableException("Not supported keystore format. Supported keystore formats: " + supportedKeystoreFormats);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ import org.keycloak.representations.idm.PasswordPolicyTypeRepresentation;
|
|||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||
import org.keycloak.representations.idm.ProtocolMapperTypeRepresentation;
|
||||
import org.keycloak.representations.info.ClientInstallationRepresentation;
|
||||
import org.keycloak.representations.info.CryptoInfoRepresentation;
|
||||
import org.keycloak.representations.info.MemoryInfoRepresentation;
|
||||
import org.keycloak.representations.info.ProfileInfoRepresentation;
|
||||
import org.keycloak.representations.info.ProviderRepresentation;
|
||||
|
@ -93,6 +94,7 @@ public class ServerInfoAdminResource {
|
|||
info.setSystemInfo(SystemInfoRepresentation.create(session.getKeycloakSessionFactory().getServerStartupTimestamp()));
|
||||
info.setMemoryInfo(MemoryInfoRepresentation.create());
|
||||
info.setProfileInfo(ProfileInfoRepresentation.create());
|
||||
info.setCryptoInfo(CryptoInfoRepresentation.create());
|
||||
|
||||
setSocialProviders(info);
|
||||
setIdentityProviders(info);
|
||||
|
|
|
@ -45,7 +45,6 @@ import java.util.Enumeration;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
|
@ -86,7 +85,7 @@ public class FileTruststoreProviderFactory implements TruststoreProviderFactory
|
|||
throw new RuntimeException("Attribute 'password' missing in 'truststore':'file' configuration");
|
||||
}
|
||||
|
||||
String type = getTruststoreType(storepath, configuredType);
|
||||
String type = KeystoreUtil.getKeystoreType(configuredType, storepath, KeyStore.getDefaultType());
|
||||
try {
|
||||
truststore = loadStore(storepath, type, pass == null ? null :pass.toCharArray());
|
||||
} catch (Exception e) {
|
||||
|
@ -159,25 +158,6 @@ public class FileTruststoreProviderFactory implements TruststoreProviderFactory
|
|||
.build();
|
||||
}
|
||||
|
||||
private String getTruststoreType(String path, String configuredType) {
|
||||
// Configured type has precedence
|
||||
if (configuredType != null) return configuredType;
|
||||
|
||||
// Fallback to detected tyoe from the file format (EG. my-keystore.pkcs12 will return "pkcs12")
|
||||
int lastDotIndex = path.lastIndexOf('.');
|
||||
if (lastDotIndex > -1) {
|
||||
String ext = path.substring(lastDotIndex).toUpperCase();
|
||||
Optional<String> detectedType = Arrays.stream(KeystoreUtil.KeystoreFormat.values())
|
||||
.map(KeystoreUtil.KeystoreFormat::toString)
|
||||
.filter(ksFormat -> ksFormat.equals(ext))
|
||||
.findFirst();
|
||||
if (detectedType.isPresent()) return detectedType.get();
|
||||
}
|
||||
|
||||
// Fallback to default JVM
|
||||
return KeyStore.getDefaultType();
|
||||
}
|
||||
|
||||
private static class TruststoreCertificatesLoader {
|
||||
|
||||
private Map<X500Principal, X509Certificate> trustedRootCerts = new HashMap<>();
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2022 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.keycloak.testsuite.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.Assume;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.keycloak.common.crypto.CryptoIntegration;
|
||||
import org.keycloak.common.util.CertificateUtils;
|
||||
import org.keycloak.common.util.KeyUtils;
|
||||
import org.keycloak.common.util.KeystoreUtil;
|
||||
import org.keycloak.common.util.PemUtils;
|
||||
import org.keycloak.representations.idm.CertificateRepresentation;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class KeystoreUtils {
|
||||
|
||||
public static String[] getSupportedKeystoreTypes() {
|
||||
String supportedKeystoreTypes = System.getProperty("auth.server.supported.keystore.types");
|
||||
if (supportedKeystoreTypes == null || supportedKeystoreTypes.trim().isEmpty()) {
|
||||
fail("Property 'auth.server.supported.keystore.types' not set");
|
||||
}
|
||||
return supportedKeystoreTypes.split(",");
|
||||
}
|
||||
|
||||
public static KeystoreUtil.KeystoreFormat getPreferredKeystoreType() {
|
||||
return Enum.valueOf(KeystoreUtil.KeystoreFormat.class, getSupportedKeystoreTypes()[0]);
|
||||
}
|
||||
|
||||
public static void assumeKeystoreTypeSupported(KeystoreUtil.KeystoreFormat keystoreType) {
|
||||
String[] supportedKeystoreTypes = KeystoreUtils.getSupportedKeystoreTypes();
|
||||
Assume.assumeTrue("Keystore type '" + keystoreType + "' not supported. Supported keystore types: " + Arrays.asList(supportedKeystoreTypes),
|
||||
Stream.of(supportedKeystoreTypes)
|
||||
.anyMatch(type -> type.equals(keystoreType.toString())));
|
||||
}
|
||||
|
||||
public static KeystoreInfo generateKeystore(TemporaryFolder folder, KeystoreUtil.KeystoreFormat keystoreType, String subject, String keystorePassword, String keyPassword) throws Exception {
|
||||
String fileName = "keystore." + keystoreType.getFileExtension();
|
||||
|
||||
KeyPair keyPair = KeyUtils.generateRsaKeyPair(2048);
|
||||
X509Certificate certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, subject);
|
||||
|
||||
KeyStore keyStore = CryptoIntegration.getProvider().getKeyStore(keystoreType);
|
||||
keyStore.load(null, null);
|
||||
|
||||
Certificate[] chain = {certificate};
|
||||
keyStore.setKeyEntry(subject, keyPair.getPrivate(), keyPassword.trim().toCharArray(), chain);
|
||||
|
||||
File file = folder.newFile(fileName);
|
||||
keyStore.store(new FileOutputStream(file), keystorePassword.trim().toCharArray());
|
||||
|
||||
CertificateRepresentation certRep = new CertificateRepresentation();
|
||||
certRep.setPrivateKey(PemUtils.encodeKey(keyPair.getPrivate()));
|
||||
certRep.setPublicKey(PemUtils.encodeKey(keyPair.getPublic()));
|
||||
certRep.setCertificate(PemUtils.encodeCertificate(certificate));
|
||||
return new KeystoreInfo(certRep, file);
|
||||
}
|
||||
|
||||
public static class KeystoreInfo {
|
||||
private final CertificateRepresentation certificateInfo;
|
||||
private final File keystoreFile;
|
||||
|
||||
private KeystoreInfo(CertificateRepresentation certificateInfo, File keystoreFile) {
|
||||
this.certificateInfo = certificateInfo;
|
||||
this.keystoreFile = keystoreFile;
|
||||
}
|
||||
|
||||
public CertificateRepresentation getCertificateInfo() {
|
||||
return certificateInfo;
|
||||
}
|
||||
|
||||
public File getKeystoreFile() {
|
||||
return keystoreFile;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,12 +23,14 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
|||
import org.keycloak.representations.info.ProviderRepresentation;
|
||||
import org.keycloak.representations.info.ServerInfoRepresentation;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -56,6 +58,12 @@ public class ServerInfoTest extends AbstractKeycloakTest {
|
|||
|
||||
assertNotNull(info.getMemoryInfo());
|
||||
assertNotNull(info.getSystemInfo());
|
||||
assertNotNull(info.getCryptoInfo());
|
||||
String expectedSupportedKeystoreTypes = System.getProperty("auth.server.supported.keystore.types");
|
||||
if (expectedSupportedKeystoreTypes == null) {
|
||||
fail("Property 'auth.server.supported.keystore.types' not set");
|
||||
}
|
||||
Assert.assertNames(info.getCryptoInfo().getSupportedKeystoreTypes(), expectedSupportedKeystoreTypes.split(","));
|
||||
|
||||
assertEquals(Version.VERSION, info.getSystemInfo().getVersion());
|
||||
assertNotNull(info.getSystemInfo().getServerTime());
|
||||
|
|
|
@ -20,8 +20,11 @@ package org.keycloak.testsuite.admin.client;
|
|||
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.keycloak.admin.client.resource.ClientAttributeCertificateResource;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.common.crypto.CryptoIntegration;
|
||||
import org.keycloak.common.util.KeystoreUtil;
|
||||
import org.keycloak.common.util.PemUtils;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
|
@ -31,13 +34,12 @@ import org.keycloak.representations.idm.CertificateRepresentation;
|
|||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.testsuite.util.AdminEventPaths;
|
||||
import org.keycloak.testsuite.util.KeystoreUtils;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URL;
|
||||
import java.io.FileInputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.Key;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
|
@ -108,81 +110,90 @@ public class CredentialsTest extends AbstractClientTest {
|
|||
|
||||
@Test
|
||||
public void testUploadKeyAndCertificate() throws Exception {
|
||||
String privateKey = "MIIEowIBAAKCAQEAhSOOC/5Xez3o75lr3TTYun+2u0a4cF5p5Uv10UowrM7Yw+p1GYcHg+o2UN13bxHB/lefqJZ0WnJQo6cj/JcMuF1y4WlHSww0r8L0u36FKk8Uu7MOqC0+AOi2UzGIchYM5nuD3+A9g1ds2+O/ydKLKqiC6gJCKJp9b3Rs8eyJUt0/tkhTAJx+LWpCbsWHFEnU2Jbl29SS4KedYR/RdH5bNzl4L0SAHS1osWI+xIQiVYybnGVqFjJeQ9006pmOJGetNablji6TxlywP8ps9N//u3txBeKlVqzCCN1iLWQrb/NHA6GDVDBYVf+qa91358vFXRHpWpEOGftB6nZzHAzEuwIDAQABAoIBAHb5IsJM8lfLJxCVBPKTeuiNn/kSZVbkx7SDgJMZvQ1vefz40tOQ+oJDFW6FuWijcbubCa1ZZXg9lxnnDh11zYQi3bnYnkDOE3bMvG2fzdfU+y4QABUA+NtPGT6WkNuCIN0Fmv7AH7fys/B7QLNVVc807me2xPALvfOPEpvNR5mnjquCTOfDzbh5U6hGFcuLnZdQbCK2hG5R8DXE2pLvoa0i1cMMgVaWQ5mVSg0N3G0Q5ZF8YJEasAeJUCGlPFgJ4ySfGsKSUcMODQzHmqvLzArJmFgW6Uah0CgqedBTujmzJ6FwfbzGR0wpk08cf5BPzs9Flwka10ITA4h4QzlBnuECgYEA8TFZWq42biHaZmo0NVVEoltIDl1ci5m2xr7yU6TMfrsGKFFiszCPWuKcK5J8Svm0P9H9vlVpCHZ+JVfEGnve1/wVB/6E0lY6cz4uJTV4t4F1QJN5j9nyRrS0i9zDEIRgO4mvD9Zlm/OvHEdTmtVg97cbS4nWvRAPdB2DaZ0w0V8CgYEAjVAAb5Q6Jqb5XT5ZM1Cc6S3PzBAA7GGc3Rqyugxts5WEReRXdNITocj/71c0VZ+qC9+EvV8im/7QPl5NbRiI2p3oPqqV5Brk/MVfDLhu/mkawW0mlPtuBkZIRE0/eXTGN9Dq6yvxo9d6kwka7RW1CBZxi1/M78hKGCHXM7umviUCgYEA4cLvgJHRIQVPCM4gUEugEtieedOp7IHVM/NHoEOBpp4pBVQortGlXcz/oUlcTlGtBo/ok2AfEGzZZtrgFGoeDM1IYlM6wCc2TujFCM8kT6A9wFRKVPwMa2J6HPBnJe7CpPgbhReJxJA0OKQK/cL9IOGkCvDar914mZeGijU4nMECgYAqZL7Muo47fEpBE+xUvbFlLu4xDPgJ8jrKBjFqKUJb5tYY1aj7De7/0Toexm2X5l9wUm0TFtBeNjKpE0dtHDgqRccfzbNMDFl4D4o1WbtKraNuNd2mQku+rCUQAJCzUjoJEq73QGasvX8zTz75s1JtC7ailmn34YGA/d3+0iPy1QKBgHXneWpJVcQ9Lk34DnSLZLK+W1sTK8xLTJSyy3U0F84r+ir8bvsP9EQpZI0Nx3DqvF4/ZHmK2cfSxGSKm4VhZfG0LYCqtSmaHErZJaLJA8xJELkkEKj/ZUqkZ+4zhY7RMwyZtmXcxvaR/pzRZZwbTQ4ueZKKUIsK2AaHTsSCGDMq";
|
||||
String certificate = "MIICnTCCAYUCBgFPPLDaTzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdjbGllbnQxMB4XDTE1MDgxNzE3MjI0N1oXDTI1MDgxNzE3MjQyN1owEjEQMA4GA1UEAwwHY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIUjjgv+V3s96O+Za9002Lp/trtGuHBeaeVL9dFKMKzO2MPqdRmHB4PqNlDdd28Rwf5Xn6iWdFpyUKOnI/yXDLhdcuFpR0sMNK/C9Lt+hSpPFLuzDqgtPgDotlMxiHIWDOZ7g9/gPYNXbNvjv8nSiyqoguoCQiiafW90bPHsiVLdP7ZIUwCcfi1qQm7FhxRJ1NiW5dvUkuCnnWEf0XR+Wzc5eC9EgB0taLFiPsSEIlWMm5xlahYyXkPdNOqZjiRnrTWm5Y4uk8ZcsD/KbPTf/7t7cQXipVaswgjdYi1kK2/zRwOhg1QwWFX/qmvdd+fLxV0R6VqRDhn7Qep2cxwMxLsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAKE6OA46sf20bz8LZPoiNsqRwBUDkaMGXfnob7s/hJZIIwDEx0IAQ3uKsG7q9wb+aA6s+v7S340zb2k3IxuhFaHaZpAd4CyR5cn1FHylbzoZ7rI/3ASqHDqpljdJaFqPH+m7nZWtyDvtZf+gkZ8OjsndwsSBK1d/jMZPp29qYbl1+XfO7RCp/jDqro/R3saYFaIFiEZPeKn1hUJn6BO48vxH1xspSu9FmlvDOEAOz4AuM58z4zRMP49GcFdCWr1wkonJUHaSptJaQwmBwLFUkCbE5I1ixGMb7mjEud6Y5jhfzJiZMo2U8RfcjNbrN0diZl3jB6LQIwESnhYSghaTjNQ==";
|
||||
String certificate2 = "MIICnTCCAYUCBgFPPQDGxTANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdjbGllbnQxMB4XDTE1MDgxNzE4NTAwNVoXDTI1MDgxNzE4NTE0NVowEjEQMA4GA1UEAwwHY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMMw3PaBffWxgS2PYSDDBp6As+cNvv9kt2C4f/RDAGmvSIHPFev9kuQiKs3Oaws3ZsV4JG3qHEuYgnh9W4vfe3DwNwtD1bjL5FYBhPBFTw0lAQECYxaBHnkjHwUKp957FqdSPPICm3LjmTcEdlH+9dpp9xHCMbbiNiWDzWI1xSxC8Fs2d0hwz1sd+Q4QeTBPIBWcPM+ICZtNG5MN+ORfayu4X+Me5d0tXG2fQO//rAevk1i5IFjKZuOjTwyKB5SJIY4b8QTeg0g/50IU7Ht00Pxw6CK02dHS+FvXHasZlD3ckomqCDjStTBWdhJo5dST0CbOqalkkpLlCCbGA1yEQRsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAUIMeJ+EAo8eNpCG/nXImacjrKakbFnZYBGD/gqeTGaZynkX+jgBSructTHR83zSH+yELEhsAy+3BfK4EEihp+PEcRnK2fASVkHste8AQ7rlzC+HGGirlwrVhWCdizNUCGK80DE537IZ7nmZw6LFG9P5/Q2MvCsOCYjRUvMkukq6TdXBXR9tETwZ+0gpSfsOxjj0ZF7ftTRUSzx4rFfcbM9fRNdVizdOuKGc8HJPA5lLOxV6CyaYIvi3y5RlQI1OHeS34lE4w9CNPRFa/vdxXvN7ClyzA0HMFNWxBN7pC/Ht/FbhSvaAagJBHg+vCrcY5C26Oli7lAglf/zZrwUPs0w==";
|
||||
TemporaryFolder folder = new TemporaryFolder();
|
||||
folder.create();
|
||||
try {
|
||||
String certificate2 = "MIICnTCCAYUCBgFPPQDGxTANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdjbGllbnQxMB4XDTE1MDgxNzE4NTAwNVoXDTI1MDgxNzE4NTE0NVowEjEQMA4GA1UEAwwHY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMMw3PaBffWxgS2PYSDDBp6As+cNvv9kt2C4f/RDAGmvSIHPFev9kuQiKs3Oaws3ZsV4JG3qHEuYgnh9W4vfe3DwNwtD1bjL5FYBhPBFTw0lAQECYxaBHnkjHwUKp957FqdSPPICm3LjmTcEdlH+9dpp9xHCMbbiNiWDzWI1xSxC8Fs2d0hwz1sd+Q4QeTBPIBWcPM+ICZtNG5MN+ORfayu4X+Me5d0tXG2fQO//rAevk1i5IFjKZuOjTwyKB5SJIY4b8QTeg0g/50IU7Ht00Pxw6CK02dHS+FvXHasZlD3ckomqCDjStTBWdhJo5dST0CbOqalkkpLlCCbGA1yEQRsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAUIMeJ+EAo8eNpCG/nXImacjrKakbFnZYBGD/gqeTGaZynkX+jgBSructTHR83zSH+yELEhsAy+3BfK4EEihp+PEcRnK2fASVkHste8AQ7rlzC+HGGirlwrVhWCdizNUCGK80DE537IZ7nmZw6LFG9P5/Q2MvCsOCYjRUvMkukq6TdXBXR9tETwZ+0gpSfsOxjj0ZF7ftTRUSzx4rFfcbM9fRNdVizdOuKGc8HJPA5lLOxV6CyaYIvi3y5RlQI1OHeS34lE4w9CNPRFa/vdxXvN7ClyzA0HMFNWxBN7pC/Ht/FbhSvaAagJBHg+vCrcY5C26Oli7lAglf/zZrwUPs0w==";
|
||||
|
||||
ClientAttributeCertificateResource certRsc = accountClient.getCertficateResource("jwt.credential");
|
||||
ClientAttributeCertificateResource certRsc = accountClient.getCertficateResource("jwt.credential");
|
||||
|
||||
// Upload privateKey and certificate as JKS store
|
||||
MultipartFormDataOutput keyCertForm = new MultipartFormDataOutput();
|
||||
keyCertForm.addFormData("keystoreFormat", "JKS", MediaType.TEXT_PLAIN_TYPE);
|
||||
keyCertForm.addFormData("keyAlias", "clientkey", MediaType.TEXT_PLAIN_TYPE);
|
||||
keyCertForm.addFormData("keyPassword", "keypass", MediaType.TEXT_PLAIN_TYPE);
|
||||
keyCertForm.addFormData("storePassword", "storepass", MediaType.TEXT_PLAIN_TYPE);
|
||||
KeystoreUtil.KeystoreFormat preferredKeystoreType = KeystoreUtils.getPreferredKeystoreType();
|
||||
|
||||
URL idpMeta = getClass().getClassLoader().getResource("client-auth-test/keystore-client1.jks");
|
||||
byte [] content = Files.readAllBytes(Paths.get(idpMeta.toURI()));
|
||||
keyCertForm.addFormData("file", content, MediaType.APPLICATION_OCTET_STREAM_TYPE);
|
||||
CertificateRepresentation cert = certRsc.uploadJks(keyCertForm);
|
||||
// Generate keystore file and upload privateKey and certificate from it as JKS store (or eventually PKCS12 or BCFKS store according to which one is preferred type)
|
||||
KeystoreUtils.KeystoreInfo generatedKeystore = KeystoreUtils.generateKeystore(folder, preferredKeystoreType, "clientkey", "storepass", "keypass");
|
||||
MultipartFormDataOutput keyCertForm = new MultipartFormDataOutput();
|
||||
|
||||
// Returned cert is not the new state but rather what was extracted from inputs
|
||||
assertNotNull("cert not null", cert);
|
||||
assertEquals("cert properly extracted", certificate, cert.getCertificate());
|
||||
assertEquals("privateKey properly extracted", privateKey, cert.getPrivateKey());
|
||||
keyCertForm.addFormData("keystoreFormat", preferredKeystoreType.toString(), MediaType.TEXT_PLAIN_TYPE);
|
||||
keyCertForm.addFormData("keyAlias", "clientkey", MediaType.TEXT_PLAIN_TYPE);
|
||||
keyCertForm.addFormData("keyPassword", "keypass", MediaType.TEXT_PLAIN_TYPE);
|
||||
keyCertForm.addFormData("storePassword", "storepass", MediaType.TEXT_PLAIN_TYPE);
|
||||
|
||||
// Get the certificate - to make sure cert was properly updated
|
||||
cert = certRsc.getKeyInfo();
|
||||
assertEquals("cert properly set", certificate, cert.getCertificate());
|
||||
assertEquals("privateKey properly set", privateKey, cert.getPrivateKey());
|
||||
FileInputStream fs = new FileInputStream(generatedKeystore.getKeystoreFile());
|
||||
byte [] content = fs.readAllBytes();
|
||||
fs.close();
|
||||
keyCertForm.addFormData("file", content, MediaType.APPLICATION_OCTET_STREAM_TYPE);
|
||||
CertificateRepresentation cert = certRsc.uploadJks(keyCertForm);
|
||||
|
||||
// Upload a different certificate via /upload-certificate, privateKey should be nullified
|
||||
MultipartFormDataOutput form = new MultipartFormDataOutput();
|
||||
form.addFormData("keystoreFormat", "Certificate PEM", MediaType.TEXT_PLAIN_TYPE);
|
||||
form.addFormData("file", certificate2.getBytes(Charset.forName("ASCII")), MediaType.APPLICATION_OCTET_STREAM_TYPE);
|
||||
cert = certRsc.uploadJksCertificate(form);
|
||||
assertNotNull("cert not null", cert);
|
||||
assertEquals("cert properly extracted", certificate2, cert.getCertificate());
|
||||
assertNull("privateKey not included", cert.getPrivateKey());
|
||||
// Returned cert is not the new state but rather what was extracted from inputs
|
||||
assertNotNull("cert not null", cert);
|
||||
assertEquals("cert properly extracted", generatedKeystore.getCertificateInfo().getCertificate(), cert.getCertificate());
|
||||
assertEquals("privateKey properly extracted", generatedKeystore.getCertificateInfo().getPrivateKey(), cert.getPrivateKey());
|
||||
|
||||
// Get the certificate - to make sure cert was properly updated, and privateKey is null
|
||||
cert = certRsc.getKeyInfo();
|
||||
assertEquals("cert properly set", certificate2, cert.getCertificate());
|
||||
assertNull("privateKey nullified", cert.getPrivateKey());
|
||||
// Get the certificate - to make sure cert was properly updated
|
||||
cert = certRsc.getKeyInfo();
|
||||
assertEquals("cert properly set", generatedKeystore.getCertificateInfo().getCertificate(), cert.getCertificate());
|
||||
assertEquals("privateKey properly set", generatedKeystore.getCertificateInfo().getPrivateKey(), cert.getPrivateKey());
|
||||
|
||||
// Re-upload the private key
|
||||
certRsc.uploadJks(keyCertForm);
|
||||
// Upload a different certificate via /upload-certificate, privateKey should be nullified
|
||||
MultipartFormDataOutput form = new MultipartFormDataOutput();
|
||||
form.addFormData("keystoreFormat", "Certificate PEM", MediaType.TEXT_PLAIN_TYPE);
|
||||
form.addFormData("file", certificate2.getBytes(Charset.forName("ASCII")), MediaType.APPLICATION_OCTET_STREAM_TYPE);
|
||||
cert = certRsc.uploadJksCertificate(form);
|
||||
assertNotNull("cert not null", cert);
|
||||
assertEquals("cert properly extracted", certificate2, cert.getCertificate());
|
||||
assertNull("privateKey not included", cert.getPrivateKey());
|
||||
|
||||
// Upload certificate as PEM via /upload - nullifies the private key
|
||||
form = new MultipartFormDataOutput();
|
||||
form.addFormData("keystoreFormat", "Certificate PEM", MediaType.TEXT_PLAIN_TYPE);
|
||||
form.addFormData("file", certificate2.getBytes(Charset.forName("ASCII")), MediaType.APPLICATION_OCTET_STREAM_TYPE);
|
||||
cert = certRsc.uploadJks(form);
|
||||
assertNotNull("cert not null", cert);
|
||||
assertEquals("cert properly extracted", certificate2, cert.getCertificate());
|
||||
assertNull("privateKey not included", cert.getPrivateKey());
|
||||
// Get the certificate - to make sure cert was properly updated, and privateKey is null
|
||||
cert = certRsc.getKeyInfo();
|
||||
assertEquals("cert properly set", certificate2, cert.getCertificate());
|
||||
assertNull("privateKey nullified", cert.getPrivateKey());
|
||||
|
||||
// Get the certificate again - to make sure cert is set, and privateKey is null
|
||||
cert = certRsc.getKeyInfo();
|
||||
assertEquals("cert properly set", certificate2, cert.getCertificate());
|
||||
assertNull("privateKey nullified", cert.getPrivateKey());
|
||||
// Re-upload the private key
|
||||
certRsc.uploadJks(keyCertForm);
|
||||
|
||||
// Upload certificate with header - should be stored without header
|
||||
form = new MultipartFormDataOutput();
|
||||
form.addFormData("keystoreFormat", "Certificate PEM", MediaType.TEXT_PLAIN_TYPE);
|
||||
// Upload certificate as PEM via /upload - nullifies the private key
|
||||
form = new MultipartFormDataOutput();
|
||||
form.addFormData("keystoreFormat", "Certificate PEM", MediaType.TEXT_PLAIN_TYPE);
|
||||
form.addFormData("file", certificate2.getBytes(Charset.forName("ASCII")), MediaType.APPLICATION_OCTET_STREAM_TYPE);
|
||||
cert = certRsc.uploadJks(form);
|
||||
assertNotNull("cert not null", cert);
|
||||
assertEquals("cert properly extracted", certificate2, cert.getCertificate());
|
||||
assertNull("privateKey not included", cert.getPrivateKey());
|
||||
|
||||
String certificate2WithHeaders = PemUtils.BEGIN_CERT + "\n" + certificate2 + "\n" + PemUtils.END_CERT;
|
||||
// Get the certificate again - to make sure cert is set, and privateKey is null
|
||||
cert = certRsc.getKeyInfo();
|
||||
assertEquals("cert properly set", certificate2, cert.getCertificate());
|
||||
assertNull("privateKey nullified", cert.getPrivateKey());
|
||||
|
||||
form.addFormData("file", certificate2WithHeaders.getBytes(Charset.forName("ASCII")), MediaType.APPLICATION_OCTET_STREAM_TYPE);
|
||||
cert = certRsc.uploadJks(form);
|
||||
assertNotNull("cert not null", cert);
|
||||
assertEquals("cert properly extracted", certificate2, cert.getCertificate());
|
||||
assertNull("privateKey not included", cert.getPrivateKey());
|
||||
// Upload certificate with header - should be stored without header
|
||||
form = new MultipartFormDataOutput();
|
||||
form.addFormData("keystoreFormat", "Certificate PEM", MediaType.TEXT_PLAIN_TYPE);
|
||||
|
||||
// Get the certificate again - to make sure cert is set, and privateKey is null
|
||||
cert = certRsc.getKeyInfo();
|
||||
assertEquals("cert properly set", certificate2, cert.getCertificate());
|
||||
assertNull("privateKey nullified", cert.getPrivateKey());
|
||||
String certificate2WithHeaders = PemUtils.BEGIN_CERT + "\n" + certificate2 + "\n" + PemUtils.END_CERT;
|
||||
|
||||
form.addFormData("file", certificate2WithHeaders.getBytes(Charset.forName("ASCII")), MediaType.APPLICATION_OCTET_STREAM_TYPE);
|
||||
cert = certRsc.uploadJks(form);
|
||||
assertNotNull("cert not null", cert);
|
||||
assertEquals("cert properly extracted", certificate2, cert.getCertificate());
|
||||
assertNull("privateKey not included", cert.getPrivateKey());
|
||||
|
||||
// Get the certificate again - to make sure cert is set, and privateKey is null
|
||||
cert = certRsc.getKeyInfo();
|
||||
assertEquals("cert properly set", certificate2, cert.getCertificate());
|
||||
assertNull("privateKey nullified", cert.getPrivateKey());
|
||||
} finally {
|
||||
folder.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -192,15 +203,17 @@ public class CredentialsTest extends AbstractClientTest {
|
|||
// generate a key pair first
|
||||
CertificateRepresentation certrep = certRsc.generate();
|
||||
|
||||
KeystoreUtil.KeystoreFormat preferredKeystoreType = KeystoreUtils.getPreferredKeystoreType();
|
||||
|
||||
// download the key and certificate
|
||||
KeyStoreConfig config = new KeyStoreConfig();
|
||||
config.setFormat("JKS");
|
||||
config.setFormat(preferredKeystoreType.toString());
|
||||
config.setKeyAlias("alias");
|
||||
config.setKeyPassword("keyPass");
|
||||
config.setStorePassword("storePass");
|
||||
byte[] result = certRsc.getKeystore(config);
|
||||
|
||||
KeyStore keyStore = KeyStore.getInstance("JKS");
|
||||
KeyStore keyStore = CryptoIntegration.getProvider().getKeyStore(preferredKeystoreType);
|
||||
keyStore.load(new ByteArrayInputStream(result), "storePass".toCharArray());
|
||||
Key key = keyStore.getKey("alias", "keyPass".toCharArray());
|
||||
Certificate cert = keyStore.getCertificate("alias");
|
||||
|
@ -220,13 +233,15 @@ public class CredentialsTest extends AbstractClientTest {
|
|||
// generate a key pair first
|
||||
CertificateRepresentation firstcert = certRsc.generate();
|
||||
|
||||
KeystoreUtil.KeystoreFormat preferredKeystoreType = KeystoreUtils.getPreferredKeystoreType();
|
||||
|
||||
KeyStoreConfig config = new KeyStoreConfig();
|
||||
config.setFormat("JKS");
|
||||
config.setFormat(preferredKeystoreType.toString());
|
||||
config.setKeyAlias("alias");
|
||||
config.setKeyPassword("keyPass");
|
||||
config.setStorePassword("storePass");
|
||||
byte[] result = certRsc.generateAndGetKeystore(config);
|
||||
KeyStore keyStore = KeyStore.getInstance("JKS");
|
||||
KeyStore keyStore = CryptoIntegration.getProvider().getKeyStore(preferredKeystoreType);
|
||||
keyStore.load(new ByteArrayInputStream(result), "storePass".toCharArray());
|
||||
Key key = keyStore.getKey("alias", "keyPass".toCharArray());
|
||||
Certificate cert = keyStore.getCertificate("alias");
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
|
||||
package org.keycloak.testsuite.keys;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.keycloak.common.util.KeystoreUtil;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.jose.jws.AlgorithmType;
|
||||
import org.keycloak.keys.JavaKeystoreKeyProviderFactory;
|
||||
|
@ -31,18 +31,20 @@ import org.keycloak.representations.idm.ErrorRepresentation;
|
|||
import org.keycloak.representations.idm.KeysMetadataRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.util.KeystoreUtils;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static io.smallrye.common.constraint.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.keycloak.common.util.KeystoreUtil.KeystoreFormat.PKCS12;
|
||||
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
|
||||
|
||||
/**
|
||||
|
@ -50,10 +52,6 @@ import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
|
|||
*/
|
||||
public class JavaKeystoreKeyProviderTest extends AbstractKeycloakTest {
|
||||
|
||||
private static final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsPAJ/X39oNRkoS+baWVhAghfO86ZPfkSHm4evmMDhbA0KqW1/hg55qUJoT91ytGozIsIxoCLKzQvZTluRpt0AMp7cmfaGWBQ8cBtb8/BL+5FkUucigmOcTrfPq9/xR9g4AMSXRItjLRsJPy2Bnjau64DVQ3N5NVbWAMw7/1XjuobEyPnw0RLqEr/TxWMteuaiV1n8amIAiT91xZ8UFyPv3urCkAz+r+iyVvdJcZwn2tUL6KLM7qX/HSX8SUtPrIMB8EdW1yNt5McO8Ro5GxwiyXimDKbY9ur2WP8/wrdk/0TkoUYeI1UsnFyoJcqqg2+1T+dNAMtJhF7uDhURVQ33QIDAQAB";
|
||||
private static final String CERTIFICATE = "MIIDeTCCAmGgAwIBAgIEbhSauDANBgkqhkiG9w0BAQsFADBsMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3duMCAXDTE2MTAxMzE4MjUxNFoYDzIyOTAwNzI4MTgyNTE0WjBsMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3duMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsPAJ/X39oNRkoS+baWVhAghfO86ZPfkSHm4evmMDhbA0KqW1/hg55qUJoT91ytGozIsIxoCLKzQvZTluRpt0AMp7cmfaGWBQ8cBtb8/BL+5FkUucigmOcTrfPq9/xR9g4AMSXRItjLRsJPy2Bnjau64DVQ3N5NVbWAMw7/1XjuobEyPnw0RLqEr/TxWMteuaiV1n8amIAiT91xZ8UFyPv3urCkAz+r+iyVvdJcZwn2tUL6KLM7qX/HSX8SUtPrIMB8EdW1yNt5McO8Ro5GxwiyXimDKbY9ur2WP8/wrdk/0TkoUYeI1UsnFyoJcqqg2+1T+dNAMtJhF7uDhURVQ33QIDAQABoyEwHzAdBgNVHQ4EFgQUgz0ABmkImZUEO2/w0shoH4rp6pwwDQYJKoZIhvcNAQELBQADggEBAK+syjqfFXmv7942+ZfmJfb4i/JilhwSyA2G1VvGR39dLW1nPmKMMUY6kKgJ2NZgaCGvJ4jxDhfNJ1jPG7rcO/eQuF3cx9r+nHiTcJ5PNLqG2q4dNNFshJ8aGuIaTQEB7S1OlGsEj0rd0YlJ+LTrFfEHsnsJvpvDRLdVMklib5fPk4W8ziuQ3rr6T/a+be3zfAqmFZx8j6E46jz9QO841uwqdzcR9kfSHS/76TNGZv8OB6jheyHrUdBygR85iizHgMqats/0zWmKEAvSp/DhAfyIFp8zZHvPjmpBl+mfmAqnrYY0oJRb5rRXmL8DKq5plc7jgO1H6aHh5mV6slXQDEw=";
|
||||
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder folder = new TemporaryFolder();
|
||||
|
||||
|
@ -65,7 +63,7 @@ public class JavaKeystoreKeyProviderTest extends AbstractKeycloakTest {
|
|||
|
||||
@Page
|
||||
protected LoginPage loginPage;
|
||||
private File file;
|
||||
private KeystoreUtils.KeystoreInfo generatedKeystore;
|
||||
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
|
@ -73,24 +71,32 @@ public class JavaKeystoreKeyProviderTest extends AbstractKeycloakTest {
|
|||
testRealms.add(realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeAbstractKeycloakTest() throws Exception {
|
||||
super.beforeAbstractKeycloakTest();
|
||||
|
||||
file = folder.newFile("keystore.jsk");
|
||||
|
||||
InputStream resourceAsStream = JavaKeystoreKeyProviderTest.class.getResourceAsStream("keystore.jks");
|
||||
IOUtils.copy(resourceAsStream, new FileOutputStream(file));
|
||||
@Test
|
||||
public void createJks() throws Exception {
|
||||
createSuccess(KeystoreUtil.KeystoreFormat.JKS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void create() throws Exception {
|
||||
public void createPkcs12() throws Exception {
|
||||
createSuccess(PKCS12);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBcfks() throws Exception {
|
||||
createSuccess(KeystoreUtil.KeystoreFormat.BCFKS);
|
||||
}
|
||||
|
||||
private void createSuccess(KeystoreUtil.KeystoreFormat keystoreType) throws Exception {
|
||||
KeystoreUtils.assumeKeystoreTypeSupported(keystoreType);
|
||||
generateKeystore(keystoreType);
|
||||
|
||||
long priority = System.currentTimeMillis();
|
||||
|
||||
ComponentRepresentation rep = createRep("valid", priority);
|
||||
|
||||
Response response = adminClient.realm("test").components().add(rep);
|
||||
String id = ApiUtil.getCreatedId(response);
|
||||
getCleanup().addComponentId(id);
|
||||
|
||||
ComponentRepresentation createdRep = adminClient.realm("test").components().component(id).toRepresentation();
|
||||
assertEquals(5, createdRep.getConfig().size());
|
||||
|
@ -105,12 +111,13 @@ public class JavaKeystoreKeyProviderTest extends AbstractKeycloakTest {
|
|||
assertEquals(id, key.getProviderId());
|
||||
assertEquals(AlgorithmType.RSA.name(), key.getType());
|
||||
assertEquals(priority, key.getProviderPriority());
|
||||
assertEquals(PUBLIC_KEY, key.getPublicKey());
|
||||
assertEquals(CERTIFICATE, key.getCertificate());
|
||||
assertEquals(generatedKeystore.getCertificateInfo().getPublicKey(), key.getPublicKey());
|
||||
assertEquals(generatedKeystore.getCertificateInfo().getCertificate(), key.getCertificate());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidKeystore() throws Exception {
|
||||
generateKeystore(KeystoreUtils.getPreferredKeystoreType());
|
||||
ComponentRepresentation rep = createRep("valid", System.currentTimeMillis());
|
||||
rep.getConfig().putSingle("keystore", "/nosuchfile");
|
||||
|
||||
|
@ -120,6 +127,7 @@ public class JavaKeystoreKeyProviderTest extends AbstractKeycloakTest {
|
|||
|
||||
@Test
|
||||
public void invalidKeystorePassword() throws Exception {
|
||||
generateKeystore(KeystoreUtils.getPreferredKeystoreType());
|
||||
ComponentRepresentation rep = createRep("valid", System.currentTimeMillis());
|
||||
rep.getConfig().putSingle("keystore", "invalid");
|
||||
|
||||
|
@ -129,6 +137,7 @@ public class JavaKeystoreKeyProviderTest extends AbstractKeycloakTest {
|
|||
|
||||
@Test
|
||||
public void invalidKeyAlias() throws Exception {
|
||||
generateKeystore(KeystoreUtils.getPreferredKeystoreType());
|
||||
ComponentRepresentation rep = createRep("valid", System.currentTimeMillis());
|
||||
rep.getConfig().putSingle("keyAlias", "invalid");
|
||||
|
||||
|
@ -138,10 +147,22 @@ public class JavaKeystoreKeyProviderTest extends AbstractKeycloakTest {
|
|||
|
||||
@Test
|
||||
public void invalidKeyPassword() throws Exception {
|
||||
KeystoreUtil.KeystoreFormat keystoreType = KeystoreUtils.getPreferredKeystoreType();
|
||||
if (keystoreType == PKCS12) {
|
||||
// only the keyStore password is significant with PKCS12. Hence we need to test with different keystore type
|
||||
String[] supportedKsTypes = KeystoreUtils.getSupportedKeystoreTypes();
|
||||
if (supportedKsTypes.length <= 1) {
|
||||
Assert.fail("Only PKCS12 type is supported, but invalidKeyPassword() scenario cannot be tested with it");
|
||||
}
|
||||
keystoreType = Enum.valueOf(KeystoreUtil.KeystoreFormat.class, supportedKsTypes[1]);
|
||||
log.infof("Fallback to keystore type '%s' for the invalidKeyPassword() test", keystoreType);
|
||||
}
|
||||
generateKeystore(keystoreType);
|
||||
ComponentRepresentation rep = createRep("valid", System.currentTimeMillis());
|
||||
rep.getConfig().putSingle("keyPassword", "invalid");
|
||||
|
||||
Response response = adminClient.realm("test").components().add(rep);
|
||||
Assert.assertEquals(400, response.getStatus());
|
||||
assertErrror(response, "Failed to load keys. Keystore on server can not be recovered.");
|
||||
}
|
||||
|
||||
|
@ -163,12 +184,16 @@ public class JavaKeystoreKeyProviderTest extends AbstractKeycloakTest {
|
|||
rep.setProviderType(KeyProvider.class.getName());
|
||||
rep.setConfig(new MultivaluedHashMap<>());
|
||||
rep.getConfig().putSingle("priority", Long.toString(priority));
|
||||
rep.getConfig().putSingle("keystore", file.getAbsolutePath());
|
||||
rep.getConfig().putSingle("keystore", generatedKeystore.getKeystoreFile().getAbsolutePath());
|
||||
rep.getConfig().putSingle("keystorePassword", "password");
|
||||
rep.getConfig().putSingle("keyAlias", "selfsigned");
|
||||
rep.getConfig().putSingle("keyPassword", "password");
|
||||
return rep;
|
||||
}
|
||||
|
||||
private void generateKeystore(KeystoreUtil.KeystoreFormat keystoreType) throws Exception {
|
||||
this.generatedKeystore = KeystoreUtils.generateKeystore(folder, keystoreType, "selfsigned", "password", "password");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -32,8 +32,11 @@ import org.apache.http.impl.client.DefaultHttpClient;
|
|||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.adapters.AdapterUtils;
|
||||
|
@ -85,13 +88,12 @@ import org.keycloak.testsuite.client.resources.TestOIDCEndpointsApplicationResou
|
|||
import org.keycloak.testsuite.rest.resource.TestingOIDCEndpointsApplicationResource;
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
import org.keycloak.testsuite.util.ClientManager;
|
||||
import org.keycloak.testsuite.util.KeystoreUtils;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import com.sun.jna.StringArray;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
|
@ -130,6 +132,21 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
|
|||
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(this);
|
||||
|
||||
@ClassRule
|
||||
public static TemporaryFolder folder = new TemporaryFolder();
|
||||
|
||||
private static KeystoreUtils.KeystoreInfo generatedKeystoreClient1;
|
||||
private static KeyPair keyPairClient1;
|
||||
|
||||
@BeforeClass
|
||||
public static void generateClient1KeyPair() throws Exception {
|
||||
generatedKeystoreClient1 = KeystoreUtils.generateKeystore(folder, KeystoreFormat.JKS, "clientkey", "storepass", "keypass");
|
||||
PublicKey publicKey = PemUtils.decodePublicKey(generatedKeystoreClient1.getCertificateInfo().getPublicKey());
|
||||
PrivateKey privateKey = PemUtils.decodePrivateKey(generatedKeystoreClient1.getCertificateInfo().getPrivateKey());
|
||||
keyPairClient1 = new KeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
private static String client1SAUserId;
|
||||
|
||||
private static RealmRepresentation testRealm;
|
||||
|
@ -151,7 +168,7 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
|
|||
app1 = ClientBuilder.create()
|
||||
.id(KeycloakModelUtils.generateId())
|
||||
.clientId("client1")
|
||||
.attribute(JWTClientAuthenticator.CERTIFICATE_ATTR, "MIICnTCCAYUCBgFPPLDaTzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdjbGllbnQxMB4XDTE1MDgxNzE3MjI0N1oXDTI1MDgxNzE3MjQyN1owEjEQMA4GA1UEAwwHY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIUjjgv+V3s96O+Za9002Lp/trtGuHBeaeVL9dFKMKzO2MPqdRmHB4PqNlDdd28Rwf5Xn6iWdFpyUKOnI/yXDLhdcuFpR0sMNK/C9Lt+hSpPFLuzDqgtPgDotlMxiHIWDOZ7g9/gPYNXbNvjv8nSiyqoguoCQiiafW90bPHsiVLdP7ZIUwCcfi1qQm7FhxRJ1NiW5dvUkuCnnWEf0XR+Wzc5eC9EgB0taLFiPsSEIlWMm5xlahYyXkPdNOqZjiRnrTWm5Y4uk8ZcsD/KbPTf/7t7cQXipVaswgjdYi1kK2/zRwOhg1QwWFX/qmvdd+fLxV0R6VqRDhn7Qep2cxwMxLsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAKE6OA46sf20bz8LZPoiNsqRwBUDkaMGXfnob7s/hJZIIwDEx0IAQ3uKsG7q9wb+aA6s+v7S340zb2k3IxuhFaHaZpAd4CyR5cn1FHylbzoZ7rI/3ASqHDqpljdJaFqPH+m7nZWtyDvtZf+gkZ8OjsndwsSBK1d/jMZPp29qYbl1+XfO7RCp/jDqro/R3saYFaIFiEZPeKn1hUJn6BO48vxH1xspSu9FmlvDOEAOz4AuM58z4zRMP49GcFdCWr1wkonJUHaSptJaQwmBwLFUkCbE5I1ixGMb7mjEud6Y5jhfzJiZMo2U8RfcjNbrN0diZl3jB6LQIwESnhYSghaTjNQ==")
|
||||
.attribute(JWTClientAuthenticator.CERTIFICATE_ATTR, generatedKeystoreClient1.getCertificateInfo().getCertificate())
|
||||
.attribute(OIDCConfigAttributes.USE_REFRESH_TOKEN_FOR_CLIENT_CREDENTIALS_GRANT, "true")
|
||||
.authenticatorType(JWTClientAuthenticator.PROVIDER_ID)
|
||||
.serviceAccountsEnabled(true)
|
||||
|
@ -171,9 +188,6 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
|
|||
|
||||
realmBuilder.client(app2);
|
||||
|
||||
// This one is for keystore-client2.p12 , which doesn't work on Sun JDK
|
||||
// app2.setAttribute(JWTClientAuthenticator.CERTIFICATE_ATTR, "MIICnTCCAYUCBgFPPLGHHjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdjbGllbnQxMB4XDTE1MDgxNzE3MjMzMVoXDTI1MDgxNzE3MjUxMVowEjEQMA4GA1UEAwwHY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIsatXj38fFD9fHslNrsWrubobudXYwwdZpGYqkHIhuDeSojGvhBSLmKIFmtbHMVcLEbS0dIEsSbNVrwjdFfuRuvd9Vu6Ng0JUC8fRhSeQniC3jcBuP8P4WlXK4+ir3Wlya+T6Hum9b68BiH0KyNZtFGJ6zLHuCcq9Bl0JifvibnUkDeTZPwgJNA9+GxS/x8fAkApcAbJrgBZvr57PwhbgHoZdB8aAY5f5ogbGzKDtSUMvFh+Jah39gWtn7p3VOuuMXA8SugogoH8C5m2itrPBL1UPhAcKUeWiqx4SmZe/lZo7x2WbSecNiFaiqBhIW+QbqCYW6I4u0YvuLuEe3+TC8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZzW5DZviCxUQdV5Ab07PZkUfvImHZ73oWWHZqzUQtZtbVdzfp3cnbb2wyXtlOvingO3hgpoTxV8vbKgLbIQfvkGGHBG1F5e0QVdtikfdcwWb7cy4/9F80OD7cgG0ZAzFbQ8ZY7iS3PToBp3+4tbIK2NK0ntt/MYgJnPbHeG4V4qfgUbFm1YgEK7WpbSVU8jGuJ5DWE+mlYgECZKZ5TSlaVGs2XOm6WXrJScucNekwcBWWiHyRsFHZEDzWmzt8TLTLnnb0vVjhx3qCYxah3RbyyMZm6WLZlLAaGEcwNDO8jaA3hAjrxoOA1xEaolQfGVsb/ElelHcR1Zfe0u4Ekd4tw==");
|
||||
|
||||
defaultUser = UserBuilder.create()
|
||||
.id(KeycloakModelUtils.generateId())
|
||||
//.serviceAccountId(app1.getClientId())
|
||||
|
@ -590,14 +604,22 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
|
|||
|
||||
@Test
|
||||
public void testClientWithGeneratedKeysJKS() throws Exception {
|
||||
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreFormat.JKS);
|
||||
testClientWithGeneratedKeys("JKS");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientWithGeneratedKeysPKCS12() throws Exception {
|
||||
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreFormat.PKCS12);
|
||||
testClientWithGeneratedKeys("PKCS12");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientWithGeneratedKeysBCFKS() throws Exception {
|
||||
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreFormat.BCFKS);
|
||||
testClientWithGeneratedKeys(KeystoreFormat.BCFKS.toString());
|
||||
}
|
||||
|
||||
private void testClientWithGeneratedKeys(String format) throws Exception {
|
||||
ClientRepresentation client = app3;
|
||||
UserRepresentation user = defaultUser;
|
||||
|
@ -664,12 +686,22 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
|
|||
|
||||
@Test
|
||||
public void testUploadKeystoreJKS() throws Exception {
|
||||
testUploadKeystore("JKS", "client-auth-test/keystore-client1.jks", "clientkey", "storepass");
|
||||
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreFormat.JKS);
|
||||
testUploadKeystore("JKS", generatedKeystoreClient1.getKeystoreFile().getAbsolutePath(), "clientkey", "storepass");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadKeystorePKCS12() throws Exception {
|
||||
testUploadKeystore("PKCS12", "client-auth-test/keystore-client2.p12", "clientkey", "pwd2");
|
||||
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreFormat.PKCS12);
|
||||
KeystoreUtils.KeystoreInfo ksInfo = KeystoreUtils.generateKeystore(folder, KeystoreFormat.PKCS12, "clientkey", "pwd2", "keypass");
|
||||
testUploadKeystore(KeystoreFormat.PKCS12.toString(), ksInfo.getKeystoreFile().getAbsolutePath(), "clientkey", "pwd2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadKeystoreBCFKS() throws Exception {
|
||||
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreFormat.BCFKS);
|
||||
KeystoreUtils.KeystoreInfo ksInfo = KeystoreUtils.generateKeystore(folder, KeystoreFormat.BCFKS, "clientkey", "pwd2", "keypass");
|
||||
testUploadKeystore(KeystoreFormat.BCFKS.toString(), ksInfo.getKeystoreFile().getAbsolutePath(), "clientkey", "pwd2");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -696,10 +728,10 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
|
|||
|
||||
// Load the keystore file
|
||||
URL fileUrl = (getClass().getClassLoader().getResource(filePath));
|
||||
if (fileUrl == null) {
|
||||
throw new IOException("File not found: " + filePath);
|
||||
File keystoreFile = fileUrl != null ? new File(fileUrl.getFile()) : new File(filePath);
|
||||
if (!keystoreFile.exists()) {
|
||||
throw new IOException("File not found: " + keystoreFile.getAbsolutePath());
|
||||
}
|
||||
File keystoreFile = new File(fileUrl.getFile());
|
||||
|
||||
// Get admin access token, no matter it's master realm's admin
|
||||
OAuthClient.AccessTokenResponse accessTokenResponse = oauth.doGrantAccessTokenRequest(
|
||||
|
@ -792,7 +824,7 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
|
|||
|
||||
@Test
|
||||
public void testAssertionMissingIssuer() throws Exception {
|
||||
String invalidJwt = getClientSignedJWT(getClient1KeyPair(), null);
|
||||
String invalidJwt = getClientSignedJWT(keyPairClient1, null);
|
||||
|
||||
List<NameValuePair> parameters = new LinkedList<NameValuePair>();
|
||||
parameters.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS));
|
||||
|
@ -807,7 +839,7 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
|
|||
|
||||
@Test
|
||||
public void testAssertionUnknownClient() throws Exception {
|
||||
String invalidJwt = getClientSignedJWT(getClient1KeyPair(), "unknown-client");
|
||||
String invalidJwt = getClientSignedJWT(keyPairClient1, "unknown-client");
|
||||
|
||||
List<NameValuePair> parameters = new LinkedList<NameValuePair>();
|
||||
parameters.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS));
|
||||
|
@ -1072,7 +1104,7 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
|
|||
|
||||
private OAuthClient.AccessTokenResponse testMissingClaim(int tokenTimeOffset, String... claims) throws Exception {
|
||||
CustomJWTClientCredentialsProvider jwtProvider = new CustomJWTClientCredentialsProvider();
|
||||
jwtProvider.setupKeyPair(getClient1KeyPair());
|
||||
jwtProvider.setupKeyPair(keyPairClient1);
|
||||
jwtProvider.setTokenTimeout(10);
|
||||
|
||||
for (String claim : claims) {
|
||||
|
@ -1304,19 +1336,14 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
|
|||
return getClientSignedJWT(getClient2KeyPair(), "client2", algorithm);
|
||||
}
|
||||
|
||||
private String getClient1SignedJWT() {
|
||||
return getClientSignedJWT(getClient1KeyPair(), "client1", Algorithm.RS256);
|
||||
private String getClient1SignedJWT() throws Exception {
|
||||
return getClientSignedJWT(keyPairClient1, "client1", Algorithm.RS256);
|
||||
}
|
||||
|
||||
private String getClient2SignedJWT() {
|
||||
return getClientSignedJWT(getClient2KeyPair(), "client2", Algorithm.RS256);
|
||||
}
|
||||
|
||||
private KeyPair getClient1KeyPair() {
|
||||
return KeystoreUtil.loadKeyPairFromKeystore("classpath:client-auth-test/keystore-client1.jks",
|
||||
"storepass", "keypass", "clientkey", KeystoreUtil.KeystoreFormat.JKS);
|
||||
}
|
||||
|
||||
private KeyPair getClient2KeyPair() {
|
||||
return KeystoreUtil.loadKeyPairFromKeystore("classpath:client-auth-test/keystore-client2.jks",
|
||||
"storepass", "keypass", "clientkey", KeystoreUtil.KeystoreFormat.JKS);
|
||||
|
|
Binary file not shown.
|
@ -263,6 +263,7 @@
|
|||
<keycloak.x509cert.lookup.provider>default</keycloak.x509cert.lookup.provider>
|
||||
<auth.server.quarkus.cluster.config>local</auth.server.quarkus.cluster.config>
|
||||
<auth.server.fips.mode>disabled</auth.server.fips.mode>
|
||||
<auth.server.supported.keystore.types>JKS,PKCS12,BCFKS</auth.server.supported.keystore.types>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
|
@ -685,6 +686,7 @@
|
|||
<!-- FIPS 140-2 -->
|
||||
<auth.server.fips.mode>${auth.server.fips.mode}</auth.server.fips.mode>
|
||||
<auth.server.fips.keystore.type>${auth.server.fips.keystore.type}</auth.server.fips.keystore.type>
|
||||
<auth.server.supported.keystore.types>${auth.server.supported.keystore.types}</auth.server.supported.keystore.types>
|
||||
|
||||
<!--
|
||||
~ Used for Wildfly Elytron 1.13.0.CR3+ RESTEasy client SSL truststore configuration.
|
||||
|
@ -1587,6 +1589,8 @@
|
|||
<properties>
|
||||
<auth.server.fips.mode>enabled</auth.server.fips.mode>
|
||||
|
||||
<auth.server.supported.keystore.types>PKCS12,BCFKS</auth.server.supported.keystore.types>
|
||||
|
||||
<auth.server.keystore.type>pkcs12</auth.server.keystore.type>
|
||||
<auth.server.keystore>${auth.server.config.dir}/keycloak-fips.keystore.${auth.server.keystore.type}</auth.server.keystore>
|
||||
<auth.server.keystore.password>passwordpassword</auth.server.keystore.password>
|
||||
|
|
Loading…
Reference in a new issue