diff --git a/adapters/oidc/adapter-core/pom.xml b/adapters/oidc/adapter-core/pom.xml index 0dd6a09a1ca..f206cedf85c 100755 --- a/adapters/oidc/adapter-core/pom.xml +++ b/adapters/oidc/adapter-core/pom.xml @@ -73,6 +73,10 @@ keycloak-core provided + + org.keycloak + ${keycloak.crypto.artifactId} + org.keycloak keycloak-authz-client diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java index af2a9610312..c83c0f073ff 100755 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java @@ -25,7 +25,9 @@ import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils; import org.keycloak.adapters.authorization.PolicyEnforcer; import org.keycloak.adapters.rotation.HardcodedPublicKeyLocator; import org.keycloak.adapters.rotation.JWKPublicKeyLocator; +import org.keycloak.common.crypto.CryptoIntegration; import org.keycloak.common.enums.SslRequired; +import org.keycloak.common.util.BouncyIntegration; import org.keycloak.common.util.PemUtils; import org.keycloak.enums.TokenStore; import org.keycloak.representations.adapters.config.AdapterConfig; @@ -181,6 +183,7 @@ public class KeycloakDeploymentBuilder { } public static KeycloakDeployment build(InputStream is) { + CryptoIntegration.init(KeycloakDeploymentBuilder.class.getClassLoader()); AdapterConfig adapterConfig = loadAdapterConfig(is); return new KeycloakDeploymentBuilder().internalBuild(adapterConfig); } diff --git a/boms/adapter/pom.xml b/boms/adapter/pom.xml index 33f9d04f355..91594eea972 100644 --- a/boms/adapter/pom.xml +++ b/boms/adapter/pom.xml @@ -44,6 +44,11 @@ keycloak-adapter-core ${project.version} + + org.keycloak + keycloak-crypto-default + ${project.version} + org.keycloak keycloak-adapter-spi diff --git a/common/src/main/java/org/keycloak/common/crypto/CryptoIntegration.java b/common/src/main/java/org/keycloak/common/crypto/CryptoIntegration.java new file mode 100644 index 00000000000..64afe01eb16 --- /dev/null +++ b/common/src/main/java/org/keycloak/common/crypto/CryptoIntegration.java @@ -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 Marek Posolda + */ +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 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); + } + } + +} diff --git a/core/src/main/java/org/keycloak/crypto/integration/CryptoProvider.java b/common/src/main/java/org/keycloak/common/crypto/CryptoProvider.java similarity index 56% rename from core/src/main/java/org/keycloak/crypto/integration/CryptoProvider.java rename to common/src/main/java/org/keycloak/common/crypto/CryptoProvider.java index 48694a346c3..7912a3ac2aa 100644 --- a/core/src/main/java/org/keycloak/crypto/integration/CryptoProvider.java +++ b/common/src/main/java/org/keycloak/common/crypto/CryptoProvider.java @@ -1,11 +1,9 @@ -package org.keycloak.crypto.integration; +package org.keycloak.common.crypto; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; -import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider; - /** * Abstraction to handle differences between the APIs for non-fips and fips mode * @@ -17,6 +15,14 @@ public interface CryptoProvider { * @return secureRandom implementation based on the available security algorithms according to environment (FIPS non-fips) */ 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 getAlgorithmProvider(Class clazz, String algorithm); } diff --git a/common/src/main/java/org/keycloak/common/crypto/CryptoProviderTypes.java b/common/src/main/java/org/keycloak/common/crypto/CryptoProviderTypes.java new file mode 100644 index 00000000000..0b9811f1ba1 --- /dev/null +++ b/common/src/main/java/org/keycloak/common/crypto/CryptoProviderTypes.java @@ -0,0 +1,11 @@ +package org.keycloak.common.crypto; + +/** + * @author Marek Posolda + */ +public class CryptoProviderTypes { + + public static final String BC_SECURITY_PROVIDER = "bc-provider"; + + public static final String AES_KEY_WRAP_ALGORITHM_PROVIDER = "aes-keywrap-alg"; +} diff --git a/common/src/main/java/org/keycloak/common/util/BouncyIntegration.java b/common/src/main/java/org/keycloak/common/util/BouncyIntegration.java index b3f32d25cf0..4447a25f86b 100755 --- a/common/src/main/java/org/keycloak/common/util/BouncyIntegration.java +++ b/common/src/main/java/org/keycloak/common/util/BouncyIntegration.java @@ -18,8 +18,9 @@ package org.keycloak.common.util; 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.Security; @@ -31,34 +32,20 @@ public class BouncyIntegration { 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(); private static String loadProvider() { - for (String providerClassName : providerClassNames) { - try { - Class providerClass = Class.forName(providerClassName, true, BouncyIntegration.class.getClassLoader()); - Constructor constructor = (Constructor) 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); - } + Provider provider = CryptoIntegration.getProvider().getAlgorithmProvider(Provider.class, CryptoProviderTypes.BC_SECURITY_PROVIDER); + if (provider == null) { + 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(); } } diff --git a/core/pom.xml b/core/pom.xml index 0ab99f34e44..1131a559fcb 100755 --- a/core/pom.xml +++ b/core/pom.xml @@ -89,6 +89,13 @@ ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + test-jar + + + org.apache.felix diff --git a/core/src/main/java/org/keycloak/crypto/integration/CryptoIntegration.java b/core/src/main/java/org/keycloak/crypto/integration/CryptoIntegration.java deleted file mode 100644 index 20bf664b1d2..00000000000 --- a/core/src/main/java/org/keycloak/crypto/integration/CryptoIntegration.java +++ /dev/null @@ -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 Marek Posolda - */ -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(); - } - -} diff --git a/core/src/main/java/org/keycloak/crypto/integration/DefaultCryptoProvider.java b/core/src/main/java/org/keycloak/crypto/integration/DefaultCryptoProvider.java deleted file mode 100644 index bd2bc8c6a1b..00000000000 --- a/core/src/main/java/org/keycloak/crypto/integration/DefaultCryptoProvider.java +++ /dev/null @@ -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 Marek Posolda - */ -public class DefaultCryptoProvider implements CryptoProvider { - - @Override - public SecureRandom getSecureRandom() throws NoSuchAlgorithmException { - return SecureRandom.getInstance("SHA1PRNG"); - } - - @Override - public JWEAlgorithmProvider getAesKeyWrapAlgorithmProvider() { - return new AesKeyWrapAlgorithmProvider(); - } -} diff --git a/core/src/main/java/org/keycloak/jose/jwe/JWERegistry.java b/core/src/main/java/org/keycloak/jose/jwe/JWERegistry.java index 5defa4df7c8..15f9e969e3f 100644 --- a/core/src/main/java/org/keycloak/jose/jwe/JWERegistry.java +++ b/core/src/main/java/org/keycloak/jose/jwe/JWERegistry.java @@ -20,7 +20,8 @@ package org.keycloak.jose.jwe; import java.util.HashMap; 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.JWEAlgorithmProvider; import org.keycloak.jose.jwe.alg.RsaKeyEncryption256JWEAlgorithmProvider; @@ -47,7 +48,7 @@ class JWERegistry { static { // Provider 'dir' just directly uses encryption keys for encrypt/decrypt content. 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_256, new RsaKeyEncryption256JWEAlgorithmProvider("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")); diff --git a/core/src/test/java/org/keycloak/KeyPairVerifierTest.java b/core/src/test/java/org/keycloak/KeyPairVerifierTest.java index da76a611b9c..db1296d2d0f 100644 --- a/core/src/test/java/org/keycloak/KeyPairVerifierTest.java +++ b/core/src/test/java/org/keycloak/KeyPairVerifierTest.java @@ -18,13 +18,21 @@ package org.keycloak; import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; 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 Stian Thorgersen */ -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 publicKey1 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"; diff --git a/core/src/test/java/org/keycloak/jose/JWETest.java b/core/src/test/java/org/keycloak/jose/JWETest.java index 918b59c617f..17918c9226e 100644 --- a/core/src/test/java/org/keycloak/jose/JWETest.java +++ b/core/src/test/java/org/keycloak/jose/JWETest.java @@ -25,7 +25,9 @@ import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.junit.Assert; +import org.junit.ClassRule; import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; import org.keycloak.common.util.Base64; 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.AesGcmJWEEncryptionProvider; 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 Marek Posolda */ -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'"; diff --git a/core/src/test/java/org/keycloak/jose/jwk/JWKTest.java b/core/src/test/java/org/keycloak/jose/jwk/JWKTest.java index 1666e0dd265..3a7694f30b2 100644 --- a/core/src/test/java/org/keycloak/jose/jwk/JWKTest.java +++ b/core/src/test/java/org/keycloak/jose/jwk/JWKTest.java @@ -19,13 +19,17 @@ package org.keycloak.jose.jwk; import java.util.Arrays; import java.util.List; + +import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.BouncyIntegration; import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.PemUtils; 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 java.nio.charset.StandardCharsets; @@ -47,9 +51,14 @@ import static org.junit.Assert.assertTrue; 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 Stian Thorgersen */ -public class JWKTest { +public abstract class JWKTest { + + @ClassRule + public static CryptoInitRule cryptoInitRule = new CryptoInitRule(); @Test public void publicRs256() throws Exception { diff --git a/core/src/test/java/org/keycloak/rule/CryptoInitRule.java b/core/src/test/java/org/keycloak/rule/CryptoInitRule.java new file mode 100644 index 00000000000..cfd63c155a7 --- /dev/null +++ b/core/src/test/java/org/keycloak/rule/CryptoInitRule.java @@ -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 Marek Posolda + */ +public class CryptoInitRule extends ExternalResource { + + @Override + protected void before() throws Throwable { + CryptoIntegration.init(CryptoProvider.class.getClassLoader()); + } +} diff --git a/crypto/default/pom.xml b/crypto/default/pom.xml new file mode 100644 index 00000000000..217bad4cdb4 --- /dev/null +++ b/crypto/default/pom.xml @@ -0,0 +1,70 @@ + + + + + + keycloak-crypto-parent + org.keycloak + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + keycloak-crypto-default + Keycloak Crypto Default + + + + + org.keycloak + keycloak-core + + + org.keycloak + keycloak-core + test + test-jar + + + + org.bouncycastle + bcprov-jdk15on + + + org.bouncycastle + bcpkix-jdk15on + + + org.jboss.logging + jboss-logging + provided + + + junit + junit + test + + + org.hamcrest + hamcrest + test + + + + \ No newline at end of file diff --git a/core/src/main/java/org/keycloak/jose/jwe/alg/AesKeyWrapAlgorithmProvider.java b/crypto/default/src/main/java/org/keycloak/crypto/def/AesKeyWrapAlgorithmProvider.java similarity index 95% rename from core/src/main/java/org/keycloak/jose/jwe/alg/AesKeyWrapAlgorithmProvider.java rename to crypto/default/src/main/java/org/keycloak/crypto/def/AesKeyWrapAlgorithmProvider.java index 24fe6342774..36fb50b8caa 100644 --- a/core/src/main/java/org/keycloak/jose/jwe/alg/AesKeyWrapAlgorithmProvider.java +++ b/crypto/default/src/main/java/org/keycloak/crypto/def/AesKeyWrapAlgorithmProvider.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.jose.jwe.alg; +package org.keycloak.crypto.def; import java.security.Key; @@ -23,6 +23,7 @@ import org.bouncycastle.crypto.Wrapper; import org.bouncycastle.crypto.engines.AESWrapEngine; import org.bouncycastle.crypto.params.KeyParameter; import org.keycloak.jose.jwe.JWEKeyStorage; +import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider; import org.keycloak.jose.jwe.enc.JWEEncryptionProvider; /** diff --git a/crypto/default/src/main/java/org/keycloak/crypto/def/DefaultCryptoProvider.java b/crypto/default/src/main/java/org/keycloak/crypto/def/DefaultCryptoProvider.java new file mode 100644 index 00000000000..765a8941b11 --- /dev/null +++ b/crypto/default/src/main/java/org/keycloak/crypto/def/DefaultCryptoProvider.java @@ -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 Marek Posolda + */ +public class DefaultCryptoProvider implements CryptoProvider { + + private Map> 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 getAlgorithmProvider(Class 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); + } +} diff --git a/crypto/default/src/main/resources/META-INF/services/org.keycloak.common.crypto.CryptoProvider b/crypto/default/src/main/resources/META-INF/services/org.keycloak.common.crypto.CryptoProvider new file mode 100644 index 00000000000..2f16ff81547 --- /dev/null +++ b/crypto/default/src/main/resources/META-INF/services/org.keycloak.common.crypto.CryptoProvider @@ -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 \ No newline at end of file diff --git a/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoJWETest.java b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoJWETest.java new file mode 100644 index 00000000000..e75dbfb7f50 --- /dev/null +++ b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoJWETest.java @@ -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 Marek Posolda + */ +public class DefaultCryptoJWETest extends JWETest { +} diff --git a/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoJWKTest.java b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoJWKTest.java new file mode 100644 index 00000000000..4ba6456e5ea --- /dev/null +++ b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoJWKTest.java @@ -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 Marek Posolda + */ +public class DefaultCryptoJWKTest extends JWKTest { +} diff --git a/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoKeyPairVerifierTest.java b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoKeyPairVerifierTest.java new file mode 100644 index 00000000000..1b0e005fc3c --- /dev/null +++ b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoKeyPairVerifierTest.java @@ -0,0 +1,11 @@ +package org.keycloak.crypto.def.test; + +import org.keycloak.KeyPairVerifierTest; + +/** + * Test with default security provider and non-fips bouncycastle + * + * @author Marek Posolda + */ +public class DefaultCryptoKeyPairVerifierTest extends KeyPairVerifierTest { +} diff --git a/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoUnitTest.java b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoUnitTest.java new file mode 100644 index 00000000000..907ab0a32c0 --- /dev/null +++ b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoUnitTest.java @@ -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 Marek Posolda + */ +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); + } +} diff --git a/crypto/fips1402/pom.xml b/crypto/fips1402/pom.xml index fd38706245e..0373eef80df 100644 --- a/crypto/fips1402/pom.xml +++ b/crypto/fips1402/pom.xml @@ -26,14 +26,32 @@ 4.0.0 - keycloak-fips1402 - Keycloak FIPS 140-2 Integration + keycloak-crypto-fips1402 + Keycloak Crypto FIPS 140-2 Integration org.keycloak keycloak-core + + + + org.bouncycastle + bcprov-jdk15on + + + org.bouncycastle + bcpkix-jdk15on + + + + + org.keycloak + keycloak-core + test + test-jar + org.bouncycastle diff --git a/crypto/fips1402/src/main/java/org/keycloak/crypto/fips/FIPS1402Provider.java b/crypto/fips1402/src/main/java/org/keycloak/crypto/fips/FIPS1402Provider.java index 481af1737e5..5e8c848a896 100644 --- a/crypto/fips1402/src/main/java/org/keycloak/crypto/fips/FIPS1402Provider.java +++ b/crypto/fips1402/src/main/java/org/keycloak/crypto/fips/FIPS1402Provider.java @@ -3,9 +3,13 @@ package org.keycloak.crypto.fips; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; 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.keycloak.jose.jwe.alg.JWEAlgorithmProvider; +import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider; +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 { + private Map> providers = new HashMap<>(); + + public FIPS1402Provider() { + providers.put(CryptoProviderTypes.BC_SECURITY_PROVIDER, BouncyCastleFipsProvider::new); + providers.put(CryptoProviderTypes.AES_KEY_WRAP_ALGORITHM_PROVIDER, FIPSAesKeyWrapAlgorithmProvider::new); + } + @Override public SecureRandom getSecureRandom() throws NoSuchAlgorithmException, NoSuchProviderException { return SecureRandom.getInstance("DEFAULT","BCFIPS"); } @Override - public JWEAlgorithmProvider getAesKeyWrapAlgorithmProvider() { - return new FIPSAesKeyWrapAlgorithmProvider(); + public T getAlgorithmProvider(Class 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); } } diff --git a/crypto/fips1402/src/main/resources/META-INF/services/org.keycloak.crypto.integration.CryptoProvider b/crypto/fips1402/src/main/resources/META-INF/services/org.keycloak.common.crypto.CryptoProvider similarity index 100% rename from crypto/fips1402/src/main/resources/META-INF/services/org.keycloak.crypto.integration.CryptoProvider rename to crypto/fips1402/src/main/resources/META-INF/services/org.keycloak.common.crypto.CryptoProvider diff --git a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402JWETest.java b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402JWETest.java new file mode 100644 index 00000000000..802e85b548a --- /dev/null +++ b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402JWETest.java @@ -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 Marek Posolda + */ +@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 { +} diff --git a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402JWKTest.java b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402JWKTest.java new file mode 100644 index 00000000000..182ef0f7da0 --- /dev/null +++ b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402JWKTest.java @@ -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 Marek Posolda + */ +@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 { +} diff --git a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402KeyPairVerifierTest.java b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402KeyPairVerifierTest.java new file mode 100644 index 00000000000..412234ffe35 --- /dev/null +++ b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402KeyPairVerifierTest.java @@ -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 Marek Posolda + */ +@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 { +} diff --git a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402UnitTest.java b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402UnitTest.java index 3798ebb27de..2d6336e6d86 100644 --- a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402UnitTest.java +++ b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402UnitTest.java @@ -1,25 +1,26 @@ package org.keycloak.crypto.fips.test; -import java.security.SecureRandom; - import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; +import org.keycloak.common.crypto.CryptoProviderTypes; 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.rule.CryptoInitRule; /** * @author Marek Posolda */ public class FIPS1402UnitTest { + @ClassRule + public static CryptoInitRule cryptoInitRule = new CryptoInitRule(); @Test 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); - - SecureRandom scr = CryptoIntegration.getProvider().getSecureRandom(); - Assert.assertEquals("BCFIPS", scr.getProvider().getName()); } } diff --git a/crypto/pom.xml b/crypto/pom.xml index 2881a99f947..077717570e1 100644 --- a/crypto/pom.xml +++ b/crypto/pom.xml @@ -32,5 +32,6 @@ fips1402 + default \ No newline at end of file diff --git a/distribution/feature-packs/adapter-feature-pack/pom.xml b/distribution/feature-packs/adapter-feature-pack/pom.xml index bcaa41d59cc..fac3f881553 100755 --- a/distribution/feature-packs/adapter-feature-pack/pom.xml +++ b/distribution/feature-packs/adapter-feature-pack/pom.xml @@ -58,6 +58,16 @@ + + org.keycloak + keycloak-crypto-default + + + * + * + + + org.keycloak keycloak-adapter-core diff --git a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-adapter-core/main/module.xml b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-adapter-core/main/module.xml index 14203f90802..1d89bc9e490 100755 --- a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-adapter-core/main/module.xml +++ b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-adapter-core/main/module.xml @@ -34,6 +34,7 @@ + diff --git a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-crypto-default/main/module.xml b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-crypto-default/main/module.xml new file mode 100644 index 00000000000..afcabe3cc9f --- /dev/null +++ b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-crypto-default/main/module.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/distribution/feature-packs/server-feature-pack-dependencies/pom.xml b/distribution/feature-packs/server-feature-pack-dependencies/pom.xml index a628c1df17a..97a4e627fe3 100644 --- a/distribution/feature-packs/server-feature-pack-dependencies/pom.xml +++ b/distribution/feature-packs/server-feature-pack-dependencies/pom.xml @@ -140,6 +140,16 @@ + + org.keycloak + keycloak-crypto-default + + + * + * + + + org.keycloak keycloak-js-adapter diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-crypto-default/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-crypto-default/main/module.xml new file mode 100644 index 00000000000..8cbcac41aac --- /dev/null +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-crypto-default/main/module.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml index 97340bedca7..f684415ff78 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml @@ -27,6 +27,7 @@ + diff --git a/distribution/galleon-feature-packs/server-galleon-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-crypto-default/main/module.xml b/distribution/galleon-feature-packs/server-galleon-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-crypto-default/main/module.xml new file mode 100644 index 00000000000..8cbcac41aac --- /dev/null +++ b/distribution/galleon-feature-packs/server-galleon-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-crypto-default/main/module.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/distribution/galleon-feature-packs/server-galleon-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml b/distribution/galleon-feature-packs/server-galleon-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml index 97340bedca7..f684415ff78 100755 --- a/distribution/galleon-feature-packs/server-galleon-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml +++ b/distribution/galleon-feature-packs/server-galleon-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml @@ -27,6 +27,7 @@ + diff --git a/distribution/galleon-feature-packs/server-galleon-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-adduser/main/module.xml b/distribution/galleon-feature-packs/server-galleon-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-adduser/main/module.xml index 88548016561..4b4e42ffd0c 100755 --- a/distribution/galleon-feature-packs/server-galleon-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-adduser/main/module.xml +++ b/distribution/galleon-feature-packs/server-galleon-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-adduser/main/module.xml @@ -29,6 +29,7 @@ + diff --git a/docs/fips.md b/docs/fips.md index 1f0d061927a..ed97e6d82c2 100644 --- a/docs/fips.md +++ b/docs/fips.md @@ -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 ``` 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: ``` diff --git a/pom.xml b/pom.xml index d7901ef7e2e..1d35863576b 100644 --- a/pom.xml +++ b/pom.xml @@ -1057,6 +1057,12 @@ keycloak-core ${project.version} + + org.keycloak + keycloak-core + test-jar + ${project.version} + org.keycloak keycloak-config-api @@ -1600,7 +1606,12 @@ org.keycloak - keycloak-fips1402 + keycloak-crypto-default + ${project.version} + + + org.keycloak + keycloak-crypto-fips1402 ${project.version} @@ -2124,5 +2135,29 @@ + + crypto-default + + + !fips140-2 + + + + keycloak-crypto-default + + + + + fips140-2 + + + fips140-2 + + + + keycloak-crypto-fips1402 + + + diff --git a/quarkus/runtime/pom.xml b/quarkus/runtime/pom.xml index 23fa53ed9f1..d42161a8ee5 100644 --- a/quarkus/runtime/pom.xml +++ b/quarkus/runtime/pom.xml @@ -141,6 +141,10 @@ + + org.keycloak + ${keycloak.crypto.artifactId} + org.keycloak keycloak-server-spi @@ -593,52 +597,6 @@ - - - crypto-default - - - !fips140-2 - - - - - org.bouncycastle - bcpkix-jdk15on - - - * - * - - - - - org.bouncycastle - bcprov-jdk15on - - - * - * - - - - - - - - fips140-2 - - - fips140-2 - - - - - org.keycloak - keycloak-fips1402 - - - diff --git a/saml-core/pom.xml b/saml-core/pom.xml index 7f17007259c..75a40c9528a 100755 --- a/saml-core/pom.xml +++ b/saml-core/pom.xml @@ -54,6 +54,11 @@ org.apache.santuario xmlsec + + org.keycloak + ${keycloak.crypto.artifactId} + test + junit junit diff --git a/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java b/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java index 5f7770e0669..accb35b62ab 100644 --- a/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java +++ b/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java @@ -19,9 +19,12 @@ package org.keycloak.saml.processing.core.parsers.saml; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; 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.DerUtils; import org.keycloak.common.util.StreamUtil; @@ -128,6 +131,11 @@ public class SAMLParserTest { @Rule public ExpectedException thrown = ExpectedException.none(); + @BeforeClass + public static void initCrypto() { + CryptoIntegration.init(CryptoProvider.class.getClassLoader()); + } + @Before public void initParser() { this.parser = SAMLParser.getInstance(); diff --git a/saml-core/src/test/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtilTest.java b/saml-core/src/test/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtilTest.java index 7155bb30486..a4310bf7c73 100644 --- a/saml-core/src/test/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtilTest.java +++ b/saml-core/src/test/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtilTest.java @@ -20,7 +20,11 @@ import org.bouncycastle.openssl.PEMKeyPair; import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; import org.bouncycastle.util.Arrays; +import org.junit.Before; +import org.junit.BeforeClass; 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.DerUtils; 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"; + @BeforeClass + public static void initCrypto() { + CryptoIntegration.init(CryptoProvider.class.getClassLoader()); + } + @Test public void testSaml20Signed() throws Exception { diff --git a/services/pom.xml b/services/pom.xml index 3e1e4fcb646..5110042cba1 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -47,6 +47,17 @@ org.keycloak keycloak-core + + org.keycloak + keycloak-core + test + test-jar + + + org.keycloak + ${keycloak.crypto.artifactId} + test + org.freemarker freemarker diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java index 7cb6993819a..c13e1eb52de 100644 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -19,6 +19,8 @@ package org.keycloak.services.resources; import com.fasterxml.jackson.core.type.TypeReference; import org.jboss.logging.Logger; 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.config.ConfigProviderFactory; import org.keycloak.exportimport.ExportImportManager; @@ -90,6 +92,7 @@ public class KeycloakApplication extends Application { logger.debugv("PlatformProvider: {0}", platform.getClass().getName()); logger.debugv("RestEasy provider: {0}", Resteasy.getProvider().getClass().getName()); + CryptoIntegration.init(KeycloakApplication.class.getClassLoader()); loadConfig(); diff --git a/services/src/test/java/org/keycloak/authentication/authenticators/x509/CertificatePemIdentityExtractorTest.java b/services/src/test/java/org/keycloak/authentication/authenticators/x509/CertificatePemIdentityExtractorTest.java index bc5df5ea78b..b0cab146a21 100644 --- a/services/src/test/java/org/keycloak/authentication/authenticators/x509/CertificatePemIdentityExtractorTest.java +++ b/services/src/test/java/org/keycloak/authentication/authenticators/x509/CertificatePemIdentityExtractorTest.java @@ -6,12 +6,17 @@ import java.io.InputStream; import java.nio.charset.Charset; import java.security.cert.X509Certificate; +import org.junit.ClassRule; +import org.keycloak.rule.CryptoInitRule; import org.junit.Test; import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.StreamUtil; public class CertificatePemIdentityExtractorTest { + @ClassRule + public static CryptoInitRule cryptoInitRule = new CryptoInitRule(); + @Test public void testExtractsCertInPemFormat() throws Exception { InputStream is = getClass().getResourceAsStream("/certs/UPN-cert.pem"); diff --git a/services/src/test/java/org/keycloak/authentication/authenticators/x509/SubjectAltNameIdentityExtractorTest.java b/services/src/test/java/org/keycloak/authentication/authenticators/x509/SubjectAltNameIdentityExtractorTest.java index c03a54d56a8..8052d3b16d2 100644 --- a/services/src/test/java/org/keycloak/authentication/authenticators/x509/SubjectAltNameIdentityExtractorTest.java +++ b/services/src/test/java/org/keycloak/authentication/authenticators/x509/SubjectAltNameIdentityExtractorTest.java @@ -21,15 +21,20 @@ import java.io.InputStream; import java.nio.charset.Charset; import java.security.cert.X509Certificate; import org.junit.Assert; +import org.junit.ClassRule; import org.junit.Test; import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.StreamUtil; +import org.keycloak.rule.CryptoInitRule; /** * @author Marek Posolda */ public class SubjectAltNameIdentityExtractorTest { + @ClassRule + public static CryptoInitRule cryptoInitRule = new CryptoInitRule(); + @Test public void testX509SubjectAltName_otherName() throws Exception { UserIdentityExtractor extractor = UserIdentityExtractor.getSubjectAltNameExtractor(0); diff --git a/services/src/test/java/org/keycloak/procotol/docker/installation/DockerComposeYamlInstallationProviderTest.java b/services/src/test/java/org/keycloak/procotol/docker/installation/DockerComposeYamlInstallationProviderTest.java index 7207b1fbc4c..d007be5dbc4 100644 --- a/services/src/test/java/org/keycloak/procotol/docker/installation/DockerComposeYamlInstallationProviderTest.java +++ b/services/src/test/java/org/keycloak/procotol/docker/installation/DockerComposeYamlInstallationProviderTest.java @@ -32,14 +32,19 @@ import javax.ws.rs.core.Response; import org.apache.commons.io.FileUtils; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Ignore; import org.junit.Test; import org.keycloak.common.util.CertificateUtils; import org.keycloak.common.util.PemUtils; import org.keycloak.protocol.docker.installation.DockerComposeYamlInstallationProvider; +import org.keycloak.rule.CryptoInitRule; public class DockerComposeYamlInstallationProviderTest { + @ClassRule + public static CryptoInitRule cryptoInitRule = new CryptoInitRule(); + DockerComposeYamlInstallationProvider installationProvider; static Certificate certificate; diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/CryptoInitRule.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/CryptoInitRule.java new file mode 100644 index 00000000000..a818ffe47d3 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/CryptoInitRule.java @@ -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 Marek Posolda + */ +public class CryptoInitRule extends ExternalResource { + + @Override + protected void before() throws Throwable { + CryptoIntegration.init(CryptoProvider.class.getClassLoader()); + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java index 16b32dbefd8..4c36e24a4a1 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java @@ -26,6 +26,7 @@ import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.logging.Logger; import org.junit.After; import org.junit.Before; +import org.junit.ClassRule; import org.junit.runner.RunWith; import org.junit.runners.model.TestTimedOutException; 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.client.KeycloakTestingClient; import org.keycloak.testsuite.pages.LoginPasswordUpdatePage; +import org.keycloak.testsuite.util.CryptoInitRule; import org.keycloak.testsuite.util.DroneUtils; import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.TestCleanup; @@ -109,6 +111,9 @@ public abstract class AbstractKeycloakTest { protected Logger log = Logger.getLogger(this.getClass()); + @ClassRule + public static CryptoInitRule cryptoInitRule = new CryptoInitRule(); + @ArquillianResource protected SuiteContext suiteContext; diff --git a/testsuite/utils/pom.xml b/testsuite/utils/pom.xml index 8f345ccc92d..61d2cc35b27 100755 --- a/testsuite/utils/pom.xml +++ b/testsuite/utils/pom.xml @@ -129,6 +129,10 @@ org.keycloak keycloak-server-spi-private + + org.keycloak + ${keycloak.crypto.artifactId} + org.keycloak keycloak-ldap-federation diff --git a/wildfly/adduser/src/main/java/org/keycloak/wildfly/adduser/AddUser.java b/wildfly/adduser/src/main/java/org/keycloak/wildfly/adduser/AddUser.java index f373730915c..e2113a56ba5 100644 --- a/wildfly/adduser/src/main/java/org/keycloak/wildfly/adduser/AddUser.java +++ b/wildfly/adduser/src/main/java/org/keycloak/wildfly/adduser/AddUser.java @@ -29,8 +29,7 @@ import org.aesh.command.invocation.CommandInvocation; import org.aesh.command.impl.registry.AeshCommandRegistryBuilder; import org.aesh.command.registry.CommandRegistry; import org.aesh.command.registry.CommandRegistryException; -import org.keycloak.common.util.Base64; -import org.keycloak.credential.CredentialModel; +import org.keycloak.common.crypto.CryptoIntegration; import org.keycloak.credential.hash.PasswordHashProvider; import org.keycloak.credential.hash.PasswordHashProviderFactory; import org.keycloak.models.PasswordPolicy; @@ -108,6 +107,8 @@ public class AddUser { File addUserFile = getAddUserFile(this); + CryptoIntegration.init(AddUser.class.getClassLoader()); + createUser(addUserFile, realm, user, password, roles, iterations); } } catch (Exception e){