Introduce crypto/default module. Refactoring BouncyIntegration (#12692)

Closes #12625
This commit is contained in:
Marek Posolda 2022-06-29 07:17:09 +02:00 committed by GitHub
parent 91335ebaad
commit be1e31dc68
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 685 additions and 166 deletions

View file

@ -73,6 +73,10 @@
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>${keycloak.crypto.artifactId}</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-authz-client</artifactId> <artifactId>keycloak-authz-client</artifactId>

View file

@ -25,7 +25,9 @@ import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
import org.keycloak.adapters.authorization.PolicyEnforcer; import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.adapters.rotation.HardcodedPublicKeyLocator; import org.keycloak.adapters.rotation.HardcodedPublicKeyLocator;
import org.keycloak.adapters.rotation.JWKPublicKeyLocator; import org.keycloak.adapters.rotation.JWKPublicKeyLocator;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.enums.SslRequired; import org.keycloak.common.enums.SslRequired;
import org.keycloak.common.util.BouncyIntegration;
import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.PemUtils;
import org.keycloak.enums.TokenStore; import org.keycloak.enums.TokenStore;
import org.keycloak.representations.adapters.config.AdapterConfig; import org.keycloak.representations.adapters.config.AdapterConfig;
@ -181,6 +183,7 @@ public class KeycloakDeploymentBuilder {
} }
public static KeycloakDeployment build(InputStream is) { public static KeycloakDeployment build(InputStream is) {
CryptoIntegration.init(KeycloakDeploymentBuilder.class.getClassLoader());
AdapterConfig adapterConfig = loadAdapterConfig(is); AdapterConfig adapterConfig = loadAdapterConfig(is);
return new KeycloakDeploymentBuilder().internalBuild(adapterConfig); return new KeycloakDeploymentBuilder().internalBuild(adapterConfig);
} }

View file

@ -44,6 +44,11 @@
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-crypto-default</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId> <artifactId>keycloak-adapter-spi</artifactId>

View file

@ -0,0 +1,57 @@
package org.keycloak.common.crypto;
import java.util.List;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.jboss.logging.Logger;
import org.keycloak.common.util.BouncyIntegration;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class CryptoIntegration {
protected static final Logger logger = Logger.getLogger(CryptoIntegration.class);
private static final Object lock = new Object();
private static volatile CryptoProvider cryptoProvider;
public static void init(ClassLoader classLoader) {
if (cryptoProvider == null) {
synchronized (lock) {
if (cryptoProvider == null) {
cryptoProvider = detectProvider(classLoader);
logger.debugv("BouncyCastle provider: {0}", BouncyIntegration.PROVIDER);
}
}
}
}
public static CryptoProvider getProvider() {
if (cryptoProvider == null) {
throw new IllegalStateException("Illegal state. Please init first before obtaining provider");
}
return cryptoProvider;
}
// Try to auto-detect provider
private static CryptoProvider detectProvider(ClassLoader classLoader) {
List<CryptoProvider> foundProviders = StreamSupport.stream(ServiceLoader.load(CryptoProvider.class, classLoader).spliterator(), false)
.collect(Collectors.toList());
if (foundProviders.isEmpty()) {
throw new IllegalStateException("Not able to load any cryptoProvider with the classLoader: " + classLoader);
} else if (foundProviders.size() > 1) {
throw new IllegalStateException("Multiple crypto providers loaded with the classLoader: " + classLoader +
". Make sure only one cryptoProvider available on the classpath. Available providers: " +foundProviders);
} else {
logger.infof("Detected security provider: %s", foundProviders.get(0).getClass().getName());
return foundProviders.get(0);
}
}
}

View file

@ -1,11 +1,9 @@
package org.keycloak.crypto.integration; package org.keycloak.common.crypto;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
import java.security.SecureRandom; import java.security.SecureRandom;
import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider;
/** /**
* Abstraction to handle differences between the APIs for non-fips and fips mode * Abstraction to handle differences between the APIs for non-fips and fips mode
* *
@ -18,5 +16,13 @@ public interface CryptoProvider {
*/ */
SecureRandom getSecureRandom() throws NoSuchAlgorithmException, NoSuchProviderException; SecureRandom getSecureRandom() throws NoSuchAlgorithmException, NoSuchProviderException;
JWEAlgorithmProvider getAesKeyWrapAlgorithmProvider(); /**
* Get some algorithm provider implementation. Returned implementation can be dependent according to if we have
* non-fips bouncycastle or fips bouncycastle on the classpath.
*
* @param clazz Returned class.
* @param algorithm Type of the algorithm, which we want to return
* @return
*/
<T> T getAlgorithmProvider(Class<T> clazz, String algorithm);
} }

View file

@ -0,0 +1,11 @@
package org.keycloak.common.crypto;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class CryptoProviderTypes {
public static final String BC_SECURITY_PROVIDER = "bc-provider";
public static final String AES_KEY_WRAP_ALGORITHM_PROVIDER = "aes-keywrap-alg";
}

View file

@ -18,8 +18,9 @@
package org.keycloak.common.util; package org.keycloak.common.util;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.crypto.CryptoProviderTypes;
import java.lang.reflect.Constructor;
import java.security.Provider; import java.security.Provider;
import java.security.Security; import java.security.Security;
@ -31,34 +32,20 @@ public class BouncyIntegration {
private static final Logger log = Logger.getLogger(BouncyIntegration.class); private static final Logger log = Logger.getLogger(BouncyIntegration.class);
private static final String[] providerClassNames = {
"org.bouncycastle.jce.provider.BouncyCastleProvider",
"org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider"
};
public static final String PROVIDER = loadProvider(); public static final String PROVIDER = loadProvider();
private static String loadProvider() { private static String loadProvider() {
for (String providerClassName : providerClassNames) { Provider provider = CryptoIntegration.getProvider().getAlgorithmProvider(Provider.class, CryptoProviderTypes.BC_SECURITY_PROVIDER);
try { if (provider == null) {
Class<?> providerClass = Class.forName(providerClassName, true, BouncyIntegration.class.getClassLoader());
Constructor<Provider> constructor = (Constructor<Provider>) providerClass.getConstructor();
Provider provider = constructor.newInstance();
if (Security.getProvider(provider.getName()) == null) {
Security.addProvider(provider);
log.debugv("Loaded {0} security provider", providerClassName);
} else {
log.debugv("Security provider {0} already loaded", providerClassName);
}
return provider.getName();
} catch (Exception e) {
log.debugv("Failed to load {0}", e, providerClassName);
}
}
throw new RuntimeException("Failed to load required security provider: BouncyCastleProvider or BouncyCastleFipsProvider"); throw new RuntimeException("Failed to load required security provider: BouncyCastleProvider or BouncyCastleFipsProvider");
} }
if (Security.getProvider(provider.getName()) == null) {
Security.addProvider(provider);
log.debugv("Loaded {0} security provider", provider.getClass().getName());
} else {
log.debugv("Security provider {0} already loaded", provider.getClass().getName());
}
return provider.getName();
}
} }

View file

@ -89,6 +89,13 @@
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive> </archive>
</configuration> </configuration>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.felix</groupId> <groupId>org.apache.felix</groupId>

View file

@ -1,43 +0,0 @@
package org.keycloak.crypto.integration;
import java.util.ServiceLoader;
import org.jboss.logging.Logger;
import org.keycloak.common.util.BouncyIntegration;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class CryptoIntegration {
protected static final Logger logger = Logger.getLogger(CryptoIntegration.class);
private static volatile CryptoProvider securityProvider;
public static CryptoProvider getProvider() {
if (securityProvider == null) {
logger.debugf("Using BouncyCastle provider: %s", BouncyIntegration.PROVIDER);
securityProvider = detectProvider();
logger.infof("Detected security provider: %s", securityProvider);
}
return securityProvider;
}
// This can be possibly set by the configuration (SPI) to override the "detected" instance
public static void setProvider(CryptoProvider provider) {
securityProvider = provider;
}
// Try to auto-detect provider
private static CryptoProvider detectProvider() {
// TODO This may not work on Wildfly (assuming FIPS module will be different Wildfly module than keycloak-core). May need to be improved (EG. with usage of org.keycloak.platform.Platform)
for (CryptoProvider cryptoProvider : ServiceLoader.load(CryptoProvider.class, CryptoIntegration.class.getClassLoader())) {
return cryptoProvider;
}
// Fallback. This should not be needed once DefaultCryptoProvider is moved into separate module like "crypto/default" and provided via ServiceLoader
return new DefaultCryptoProvider();
}
}

View file

@ -1,23 +0,0 @@
package org.keycloak.crypto.integration;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import org.keycloak.jose.jwe.alg.AesKeyWrapAlgorithmProvider;
import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class DefaultCryptoProvider implements CryptoProvider {
@Override
public SecureRandom getSecureRandom() throws NoSuchAlgorithmException {
return SecureRandom.getInstance("SHA1PRNG");
}
@Override
public JWEAlgorithmProvider getAesKeyWrapAlgorithmProvider() {
return new AesKeyWrapAlgorithmProvider();
}
}

View file

@ -20,7 +20,8 @@ package org.keycloak.jose.jwe;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.keycloak.crypto.integration.CryptoIntegration; import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.crypto.CryptoProviderTypes;
import org.keycloak.jose.jwe.alg.DirectAlgorithmProvider; import org.keycloak.jose.jwe.alg.DirectAlgorithmProvider;
import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider; import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider;
import org.keycloak.jose.jwe.alg.RsaKeyEncryption256JWEAlgorithmProvider; import org.keycloak.jose.jwe.alg.RsaKeyEncryption256JWEAlgorithmProvider;
@ -47,7 +48,7 @@ class JWERegistry {
static { static {
// Provider 'dir' just directly uses encryption keys for encrypt/decrypt content. // Provider 'dir' just directly uses encryption keys for encrypt/decrypt content.
ALG_PROVIDERS.put(JWEConstants.DIR, new DirectAlgorithmProvider()); ALG_PROVIDERS.put(JWEConstants.DIR, new DirectAlgorithmProvider());
ALG_PROVIDERS.put(JWEConstants.A128KW, CryptoIntegration.getProvider().getAesKeyWrapAlgorithmProvider()); ALG_PROVIDERS.put(JWEConstants.A128KW, CryptoIntegration.getProvider().getAlgorithmProvider(JWEAlgorithmProvider.class, CryptoProviderTypes.AES_KEY_WRAP_ALGORITHM_PROVIDER));
ALG_PROVIDERS.put(JWEConstants.RSA_OAEP, new RsaKeyEncryptionJWEAlgorithmProvider("RSA/ECB/OAEPWithSHA-1AndMGF1Padding")); ALG_PROVIDERS.put(JWEConstants.RSA_OAEP, new RsaKeyEncryptionJWEAlgorithmProvider("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"));
ALG_PROVIDERS.put(JWEConstants.RSA_OAEP_256, new RsaKeyEncryption256JWEAlgorithmProvider("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")); ALG_PROVIDERS.put(JWEConstants.RSA_OAEP_256, new RsaKeyEncryption256JWEAlgorithmProvider("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"));

View file

@ -18,13 +18,21 @@
package org.keycloak; package org.keycloak;
import org.junit.Assert; import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.common.VerificationException; import org.keycloak.common.VerificationException;
import org.keycloak.rule.CryptoInitRule;
/** /**
* This is not tested in keycloak-core. The subclasses should be created in the crypto modules to make sure it is tested with corresponding modules (bouncycastle VS bouncycastle-fips)
*
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
public class KeyPairVerifierTest { public abstract class KeyPairVerifierTest {
@ClassRule
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
String privateKey1 = "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y="; String privateKey1 = "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=";
String publicKey1 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"; String publicKey1 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB";

View file

@ -25,7 +25,9 @@ import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import org.junit.Assert; import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.common.util.Base64; import org.keycloak.common.util.Base64;
import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.Base64Url;
@ -36,11 +38,17 @@ import org.keycloak.jose.jwe.alg.RsaKeyEncryptionJWEAlgorithmProvider;
import org.keycloak.jose.jwe.enc.AesCbcHmacShaJWEEncryptionProvider; import org.keycloak.jose.jwe.enc.AesCbcHmacShaJWEEncryptionProvider;
import org.keycloak.jose.jwe.enc.AesGcmJWEEncryptionProvider; import org.keycloak.jose.jwe.enc.AesGcmJWEEncryptionProvider;
import org.keycloak.jose.jwe.enc.JWEEncryptionProvider; import org.keycloak.jose.jwe.enc.JWEEncryptionProvider;
import org.keycloak.rule.CryptoInitRule;
/** /**
* This is not tested in keycloak-core. The subclasses should be created in the crypto modules to make sure it is tested with corresponding modules (bouncycastle VS bouncycastle-fips)
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class JWETest { public abstract class JWETest {
@ClassRule
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
private static final String PAYLOAD = "Hello world! How are you man? I hope you are fine. This is some quite a long text, which is much longer than just simple 'Hello World'"; private static final String PAYLOAD = "Hello world! How are you man? I hope you are fine. This is some quite a long text, which is much longer than just simple 'Hello World'";

View file

@ -19,13 +19,17 @@ package org.keycloak.jose.jwk;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.BouncyIntegration; import org.keycloak.common.util.BouncyIntegration;
import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.PemUtils;
import org.keycloak.crypto.JavaAlgorithm; import org.keycloak.crypto.JavaAlgorithm;
import org.keycloak.crypto.integration.CryptoIntegration; import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.rule.CryptoInitRule;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -47,9 +51,14 @@ import static org.junit.Assert.assertTrue;
import static org.keycloak.common.util.CertificateUtils.generateV1SelfSignedCertificate; import static org.keycloak.common.util.CertificateUtils.generateV1SelfSignedCertificate;
/** /**
* This is not tested in keycloak-core. The subclasses should be created in the crypto modules to make sure it is tested with corresponding modules (bouncycastle VS bouncycastle-fips)
*
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
public class JWKTest { public abstract class JWKTest {
@ClassRule
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
@Test @Test
public void publicRs256() throws Exception { public void publicRs256() throws Exception {

View file

@ -0,0 +1,16 @@
package org.keycloak.rule;
import org.junit.rules.ExternalResource;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.crypto.CryptoProvider;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class CryptoInitRule extends ExternalResource {
@Override
protected void before() throws Throwable {
CryptoIntegration.init(CryptoProvider.class.getClassLoader());
}
}

70
crypto/default/pom.xml Normal file
View file

@ -0,0 +1,70 @@
<?xml version="1.0"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-crypto-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>999-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-crypto-default</artifactId>
<name>Keycloak Crypto Default</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View file

@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.keycloak.jose.jwe.alg; package org.keycloak.crypto.def;
import java.security.Key; import java.security.Key;
@ -23,6 +23,7 @@ import org.bouncycastle.crypto.Wrapper;
import org.bouncycastle.crypto.engines.AESWrapEngine; import org.bouncycastle.crypto.engines.AESWrapEngine;
import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.KeyParameter;
import org.keycloak.jose.jwe.JWEKeyStorage; import org.keycloak.jose.jwe.JWEKeyStorage;
import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider;
import org.keycloak.jose.jwe.enc.JWEEncryptionProvider; import org.keycloak.jose.jwe.enc.JWEEncryptionProvider;
/** /**

View file

@ -0,0 +1,38 @@
package org.keycloak.crypto.def;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.keycloak.common.crypto.CryptoProvider;
import org.keycloak.common.crypto.CryptoProviderTypes;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class DefaultCryptoProvider implements CryptoProvider {
private Map<String, Supplier<?>> providers = new HashMap<>();
public DefaultCryptoProvider() {
providers.put(CryptoProviderTypes.BC_SECURITY_PROVIDER, BouncyCastleProvider::new);
providers.put(CryptoProviderTypes.AES_KEY_WRAP_ALGORITHM_PROVIDER, AesKeyWrapAlgorithmProvider::new);
}
@Override
public SecureRandom getSecureRandom() throws NoSuchAlgorithmException {
return SecureRandom.getInstance("SHA1PRNG");
}
@Override
public <T> T getAlgorithmProvider(Class<T> clazz, String algorithm) {
Object o = providers.get(algorithm).get();
if (o == null) {
throw new IllegalArgumentException("Not found provider of algorithm: " + algorithm);
}
return clazz.cast(o);
}
}

View file

@ -0,0 +1,18 @@
#
# 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.
#
org.keycloak.crypto.def.DefaultCryptoProvider

View file

@ -0,0 +1,11 @@
package org.keycloak.crypto.def.test;
import org.keycloak.jose.JWETest;
/**
* Test with default security provider and non-fips bouncycastle
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class DefaultCryptoJWETest extends JWETest {
}

View file

@ -0,0 +1,11 @@
package org.keycloak.crypto.def.test;
import org.keycloak.jose.jwk.JWKTest;
/**
* Test with default security provider and non-fips bouncycastle
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class DefaultCryptoJWKTest extends JWKTest {
}

View file

@ -0,0 +1,11 @@
package org.keycloak.crypto.def.test;
import org.keycloak.KeyPairVerifierTest;
/**
* Test with default security provider and non-fips bouncycastle
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class DefaultCryptoKeyPairVerifierTest extends KeyPairVerifierTest {
}

View file

@ -0,0 +1,20 @@
package org.keycloak.crypto.def.test;
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.crypto.CryptoProviderTypes;
import org.keycloak.crypto.def.AesKeyWrapAlgorithmProvider;
import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class DefaultCryptoUnitTest {
@Test
public void testDefaultCrypto() throws Exception {
JWEAlgorithmProvider jweAlg = CryptoIntegration.getProvider().getAlgorithmProvider(JWEAlgorithmProvider.class, CryptoProviderTypes.AES_KEY_WRAP_ALGORITHM_PROVIDER);
Assert.assertEquals(jweAlg.getClass(), AesKeyWrapAlgorithmProvider.class);
}
}

View file

@ -26,14 +26,32 @@
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-fips1402</artifactId> <artifactId>keycloak-crypto-fips1402</artifactId>
<name>Keycloak FIPS 140-2 Integration</name> <name>Keycloak Crypto FIPS 140-2 Integration</name>
<description/> <description/>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
<!-- TODO: These exclusions can be removed once we remove dependency of keycloak-core on bouncycastle -->
<exclusions>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</exclusion>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<scope>test</scope>
<type>test-jar</type>
<!-- TODO: These exclusions can be removed once we remove dependency of keycloak-core on bouncycastle -->
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>org.bouncycastle</groupId> <groupId>org.bouncycastle</groupId>

View file

@ -3,9 +3,13 @@ package org.keycloak.crypto.fips;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import org.keycloak.crypto.integration.CryptoProvider; import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider;
import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider; import org.keycloak.common.crypto.CryptoProvider;
import org.keycloak.common.crypto.CryptoProviderTypes;
/** /**
@ -15,13 +19,24 @@ import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider;
*/ */
public class FIPS1402Provider implements CryptoProvider { public class FIPS1402Provider implements CryptoProvider {
private Map<String, Supplier<?>> providers = new HashMap<>();
public FIPS1402Provider() {
providers.put(CryptoProviderTypes.BC_SECURITY_PROVIDER, BouncyCastleFipsProvider::new);
providers.put(CryptoProviderTypes.AES_KEY_WRAP_ALGORITHM_PROVIDER, FIPSAesKeyWrapAlgorithmProvider::new);
}
@Override @Override
public SecureRandom getSecureRandom() throws NoSuchAlgorithmException, NoSuchProviderException { public SecureRandom getSecureRandom() throws NoSuchAlgorithmException, NoSuchProviderException {
return SecureRandom.getInstance("DEFAULT","BCFIPS"); return SecureRandom.getInstance("DEFAULT","BCFIPS");
} }
@Override @Override
public JWEAlgorithmProvider getAesKeyWrapAlgorithmProvider() { public <T> T getAlgorithmProvider(Class<T> clazz, String algorithm) {
return new FIPSAesKeyWrapAlgorithmProvider(); Object o = providers.get(algorithm).get();
if (o == null) {
throw new IllegalArgumentException("Not found provider of algorithm: " + algorithm);
}
return clazz.cast(o);
} }
} }

View file

@ -0,0 +1,13 @@
package org.keycloak.crypto.fips.test;
import org.junit.Ignore;
import org.keycloak.jose.JWETest;
/**
* Test with fips1402 security provider and bouncycastle-fips
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@Ignore("Ignored by default as it does not work on non-fips enabled environment") // TODO: Figure how to test in the FIPS environments, but still keep disabled in the non-FIPS environments
public class FIPS1402JWETest extends JWETest {
}

View file

@ -0,0 +1,13 @@
package org.keycloak.crypto.fips.test;
import org.junit.Ignore;
import org.keycloak.jose.jwk.JWKTest;
/**
* Test with fips1402 security provider and bouncycastle-fips
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@Ignore("Ignored by default as it does not work on non-fips enabled environment") // TODO: Figure how to test in the FIPS environments, but still keep disabled in the non-FIPS environments
public class FIPS1402JWKTest extends JWKTest {
}

View file

@ -0,0 +1,13 @@
package org.keycloak.crypto.fips.test;
import org.junit.Ignore;
import org.keycloak.KeyPairVerifierTest;
/**
* Test with fips1402 security provider and bouncycastle-fips
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@Ignore("Ignored by default as it does not work on non-fips enabled environment") // TODO: Figure how to test in the FIPS environments, but still keep disabled in the non-FIPS environments
public class FIPS1402KeyPairVerifierTest extends KeyPairVerifierTest {
}

View file

@ -1,25 +1,26 @@
package org.keycloak.crypto.fips.test; package org.keycloak.crypto.fips.test;
import java.security.SecureRandom;
import org.junit.Assert; import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.common.crypto.CryptoProviderTypes;
import org.keycloak.crypto.fips.FIPSAesKeyWrapAlgorithmProvider; import org.keycloak.crypto.fips.FIPSAesKeyWrapAlgorithmProvider;
import org.keycloak.crypto.integration.CryptoIntegration; import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider; import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider;
import org.keycloak.rule.CryptoInitRule;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class FIPS1402UnitTest { public class FIPS1402UnitTest {
@ClassRule
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
@Test @Test
public void testFips() throws Exception { public void testFips() throws Exception {
JWEAlgorithmProvider jweAlg = CryptoIntegration.getProvider().getAesKeyWrapAlgorithmProvider(); JWEAlgorithmProvider jweAlg = CryptoIntegration.getProvider().getAlgorithmProvider(JWEAlgorithmProvider.class, CryptoProviderTypes.AES_KEY_WRAP_ALGORITHM_PROVIDER);
Assert.assertEquals(jweAlg.getClass(), FIPSAesKeyWrapAlgorithmProvider.class); Assert.assertEquals(jweAlg.getClass(), FIPSAesKeyWrapAlgorithmProvider.class);
SecureRandom scr = CryptoIntegration.getProvider().getSecureRandom();
Assert.assertEquals("BCFIPS", scr.getProvider().getName());
} }
} }

View file

@ -32,5 +32,6 @@
<modules> <modules>
<module>fips1402</module> <module>fips1402</module>
<module>default</module>
</modules> </modules>
</project> </project>

View file

@ -58,6 +58,16 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-crypto-default</artifactId>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>

View file

@ -34,6 +34,7 @@
<module name="org.keycloak.keycloak-adapter-spi"/> <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-common"/> <module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-crypto-default" services="import"/>
<module name="org.keycloak.keycloak-authz-client"/> <module name="org.keycloak.keycloak-authz-client"/>
</dependencies> </dependencies>

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-crypto-default">
<resources>
<artifact name="${org.keycloak:keycloak-crypto-default}"/>
</resources>
<dependencies>
<module name="com.fasterxml.jackson.core.jackson-annotations"/>
<module name="com.fasterxml.jackson.core.jackson-core"/>
<module name="com.fasterxml.jackson.core.jackson-databind"/>
<module name="com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider"/>
<module name="org.keycloak.keycloak-common" />
<module name="org.keycloak.keycloak-core" />
<module name="org.bouncycastle" />
<module name="org.jboss.logging"/>
<module name="javax.api"/>
<module name="javax.activation.api"/>
<module name="sun.jdk" optional="true" />
</dependencies>
</module>

View file

@ -140,6 +140,16 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-crypto-default</artifactId>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-js-adapter</artifactId> <artifactId>keycloak-js-adapter</artifactId>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-crypto-default">
<resources>
<artifact name="${org.keycloak:keycloak-crypto-default}"/>
</resources>
<dependencies>
<module name="com.fasterxml.jackson.core.jackson-core"/>
<module name="com.fasterxml.jackson.core.jackson-annotations"/>
<module name="com.fasterxml.jackson.core.jackson-databind"/>
<module name="com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider"/>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.bouncycastle" />
<module name="org.jboss.logging"/>
<module name="javax.api"/>
<module name="javax.activation.api"/>
<module name="sun.jdk" optional="true" />
</dependencies>
</module>

View file

@ -27,6 +27,7 @@
<dependencies> <dependencies>
<module name="org.keycloak.keycloak-common" services="import"/> <module name="org.keycloak.keycloak-common" services="import"/>
<module name="org.keycloak.keycloak-core" services="import"/> <module name="org.keycloak.keycloak-core" services="import"/>
<module name="org.keycloak.keycloak-crypto-default" services="import"/>
<module name="org.keycloak.keycloak-js-adapter" services="import"/> <module name="org.keycloak.keycloak-js-adapter" services="import"/>
<module name="org.keycloak.keycloak-kerberos-federation" services="import"/> <module name="org.keycloak.keycloak-kerberos-federation" services="import"/>
<module name="org.keycloak.keycloak-ldap-federation" services="import"/> <module name="org.keycloak.keycloak-ldap-federation" services="import"/>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-crypto-default">
<resources>
<artifact name="${org.keycloak:keycloak-crypto-default}"/>
</resources>
<dependencies>
<module name="com.fasterxml.jackson.core.jackson-core"/>
<module name="com.fasterxml.jackson.core.jackson-annotations"/>
<module name="com.fasterxml.jackson.core.jackson-databind"/>
<module name="com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider"/>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.bouncycastle" />
<module name="org.jboss.logging"/>
<module name="javax.api"/>
<module name="javax.activation.api"/>
<module name="sun.jdk" optional="true" />
</dependencies>
</module>

View file

@ -27,6 +27,7 @@
<dependencies> <dependencies>
<module name="org.keycloak.keycloak-common" services="import"/> <module name="org.keycloak.keycloak-common" services="import"/>
<module name="org.keycloak.keycloak-core" services="import"/> <module name="org.keycloak.keycloak-core" services="import"/>
<module name="org.keycloak.keycloak-crypto-default" services="import"/>
<module name="org.keycloak.keycloak-js-adapter" services="import"/> <module name="org.keycloak.keycloak-js-adapter" services="import"/>
<module name="org.keycloak.keycloak-kerberos-federation" services="import"/> <module name="org.keycloak.keycloak-kerberos-federation" services="import"/>
<module name="org.keycloak.keycloak-ldap-federation" services="import"/> <module name="org.keycloak.keycloak-ldap-federation" services="import"/>

View file

@ -29,6 +29,7 @@
<dependencies> <dependencies>
<module name="org.keycloak.keycloak-common"/> <module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-crypto-default" services="import"/>
<module name="org.keycloak.keycloak-server-spi" services="import"/> <module name="org.keycloak.keycloak-server-spi" services="import"/>
<module name="org.keycloak.keycloak-server-spi-private" services="import"/> <module name="org.keycloak.keycloak-server-spi-private" services="import"/>
<module name="org.keycloak.keycloak-services" services="import"/> <module name="org.keycloak.keycloak-services" services="import"/>

View file

@ -10,7 +10,10 @@ With OpenJDK 11 on the classpath, run this from the project root directory:
mvn clean install -DskipTests=true -Dfips140-2 -Pquarkus mvn clean install -DskipTests=true -Dfips140-2 -Pquarkus
``` ```
The property `fips140-2` is used to trigger maven profile to build keycloak+quarkus distribution with `bouncycastle-fips` dependencies instead of plain `bouncycastle` The property `fips140-2` is used to trigger maven profile to build keycloak+quarkus distribution with `bouncycastle-fips` dependencies instead of plain `bouncycastle`
and also with `keycloak-fips140-2` module containing some security code dependent on bouncycastle-fips APIs. and also with `keycloak-crypto-fips1402` module containing some security code dependent on bouncycastle-fips APIs.
Note, that if you ommit the `fips140-2` property from the command above, then the quarkus distribution will be built
with the plain non-fips bouncycastle dependencies and with `keycloak-crypto-default` module.
Then unzip and check only bouncycastle-fips libraries are inside "lib" directory: Then unzip and check only bouncycastle-fips libraries are inside "lib" directory:
``` ```

37
pom.xml
View file

@ -1057,6 +1057,12 @@
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<type>test-jar</type>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-config-api</artifactId> <artifactId>keycloak-config-api</artifactId>
@ -1600,7 +1606,12 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-fips1402</artifactId> <artifactId>keycloak-crypto-default</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-crypto-fips1402</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency> <dependency>
@ -2124,5 +2135,29 @@
</build> </build>
</profile> </profile>
<profile>
<id>crypto-default</id>
<activation>
<property>
<name>!fips140-2</name>
</property>
</activation>
<properties>
<keycloak.crypto.artifactId>keycloak-crypto-default</keycloak.crypto.artifactId>
</properties>
</profile>
<profile>
<id>fips140-2</id>
<activation>
<property>
<name>fips140-2</name>
</property>
</activation>
<properties>
<keycloak.crypto.artifactId>keycloak-crypto-fips1402</keycloak.crypto.artifactId>
</properties>
</profile>
</profiles> </profiles>
</project> </project>

View file

@ -141,6 +141,10 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>${keycloak.crypto.artifactId}</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId> <artifactId>keycloak-server-spi</artifactId>
@ -593,52 +597,6 @@
</dependency> </dependency>
</dependencies> </dependencies>
</profile> </profile>
<profile>
<id>crypto-default</id>
<activation>
<property>
<name>!fips140-2</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</profile>
<profile>
<id>fips140-2</id>
<activation>
<property>
<name>fips140-2</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-fips1402</artifactId>
</dependency>
</dependencies>
</profile>
</profiles> </profiles>
<build> <build>

View file

@ -54,6 +54,11 @@
<groupId>org.apache.santuario</groupId> <groupId>org.apache.santuario</groupId>
<artifactId>xmlsec</artifactId> <artifactId>xmlsec</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>${keycloak.crypto.artifactId}</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>

View file

@ -19,9 +19,12 @@ package org.keycloak.saml.processing.core.parsers.saml;
import org.hamcrest.Matcher; import org.hamcrest.Matcher;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.crypto.CryptoProvider;
import org.keycloak.common.util.Base64; import org.keycloak.common.util.Base64;
import org.keycloak.common.util.DerUtils; import org.keycloak.common.util.DerUtils;
import org.keycloak.common.util.StreamUtil; import org.keycloak.common.util.StreamUtil;
@ -128,6 +131,11 @@ public class SAMLParserTest {
@Rule @Rule
public ExpectedException thrown = ExpectedException.none(); public ExpectedException thrown = ExpectedException.none();
@BeforeClass
public static void initCrypto() {
CryptoIntegration.init(CryptoProvider.class.getClassLoader());
}
@Before @Before
public void initParser() { public void initParser() {
this.parser = SAMLParser.getInstance(); this.parser = SAMLParser.getInstance();

View file

@ -20,7 +20,11 @@ import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Arrays;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.crypto.CryptoProvider;
import org.keycloak.common.util.Base64; import org.keycloak.common.util.Base64;
import org.keycloak.common.util.DerUtils; import org.keycloak.common.util.DerUtils;
import org.keycloak.dom.saml.v2.assertion.NameIDType; import org.keycloak.dom.saml.v2.assertion.NameIDType;
@ -40,6 +44,11 @@ public class AssertionUtilTest {
*/ */
private static final String PUBLIC_CERT = "MIIDdzCCAl+gAwIBAgIEbySuqTANBgkqhkiG9w0BAQsFADBsMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3duMB4XDTE1MDEyODIyMTYyMFoXDTE3MTAyNDIyMTYyMFowbDEQMA4GA1UEBhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAII/K9NNvXi9IySl7+l2zY/kKrGTtuR4WdCI0xLW/Jn4dLY7v1/HOnV4CC4ecFOzhdNFPtJkmEhP/q62CpmOYOKApXk3tfmm2rwEz9bWprVxgFGKnbrWlz61Z/cjLAlhD3IUj2ZRBquYgSXQPsYfXo1JmSWF5pZ9uh1FVqu9f4wvRqY20ZhUN+39F+1iaBsoqsrbXypCn1HgZkW1/9D9GZug1c3vB4wg1TwZZWRNGtxwoEhdK6dPrNcZ+6PdanVilWrbQFbBjY4wz8/7IMBzssoQ7Usmo8F1Piv0FGfaVeJqBrcAvbiBMpk8pT+27u6p8VyIX6LhGvnxIwM07NByeSUCAwEAAaMhMB8wHQYDVR0OBBYEFFlcNuTYwI9W0tQ224K1gFJlMam0MA0GCSqGSIb3DQEBCwUAA4IBAQB5snl1KWOJALtAjLqD0mLPg1iElmZP82Lq1htLBt3XagwzU9CaeVeCQ7lTp+DXWzPa9nCLhsC3QyrV3/+oqNli8C6NpeqI8FqN2yQW/QMWN1m5jWDbmrWwtQzRUn/rh5KEb5m3zPB+tOC6e/2bV3QeQebxeW7lVMD0tSCviUg1MQf1l2gzuXQo60411YwqrXwk6GMkDOhFDQKDlMchO3oRbQkGbcP8UeiKAXjMeHfzbiBr+cWz8NYZEtxUEDYDjTpKrYCSMJBXpmgVJCZ00BswbksxJwaGqGMPpUKmCV671pf3m8nq3xyiHMDGuGwtbU+GE8kVx85menmp8+964nin"; private static final String PUBLIC_CERT = "MIIDdzCCAl+gAwIBAgIEbySuqTANBgkqhkiG9w0BAQsFADBsMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3duMB4XDTE1MDEyODIyMTYyMFoXDTE3MTAyNDIyMTYyMFowbDEQMA4GA1UEBhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAII/K9NNvXi9IySl7+l2zY/kKrGTtuR4WdCI0xLW/Jn4dLY7v1/HOnV4CC4ecFOzhdNFPtJkmEhP/q62CpmOYOKApXk3tfmm2rwEz9bWprVxgFGKnbrWlz61Z/cjLAlhD3IUj2ZRBquYgSXQPsYfXo1JmSWF5pZ9uh1FVqu9f4wvRqY20ZhUN+39F+1iaBsoqsrbXypCn1HgZkW1/9D9GZug1c3vB4wg1TwZZWRNGtxwoEhdK6dPrNcZ+6PdanVilWrbQFbBjY4wz8/7IMBzssoQ7Usmo8F1Piv0FGfaVeJqBrcAvbiBMpk8pT+27u6p8VyIX6LhGvnxIwM07NByeSUCAwEAAaMhMB8wHQYDVR0OBBYEFFlcNuTYwI9W0tQ224K1gFJlMam0MA0GCSqGSIb3DQEBCwUAA4IBAQB5snl1KWOJALtAjLqD0mLPg1iElmZP82Lq1htLBt3XagwzU9CaeVeCQ7lTp+DXWzPa9nCLhsC3QyrV3/+oqNli8C6NpeqI8FqN2yQW/QMWN1m5jWDbmrWwtQzRUn/rh5KEb5m3zPB+tOC6e/2bV3QeQebxeW7lVMD0tSCviUg1MQf1l2gzuXQo60411YwqrXwk6GMkDOhFDQKDlMchO3oRbQkGbcP8UeiKAXjMeHfzbiBr+cWz8NYZEtxUEDYDjTpKrYCSMJBXpmgVJCZ00BswbksxJwaGqGMPpUKmCV671pf3m8nq3xyiHMDGuGwtbU+GE8kVx85menmp8+964nin";
@BeforeClass
public static void initCrypto() {
CryptoIntegration.init(CryptoProvider.class.getClassLoader());
}
@Test @Test
public void testSaml20Signed() throws Exception { public void testSaml20Signed() throws Exception {

View file

@ -47,6 +47,17 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>${keycloak.crypto.artifactId}</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.freemarker</groupId> <groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId> <artifactId>freemarker</artifactId>

View file

@ -19,6 +19,8 @@ package org.keycloak.services.resources;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.util.BouncyIntegration;
import org.keycloak.common.util.Resteasy; import org.keycloak.common.util.Resteasy;
import org.keycloak.config.ConfigProviderFactory; import org.keycloak.config.ConfigProviderFactory;
import org.keycloak.exportimport.ExportImportManager; import org.keycloak.exportimport.ExportImportManager;
@ -90,6 +92,7 @@ public class KeycloakApplication extends Application {
logger.debugv("PlatformProvider: {0}", platform.getClass().getName()); logger.debugv("PlatformProvider: {0}", platform.getClass().getName());
logger.debugv("RestEasy provider: {0}", Resteasy.getProvider().getClass().getName()); logger.debugv("RestEasy provider: {0}", Resteasy.getProvider().getClass().getName());
CryptoIntegration.init(KeycloakApplication.class.getClassLoader());
loadConfig(); loadConfig();

View file

@ -6,12 +6,17 @@ import java.io.InputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import org.junit.ClassRule;
import org.keycloak.rule.CryptoInitRule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.PemUtils;
import org.keycloak.common.util.StreamUtil; import org.keycloak.common.util.StreamUtil;
public class CertificatePemIdentityExtractorTest { public class CertificatePemIdentityExtractorTest {
@ClassRule
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
@Test @Test
public void testExtractsCertInPemFormat() throws Exception { public void testExtractsCertInPemFormat() throws Exception {
InputStream is = getClass().getResourceAsStream("/certs/UPN-cert.pem"); InputStream is = getClass().getResourceAsStream("/certs/UPN-cert.pem");

View file

@ -21,15 +21,20 @@ import java.io.InputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import org.junit.Assert; import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.PemUtils;
import org.keycloak.common.util.StreamUtil; import org.keycloak.common.util.StreamUtil;
import org.keycloak.rule.CryptoInitRule;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class SubjectAltNameIdentityExtractorTest { public class SubjectAltNameIdentityExtractorTest {
@ClassRule
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
@Test @Test
public void testX509SubjectAltName_otherName() throws Exception { public void testX509SubjectAltName_otherName() throws Exception {
UserIdentityExtractor extractor = UserIdentityExtractor.getSubjectAltNameExtractor(0); UserIdentityExtractor extractor = UserIdentityExtractor.getSubjectAltNameExtractor(0);

View file

@ -32,14 +32,19 @@ import javax.ws.rs.core.Response;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.keycloak.common.util.CertificateUtils; import org.keycloak.common.util.CertificateUtils;
import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.PemUtils;
import org.keycloak.protocol.docker.installation.DockerComposeYamlInstallationProvider; import org.keycloak.protocol.docker.installation.DockerComposeYamlInstallationProvider;
import org.keycloak.rule.CryptoInitRule;
public class DockerComposeYamlInstallationProviderTest { public class DockerComposeYamlInstallationProviderTest {
@ClassRule
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
DockerComposeYamlInstallationProvider installationProvider; DockerComposeYamlInstallationProvider installationProvider;
static Certificate certificate; static Certificate certificate;

View file

@ -0,0 +1,16 @@
package org.keycloak.testsuite.util;
import org.junit.rules.ExternalResource;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.crypto.CryptoProvider;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class CryptoInitRule extends ExternalResource {
@Override
protected void before() throws Throwable {
CryptoIntegration.init(CryptoProvider.class.getClassLoader());
}
}

View file

@ -26,6 +26,7 @@ import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.ClassRule;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.model.TestTimedOutException; import org.junit.runners.model.TestTimedOutException;
import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.Keycloak;
@ -59,6 +60,7 @@ import org.keycloak.testsuite.auth.page.login.OIDCLogin;
import org.keycloak.testsuite.auth.page.login.UpdatePassword; import org.keycloak.testsuite.auth.page.login.UpdatePassword;
import org.keycloak.testsuite.client.KeycloakTestingClient; import org.keycloak.testsuite.client.KeycloakTestingClient;
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage; import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
import org.keycloak.testsuite.util.CryptoInitRule;
import org.keycloak.testsuite.util.DroneUtils; import org.keycloak.testsuite.util.DroneUtils;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.TestCleanup; import org.keycloak.testsuite.util.TestCleanup;
@ -109,6 +111,9 @@ public abstract class AbstractKeycloakTest {
protected Logger log = Logger.getLogger(this.getClass()); protected Logger log = Logger.getLogger(this.getClass());
@ClassRule
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
@ArquillianResource @ArquillianResource
protected SuiteContext suiteContext; protected SuiteContext suiteContext;

View file

@ -129,6 +129,10 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi-private</artifactId> <artifactId>keycloak-server-spi-private</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>${keycloak.crypto.artifactId}</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-ldap-federation</artifactId> <artifactId>keycloak-ldap-federation</artifactId>

View file

@ -29,8 +29,7 @@ import org.aesh.command.invocation.CommandInvocation;
import org.aesh.command.impl.registry.AeshCommandRegistryBuilder; import org.aesh.command.impl.registry.AeshCommandRegistryBuilder;
import org.aesh.command.registry.CommandRegistry; import org.aesh.command.registry.CommandRegistry;
import org.aesh.command.registry.CommandRegistryException; import org.aesh.command.registry.CommandRegistryException;
import org.keycloak.common.util.Base64; import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.hash.PasswordHashProvider; import org.keycloak.credential.hash.PasswordHashProvider;
import org.keycloak.credential.hash.PasswordHashProviderFactory; import org.keycloak.credential.hash.PasswordHashProviderFactory;
import org.keycloak.models.PasswordPolicy; import org.keycloak.models.PasswordPolicy;
@ -108,6 +107,8 @@ public class AddUser {
File addUserFile = getAddUserFile(this); File addUserFile = getAddUserFile(this);
CryptoIntegration.init(AddUser.class.getClassLoader());
createUser(addUserFile, realm, user, password, roles, iterations); createUser(addUserFile, realm, user, password, roles, iterations);
} }
} catch (Exception e){ } catch (Exception e){