diff --git a/common/src/main/java/org/keycloak/common/crypto/CryptoConstants.java b/common/src/main/java/org/keycloak/common/crypto/CryptoConstants.java new file mode 100644 index 0000000000..a8d55144d1 --- /dev/null +++ b/common/src/main/java/org/keycloak/common/crypto/CryptoConstants.java @@ -0,0 +1,19 @@ +package org.keycloak.common.crypto; + +/** + * @author Marek Posolda + */ +public class CryptoConstants { + + // JWE algorithms + public static final String A128KW = "A128KW"; + public static final String RSA1_5 = "RSA1_5"; + public static final String RSA_OAEP = "RSA-OAEP"; + public static final String RSA_OAEP_256 = "RSA-OAEP-256"; + + /** Name of Java security provider used with non-fips BouncyCastle. Should be used in non-FIPS environment */ + public static final String BC_PROVIDER_ID = "BC"; + + /** Name of Java security provider used with fips BouncyCastle. Should be used in FIPS environment */ + public static final String BCFIPS_PROVIDER_ID = "BCFIPS"; +} diff --git a/common/src/main/java/org/keycloak/common/crypto/CryptoProvider.java b/common/src/main/java/org/keycloak/common/crypto/CryptoProvider.java index 4d6f47d169..bffb7eaa7f 100644 --- a/common/src/main/java/org/keycloak/common/crypto/CryptoProvider.java +++ b/common/src/main/java/org/keycloak/common/crypto/CryptoProvider.java @@ -1,5 +1,6 @@ package org.keycloak.common.crypto; +import java.security.Provider; import java.security.spec.ECParameterSpec; /** @@ -9,6 +10,11 @@ import java.security.spec.ECParameterSpec; */ public interface CryptoProvider { + /** + * @return BouncyCastle security provider. Can be either non-FIPS or FIPS based provider + */ + Provider getBouncyCastleProvider(); + /** * Get some algorithm provider implementation. Returned implementation can be dependent according to if we have * non-fips bouncycastle or fips bouncycastle on the classpath. @@ -25,7 +31,7 @@ public interface CryptoProvider { * * @return */ - public CertificateUtilsProvider getCertificateUtils(); + CertificateUtilsProvider getCertificateUtils(); /** @@ -34,7 +40,7 @@ public interface CryptoProvider { * * @return */ - public PemUtilsProvider getPemUtils(); + PemUtilsProvider getPemUtils(); /** @@ -43,6 +49,6 @@ public interface CryptoProvider { * @param curveName * @return */ - public ECParameterSpec createECParams(String curveName); + ECParameterSpec createECParams(String curveName); } diff --git a/common/src/main/java/org/keycloak/common/crypto/CryptoProviderTypes.java b/common/src/main/java/org/keycloak/common/crypto/CryptoProviderTypes.java deleted file mode 100644 index 0b9811f1ba..0000000000 --- a/common/src/main/java/org/keycloak/common/crypto/CryptoProviderTypes.java +++ /dev/null @@ -1,11 +0,0 @@ -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 4447a25f86..6022d936b5 100755 --- a/common/src/main/java/org/keycloak/common/util/BouncyIntegration.java +++ b/common/src/main/java/org/keycloak/common/util/BouncyIntegration.java @@ -19,7 +19,7 @@ package org.keycloak.common.util; import org.jboss.logging.Logger; import org.keycloak.common.crypto.CryptoIntegration; -import org.keycloak.common.crypto.CryptoProviderTypes; +import org.keycloak.common.crypto.CryptoConstants; import java.security.Provider; import java.security.Security; @@ -35,7 +35,7 @@ public class BouncyIntegration { public static final String PROVIDER = loadProvider(); private static String loadProvider() { - Provider provider = CryptoIntegration.getProvider().getAlgorithmProvider(Provider.class, CryptoProviderTypes.BC_SECURITY_PROVIDER); + Provider provider = CryptoIntegration.getProvider().getBouncyCastleProvider(); if (provider == null) { throw new RuntimeException("Failed to load required security provider: BouncyCastleProvider or BouncyCastleFipsProvider"); } diff --git a/common/src/main/java/org/keycloak/common/util/CertificateUtils.java b/common/src/main/java/org/keycloak/common/util/CertificateUtils.java index 9dca0728f3..1a4f3585e1 100755 --- a/common/src/main/java/org/keycloak/common/util/CertificateUtils.java +++ b/common/src/main/java/org/keycloak/common/util/CertificateUtils.java @@ -30,10 +30,6 @@ import org.keycloak.common.crypto.CryptoIntegration; */ public class CertificateUtils { - static { - CryptoIntegration.init(ClassLoader.getSystemClassLoader()); - } - /** * Generates version 3 {@link java.security.cert.X509Certificate}. diff --git a/common/src/main/java/org/keycloak/common/util/Environment.java b/common/src/main/java/org/keycloak/common/util/Environment.java index 4f9d976d64..02aa6da79d 100644 --- a/common/src/main/java/org/keycloak/common/util/Environment.java +++ b/common/src/main/java/org/keycloak/common/util/Environment.java @@ -43,6 +43,13 @@ public class Environment { * @return true if java is FIPS mode */ public static boolean isJavaInFipsMode() { + // Check if FIPS explicitly enabled by system property + String property = System.getProperty("com.redhat.fips"); + if (property != null) { + return Boolean.parseBoolean(property); + } + + // Otherwise try to auto-detect for (Provider provider : Security.getProviders()) { if (provider.getName().equals("BCFIPS")) continue; // Ignore BCFIPS provider for the detection as we may register it programatically if (provider.getName().toUpperCase().contains("FIPS")) return true; diff --git a/common/src/main/java/org/keycloak/common/util/PemUtils.java b/common/src/main/java/org/keycloak/common/util/PemUtils.java index f0bfbd2838..0029150462 100755 --- a/common/src/main/java/org/keycloak/common/util/PemUtils.java +++ b/common/src/main/java/org/keycloak/common/util/PemUtils.java @@ -37,10 +37,6 @@ public class PemUtils { public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----"; public static final String END_CERT = "-----END CERTIFICATE-----"; - static { - CryptoIntegration.init(ClassLoader.getSystemClassLoader()); - } - /** * Decode a X509 Certificate from a PEM string * diff --git a/core/src/main/java/org/keycloak/jose/jwe/JWEConstants.java b/core/src/main/java/org/keycloak/jose/jwe/JWEConstants.java index 6cd9279c48..ad33e73f7b 100644 --- a/core/src/main/java/org/keycloak/jose/jwe/JWEConstants.java +++ b/core/src/main/java/org/keycloak/jose/jwe/JWEConstants.java @@ -17,16 +17,18 @@ package org.keycloak.jose.jwe; +import org.keycloak.common.crypto.CryptoConstants; + /** * @author Marek Posolda */ public class JWEConstants { - public static final String DIR = "dir"; - public static final String A128KW = "A128KW"; - public static final String RSA1_5 = "RSA1_5"; - public static final String RSA_OAEP = "RSA-OAEP"; - public static final String RSA_OAEP_256 = "RSA-OAEP-256"; + public static final String DIRECT = "dir"; + public static final String A128KW = CryptoConstants.A128KW; + public static final String RSA1_5 = CryptoConstants.RSA1_5; + public static final String RSA_OAEP = CryptoConstants.RSA_OAEP; + public static final String RSA_OAEP_256 = CryptoConstants.RSA_OAEP_256; public static final String A128CBC_HS256 = "A128CBC-HS256"; public static final String A192CBC_HS384 = "A192CBC-HS384"; 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 15f9e969e3..dc9aa1f80e 100644 --- a/core/src/main/java/org/keycloak/jose/jwe/JWERegistry.java +++ b/core/src/main/java/org/keycloak/jose/jwe/JWERegistry.java @@ -21,11 +21,8 @@ import java.util.HashMap; import java.util.Map; 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; -import org.keycloak.jose.jwe.alg.RsaKeyEncryptionJWEAlgorithmProvider; import org.keycloak.jose.jwe.enc.AesCbcHmacShaEncryptionProvider; import org.keycloak.jose.jwe.enc.AesGcmJWEEncryptionProvider; import org.keycloak.jose.jwe.enc.JWEEncryptionProvider; @@ -36,23 +33,11 @@ import org.keycloak.jose.jwe.enc.JWEEncryptionProvider; */ class JWERegistry { - // https://tools.ietf.org/html/rfc7518#page-12 + // https://tools.ietf.org/html/rfc7518#page-22 // Registry not pluggable for now. Just supported algorithms included private static final Map ENC_PROVIDERS = new HashMap<>(); - // https://tools.ietf.org/html/rfc7518#page-22 - // Registry not pluggable for now. Just supported algorithms included - private static final Map ALG_PROVIDERS = new HashMap<>(); - - 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().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")); - - ENC_PROVIDERS.put(JWEConstants.A256GCM, new AesGcmJWEEncryptionProvider(JWEConstants.A256GCM)); ENC_PROVIDERS.put(JWEConstants.A128CBC_HS256, new AesCbcHmacShaEncryptionProvider.Aes128CbcHmacSha256Provider()); ENC_PROVIDERS.put(JWEConstants.A192CBC_HS384, new AesCbcHmacShaEncryptionProvider.Aes192CbcHmacSha384Provider()); @@ -61,7 +46,12 @@ class JWERegistry { static JWEAlgorithmProvider getAlgProvider(String alg) { - return ALG_PROVIDERS.get(alg); + // https://tools.ietf.org/html/rfc7518#page-12 + if (JWEConstants.DIRECT.equals(alg)) { + return new DirectAlgorithmProvider(); + } else { + return CryptoIntegration.getProvider().getAlgorithmProvider(JWEAlgorithmProvider.class, alg); + } } diff --git a/core/src/main/java/org/keycloak/jose/jwe/alg/KeyEncryptionJWEAlgorithmProvider.java b/core/src/main/java/org/keycloak/jose/jwe/alg/KeyEncryptionJWEAlgorithmProvider.java deleted file mode 100644 index 778a77d47e..0000000000 --- a/core/src/main/java/org/keycloak/jose/jwe/alg/KeyEncryptionJWEAlgorithmProvider.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2018 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.jose.jwe.alg; - -import java.security.Key; - -import javax.crypto.Cipher; - -import org.keycloak.jose.jwe.JWEKeyStorage; -import org.keycloak.jose.jwe.enc.JWEEncryptionProvider; - -public abstract class KeyEncryptionJWEAlgorithmProvider implements JWEAlgorithmProvider { - - @Override - public byte[] decodeCek(byte[] encodedCek, Key privateKey) throws Exception { - Cipher cipher = getCipherProvider(); - cipher.init(Cipher.DECRYPT_MODE, privateKey); - return cipher.doFinal(encodedCek); - } - - @Override - public byte[] encodeCek(JWEEncryptionProvider encryptionProvider, JWEKeyStorage keyStorage, Key publicKey) throws Exception { - Cipher cipher = getCipherProvider(); - cipher.init(Cipher.ENCRYPT_MODE, publicKey); - byte[] cekBytes = keyStorage.getCekBytes(); - return cipher.doFinal(cekBytes); - } - - protected abstract Cipher getCipherProvider() throws Exception; - -} diff --git a/core/src/main/java/org/keycloak/jose/jwe/alg/RsaKeyEncryption256JWEAlgorithmProvider.java b/core/src/main/java/org/keycloak/jose/jwe/alg/RsaKeyEncryption256JWEAlgorithmProvider.java deleted file mode 100644 index c5b06e33aa..0000000000 --- a/core/src/main/java/org/keycloak/jose/jwe/alg/RsaKeyEncryption256JWEAlgorithmProvider.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2018 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.jose.jwe.alg; - -import org.keycloak.jose.jwe.JWEKeyStorage; -import org.keycloak.jose.jwe.enc.JWEEncryptionProvider; - -import javax.crypto.Cipher; -import javax.crypto.spec.OAEPParameterSpec; -import javax.crypto.spec.PSource; -import java.security.AlgorithmParameters; -import java.security.Key; -import java.security.spec.AlgorithmParameterSpec; -import java.security.spec.MGF1ParameterSpec; - -public class RsaKeyEncryption256JWEAlgorithmProvider extends KeyEncryptionJWEAlgorithmProvider { - - private final String jcaAlgorithmName; - - public RsaKeyEncryption256JWEAlgorithmProvider(String jcaAlgorithmName) { - this.jcaAlgorithmName = jcaAlgorithmName; - } - - @Override - protected Cipher getCipherProvider() throws Exception { - return Cipher.getInstance(jcaAlgorithmName); - } - - @Override - public byte[] decodeCek(byte[] encodedCek, Key privateKey) throws Exception { - AlgorithmParameters algp = AlgorithmParameters.getInstance("OAEP"); - AlgorithmParameterSpec paramSpec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, - PSource.PSpecified.DEFAULT); - algp.init(paramSpec); - Cipher cipher = getCipherProvider(); - cipher.init(Cipher.DECRYPT_MODE, privateKey, algp); - return cipher.doFinal(encodedCek); - } - - @Override - public byte[] encodeCek(JWEEncryptionProvider encryptionProvider, JWEKeyStorage keyStorage, Key publicKey) - throws Exception { - AlgorithmParameters algp = AlgorithmParameters.getInstance("OAEP"); - AlgorithmParameterSpec paramSpec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, - PSource.PSpecified.DEFAULT); - algp.init(paramSpec); - Cipher cipher = getCipherProvider(); - cipher.init(Cipher.ENCRYPT_MODE, publicKey, algp); - byte[] cekBytes = keyStorage.getCekBytes(); - return cipher.doFinal(cekBytes); - } -} diff --git a/core/src/main/java/org/keycloak/jose/jwe/alg/RsaKeyEncryptionJWEAlgorithmProvider.java b/core/src/main/java/org/keycloak/jose/jwe/alg/RsaKeyEncryptionJWEAlgorithmProvider.java deleted file mode 100644 index c6909027dc..0000000000 --- a/core/src/main/java/org/keycloak/jose/jwe/alg/RsaKeyEncryptionJWEAlgorithmProvider.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2018 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.jose.jwe.alg; - -import javax.crypto.Cipher; - -public class RsaKeyEncryptionJWEAlgorithmProvider extends KeyEncryptionJWEAlgorithmProvider { - - private final String jcaAlgorithmName; - - public RsaKeyEncryptionJWEAlgorithmProvider(String jcaAlgorithmName) { - this.jcaAlgorithmName = jcaAlgorithmName; - } - - @Override - protected Cipher getCipherProvider() throws Exception { - return Cipher.getInstance(jcaAlgorithmName); - } - -} diff --git a/core/src/main/java/org/keycloak/util/TokenUtil.java b/core/src/main/java/org/keycloak/util/TokenUtil.java index 587e1ea8a5..5e48b65c57 100644 --- a/core/src/main/java/org/keycloak/util/TokenUtil.java +++ b/core/src/main/java/org/keycloak/util/TokenUtil.java @@ -203,7 +203,7 @@ public class TokenUtil { default: throw new IllegalArgumentException("Bad size for Encryption key: " + aesKey + ". Valid sizes are 16, 24, 32."); } - JWEHeader jweHeader = new JWEHeader(JWEConstants.DIR, encAlgorithm, null); + JWEHeader jweHeader = new JWEHeader(JWEConstants.DIRECT, encAlgorithm, null); JWE jwe = new JWE() .header(jweHeader) .content(contentBytes); diff --git a/core/src/test/java/org/keycloak/jose/JWETest.java b/core/src/test/java/org/keycloak/jose/JWETest.java index 1bb19c1082..220478a015 100644 --- a/core/src/test/java/org/keycloak/jose/JWETest.java +++ b/core/src/test/java/org/keycloak/jose/JWETest.java @@ -21,6 +21,7 @@ import org.junit.Assert; import org.junit.ClassRule; import org.junit.Ignore; import org.junit.Test; +import org.keycloak.common.crypto.CryptoIntegration; import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.KeyUtils; import org.keycloak.jose.jwe.JWE; @@ -30,7 +31,6 @@ import org.keycloak.jose.jwe.JWEHeader; import org.keycloak.jose.jwe.JWEKeyStorage; import org.keycloak.jose.jwe.JWEUtils; import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider; -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; @@ -80,7 +80,7 @@ public abstract class JWETest { private void testDirectEncryptAndDecrypt(Key aesKey, Key hmacKey, String encAlgorithm, String payload, boolean sysout) throws Exception { - JWEHeader jweHeader = new JWEHeader(JWEConstants.DIR, encAlgorithm, null); + JWEHeader jweHeader = new JWEHeader(JWEConstants.DIRECT, encAlgorithm, null); JWE jwe = new JWE() .header(jweHeader) .content(payload.getBytes(StandardCharsets.UTF_8)); @@ -273,7 +273,7 @@ public abstract class JWETest { private void testKeyEncryption_ContentEncryptionAesGcm(String jweAlgorithmName, String jweEncryptionName) throws Exception { // generate key pair for KEK KeyPair keyPair = KeyUtils.generateRsaKeyPair(2048); - JWEAlgorithmProvider jweAlgorithmProvider = new RsaKeyEncryptionJWEAlgorithmProvider(getJcaAlgorithmName(jweAlgorithmName)); + JWEAlgorithmProvider jweAlgorithmProvider = CryptoIntegration.getProvider().getAlgorithmProvider(JWEAlgorithmProvider.class, jweAlgorithmName); JWEEncryptionProvider jweEncryptionProvider = new AesGcmJWEEncryptionProvider(jweEncryptionName); JWEHeader jweHeader = new JWEHeader(jweAlgorithmName, jweEncryptionName, null); @@ -306,7 +306,7 @@ public abstract class JWETest { final SecretKey aesKey = new SecretKeySpec(AES_128_KEY, "AES"); final SecretKey hmacKey = new SecretKeySpec(HMAC_SHA256_KEY, "HMACSHA2"); - JWEAlgorithmProvider jweAlgorithmProvider = new RsaKeyEncryptionJWEAlgorithmProvider(getJcaAlgorithmName(jweAlgorithmName)); + JWEAlgorithmProvider jweAlgorithmProvider = CryptoIntegration.getProvider().getAlgorithmProvider(JWEAlgorithmProvider.class, jweAlgorithmName); JWEEncryptionProvider jweEncryptionProvider = new AesCbcHmacShaJWEEncryptionProvider(jweEncryptionName); JWEHeader jweHeader = new JWEHeader(jweAlgorithmName, jweEncryptionName, null); @@ -339,15 +339,4 @@ public abstract class JWETest { Assert.assertEquals(PAYLOAD, decodedContent); } - private String getJcaAlgorithmName(String jweAlgorithmName) { - String jcaAlgorithmName = null; - if (JWEConstants.RSA1_5.equals(jweAlgorithmName)) { - jcaAlgorithmName = "RSA/ECB/PKCS1Padding"; - } else if (JWEConstants.RSA_OAEP.equals(jweAlgorithmName)) { - jcaAlgorithmName = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"; - } else if (JWEConstants.RSA_OAEP_256.equals(jweAlgorithmName)) { - jcaAlgorithmName = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; - } - return jcaAlgorithmName; - } } 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 index 9f3f91ae0b..57b46910c4 100644 --- a/crypto/default/src/main/java/org/keycloak/crypto/def/DefaultCryptoProvider.java +++ b/crypto/default/src/main/java/org/keycloak/crypto/def/DefaultCryptoProvider.java @@ -1,5 +1,7 @@ package org.keycloak.crypto.def; +import java.security.Provider; +import java.security.Security; import java.security.spec.ECParameterSpec; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -9,7 +11,7 @@ import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; import org.bouncycastle.jce.spec.ECNamedCurveSpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.keycloak.common.crypto.CryptoProvider; -import org.keycloak.common.crypto.CryptoProviderTypes; +import org.keycloak.common.crypto.CryptoConstants; import org.keycloak.common.crypto.CertificateUtilsProvider; import org.keycloak.common.crypto.PemUtilsProvider; @@ -18,18 +20,33 @@ import org.keycloak.common.crypto.PemUtilsProvider; */ public class DefaultCryptoProvider implements CryptoProvider { + private final BouncyCastleProvider bcProvider; + private Map providers = new ConcurrentHashMap<>(); public DefaultCryptoProvider() { - providers.put(CryptoProviderTypes.BC_SECURITY_PROVIDER, new BouncyCastleProvider()); - providers.put(CryptoProviderTypes.AES_KEY_WRAP_ALGORITHM_PROVIDER, new AesKeyWrapAlgorithmProvider()); + // Make sure to instantiate this only once due it is expensive. And skip registration if already available in Java security providers (EG. due explicitly configured in java security file) + BouncyCastleProvider existingBc = (BouncyCastleProvider) Security.getProvider(CryptoConstants.BC_PROVIDER_ID); + this.bcProvider = existingBc == null ? new BouncyCastleProvider() : existingBc; + + providers.put(CryptoConstants.A128KW, new AesKeyWrapAlgorithmProvider()); + providers.put(CryptoConstants.RSA1_5, new DefaultRsaKeyEncryptionJWEAlgorithmProvider("RSA/ECB/PKCS1Padding")); + providers.put(CryptoConstants.RSA_OAEP, new DefaultRsaKeyEncryptionJWEAlgorithmProvider("RSA/ECB/OAEPWithSHA-1AndMGF1Padding")); + providers.put(CryptoConstants.RSA_OAEP_256, new DefaultRsaKeyEncryption256JWEAlgorithmProvider("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")); } + @Override - public T getAlgorithmProvider(Class clazz, String algorithm) { - Object o = providers.get(algorithm); + public Provider getBouncyCastleProvider() { + return bcProvider; + } + + + @Override + public T getAlgorithmProvider(Class clazz, String algorithmType) { + Object o = providers.get(algorithmType); if (o == null) { - throw new IllegalArgumentException("Not found provider of algorithm: " + algorithm); + throw new IllegalArgumentException("Not found provider of algorithm type: " + algorithmType); } return clazz.cast(o); } diff --git a/crypto/default/src/main/java/org/keycloak/crypto/def/DefaultRsaKeyEncryption256JWEAlgorithmProvider.java b/crypto/default/src/main/java/org/keycloak/crypto/def/DefaultRsaKeyEncryption256JWEAlgorithmProvider.java new file mode 100644 index 0000000000..54878f0231 --- /dev/null +++ b/crypto/default/src/main/java/org/keycloak/crypto/def/DefaultRsaKeyEncryption256JWEAlgorithmProvider.java @@ -0,0 +1,27 @@ +package org.keycloak.crypto.def; + +import java.security.AlgorithmParameters; +import java.security.Key; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.MGF1ParameterSpec; + +import javax.crypto.Cipher; +import javax.crypto.spec.OAEPParameterSpec; +import javax.crypto.spec.PSource; + + +public class DefaultRsaKeyEncryption256JWEAlgorithmProvider extends DefaultRsaKeyEncryptionJWEAlgorithmProvider { + + public DefaultRsaKeyEncryption256JWEAlgorithmProvider(String jcaAlgorithmName) { + super(jcaAlgorithmName); + } + + @Override + protected void initCipher(Cipher cipher, int mode, Key key) throws Exception { + AlgorithmParameters algp = AlgorithmParameters.getInstance("OAEP"); + AlgorithmParameterSpec paramSpec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, + PSource.PSpecified.DEFAULT); + algp.init(paramSpec); + cipher.init(mode, key, algp); + } +} diff --git a/crypto/default/src/main/java/org/keycloak/crypto/def/DefaultRsaKeyEncryptionJWEAlgorithmProvider.java b/crypto/default/src/main/java/org/keycloak/crypto/def/DefaultRsaKeyEncryptionJWEAlgorithmProvider.java new file mode 100644 index 0000000000..b0bc523e7c --- /dev/null +++ b/crypto/default/src/main/java/org/keycloak/crypto/def/DefaultRsaKeyEncryptionJWEAlgorithmProvider.java @@ -0,0 +1,40 @@ +package org.keycloak.crypto.def; + +import java.security.Key; + +import javax.crypto.Cipher; +import org.keycloak.jose.jwe.JWEKeyStorage; +import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider; +import org.keycloak.jose.jwe.enc.JWEEncryptionProvider; + +public class DefaultRsaKeyEncryptionJWEAlgorithmProvider implements JWEAlgorithmProvider { + + private final String jcaAlgorithmName; + + public DefaultRsaKeyEncryptionJWEAlgorithmProvider(String jcaAlgorithmName) { + this.jcaAlgorithmName = jcaAlgorithmName; + } + + @Override + public byte[] decodeCek(byte[] encodedCek, Key privateKey) throws Exception { + Cipher cipher = getCipherProvider(); + initCipher(cipher, Cipher.DECRYPT_MODE, privateKey); + return cipher.doFinal(encodedCek); + } + + @Override + public byte[] encodeCek(JWEEncryptionProvider encryptionProvider, JWEKeyStorage keyStorage, Key publicKey) throws Exception { + Cipher cipher = getCipherProvider(); + initCipher(cipher, Cipher.ENCRYPT_MODE, publicKey); + byte[] cekBytes = keyStorage.getCekBytes(); + return cipher.doFinal(cekBytes); + } + + private Cipher getCipherProvider() throws Exception { + return Cipher.getInstance(jcaAlgorithmName); + } + + protected void initCipher(Cipher cipher, int mode, Key key) throws Exception { + cipher.init(mode, key); + } +} diff --git a/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoHmacTest.java b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoHmacTest.java index fe7c6f25a6..fa9cae6884 100644 --- a/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoHmacTest.java +++ b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoHmacTest.java @@ -1,5 +1,8 @@ package org.keycloak.crypto.def.test; +import org.junit.Assume; +import org.junit.Before; +import org.keycloak.common.util.Environment; import org.keycloak.jose.HmacTest; @@ -8,4 +11,10 @@ import org.keycloak.jose.HmacTest; * */ public class DefaultCryptoHmacTest extends HmacTest { + + @Before + public void before() { + // Run this test just if java is not in FIPS mode + Assume.assumeFalse("Java is in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode()); + } } diff --git a/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoJWKSUtilsTest.java b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoJWKSUtilsTest.java index 8a24cd859a..06785c34a8 100644 --- a/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoJWKSUtilsTest.java +++ b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoJWKSUtilsTest.java @@ -1,5 +1,8 @@ package org.keycloak.crypto.def.test; +import org.junit.Assume; +import org.junit.Before; +import org.keycloak.common.util.Environment; import org.keycloak.util.JWKSUtilsTest; /** @@ -7,4 +10,10 @@ import org.keycloak.util.JWKSUtilsTest; * */ public class DefaultCryptoJWKSUtilsTest extends JWKSUtilsTest { + + @Before + public void before() { + // Run this test just if java is not in FIPS mode + Assume.assumeFalse("Java is in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode()); + } } diff --git a/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoRSAVerifierTest.java b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoRSAVerifierTest.java index cb6541286f..2d8daf0977 100644 --- a/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoRSAVerifierTest.java +++ b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCryptoRSAVerifierTest.java @@ -1,10 +1,19 @@ package org.keycloak.crypto.def.test; +import org.junit.Assume; +import org.junit.Before; import org.keycloak.RSAVerifierTest; +import org.keycloak.common.util.Environment; /** * Test with bouncycastle security provider * */ public class DefaultCryptoRSAVerifierTest extends RSAVerifierTest { + + @Before + public void before() { + // Run this test just if java is not in FIPS mode + Assume.assumeFalse("Java is in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode()); + } } 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 index 6fa4faf7c0..ff978f1188 100644 --- 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 @@ -2,25 +2,25 @@ package org.keycloak.crypto.def.test; import org.junit.Assert; import org.junit.Before; +import org.junit.ClassRule; import org.junit.Test; import org.keycloak.common.crypto.CryptoIntegration; -import org.keycloak.common.crypto.CryptoProviderTypes; +import org.keycloak.common.crypto.CryptoConstants; import org.keycloak.crypto.def.AesKeyWrapAlgorithmProvider; import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider; +import org.keycloak.rule.CryptoInitRule; /** * @author Marek Posolda */ public class DefaultCryptoUnitTest { - @Before - public void init() { - CryptoIntegration.init(ClassLoader.getSystemClassLoader()); - } + @ClassRule + public static CryptoInitRule cryptoInitRule = new CryptoInitRule(); @Test public void testDefaultCrypto() throws Exception { - JWEAlgorithmProvider jweAlg = CryptoIntegration.getProvider().getAlgorithmProvider(JWEAlgorithmProvider.class, CryptoProviderTypes.AES_KEY_WRAP_ALGORITHM_PROVIDER); + JWEAlgorithmProvider jweAlg = CryptoIntegration.getProvider().getAlgorithmProvider(JWEAlgorithmProvider.class, CryptoConstants.A128KW); Assert.assertEquals(jweAlg.getClass(), AesKeyWrapAlgorithmProvider.class); } } diff --git a/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultSecureRandomTest.java b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultSecureRandomTest.java index d5f62ac191..eb68d0776f 100644 --- a/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultSecureRandomTest.java +++ b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultSecureRandomTest.java @@ -4,9 +4,12 @@ import java.security.SecureRandom; import org.jboss.logging.Logger; import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; import org.keycloak.common.crypto.CryptoIntegration; +import org.keycloak.common.util.Environment; import org.keycloak.rule.CryptoInitRule; /** @@ -17,6 +20,12 @@ public class DefaultSecureRandomTest { @ClassRule public static CryptoInitRule cryptoInitRule = new CryptoInitRule(); + @Before + public void before() { + // Run this test just if java is not in FIPS mode + Assume.assumeFalse("Java is in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode()); + } + protected static final Logger logger = Logger.getLogger(DefaultSecureRandomTest.class); @Test diff --git a/crypto/default/src/test/java/org/keycloak/crypto/def/test/PemUtilsBCTest.java b/crypto/default/src/test/java/org/keycloak/crypto/def/test/PemUtilsBCTest.java index 8e899d746a..0d5d42fddf 100644 --- a/crypto/default/src/test/java/org/keycloak/crypto/def/test/PemUtilsBCTest.java +++ b/crypto/default/src/test/java/org/keycloak/crypto/def/test/PemUtilsBCTest.java @@ -1,6 +1,9 @@ package org.keycloak.crypto.def.test; +import org.junit.Assume; +import org.junit.Before; import org.junit.Test; +import org.keycloak.common.util.Environment; import java.security.NoSuchAlgorithmException; @@ -8,6 +11,11 @@ import static org.junit.Assert.assertEquals; public class PemUtilsBCTest { + @Before + public void before() { + // Run this test just if java is not in FIPS mode + Assume.assumeFalse("Java is in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode()); + } @Test public void testGenerateThumbprintSha1() throws NoSuchAlgorithmException { diff --git a/crypto/fips1402/pom.xml b/crypto/fips1402/pom.xml index 0373eef80d..386e9c897a 100644 --- a/crypto/fips1402/pom.xml +++ b/crypto/fips1402/pom.xml @@ -34,34 +34,12 @@ org.keycloak keycloak-core - - - - org.bouncycastle - bcprov-jdk15on - - - org.bouncycastle - bcpkix-jdk15on - - org.keycloak keycloak-core test test-jar - - - - org.bouncycastle - bcprov-jdk15on - - - org.bouncycastle - bcpkix-jdk15on - - 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 df989d3375..2282c8ac2d 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 @@ -1,5 +1,6 @@ package org.keycloak.crypto.fips; +import java.security.Provider; import java.security.spec.ECField; import java.security.spec.ECFieldF2m; import java.security.spec.ECFieldFp; @@ -12,10 +13,12 @@ import java.util.concurrent.ConcurrentHashMap; import org.bouncycastle.asn1.x9.ECNamedCurveTable; import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.fips.FipsRSA; +import org.bouncycastle.crypto.fips.FipsSHS; import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider; import org.bouncycastle.math.ec.ECCurve; import org.keycloak.common.crypto.CryptoProvider; -import org.keycloak.common.crypto.CryptoProviderTypes; +import org.keycloak.common.crypto.CryptoConstants; import org.keycloak.common.crypto.CertificateUtilsProvider; import org.keycloak.common.crypto.PemUtilsProvider; @@ -27,16 +30,28 @@ import org.keycloak.common.crypto.PemUtilsProvider; */ public class FIPS1402Provider implements CryptoProvider { + private final BouncyCastleFipsProvider bcFipsProvider; private final Map providers = new ConcurrentHashMap<>(); public FIPS1402Provider() { - BouncyCastleFipsProvider bcFipsProvider = new BouncyCastleFipsProvider(); - providers.put(CryptoProviderTypes.BC_SECURITY_PROVIDER, bcFipsProvider); - providers.put(CryptoProviderTypes.AES_KEY_WRAP_ALGORITHM_PROVIDER, new FIPSAesKeyWrapAlgorithmProvider()); + // Case when BCFIPS provider already registered in Java security file + BouncyCastleFipsProvider existingBcFipsProvider = (BouncyCastleFipsProvider) Security.getProvider(CryptoConstants.BCFIPS_PROVIDER_ID); + this.bcFipsProvider = existingBcFipsProvider == null ? new BouncyCastleFipsProvider() : existingBcFipsProvider; + + providers.put(CryptoConstants.A128KW, new FIPSAesKeyWrapAlgorithmProvider()); + providers.put(CryptoConstants.RSA1_5, new FIPSRsaKeyEncryptionJWEAlgorithmProvider(FipsRSA.WRAP_PKCS1v1_5)); + providers.put(CryptoConstants.RSA_OAEP, new FIPSRsaKeyEncryptionJWEAlgorithmProvider(FipsRSA.WRAP_OAEP)); + providers.put(CryptoConstants.RSA_OAEP_256, new FIPSRsaKeyEncryptionJWEAlgorithmProvider(FipsRSA.WRAP_OAEP.withDigest(FipsSHS.Algorithm.SHA256))); Security.insertProviderAt(new KeycloakFipsSecurityProvider(bcFipsProvider), 1); } + + @Override + public Provider getBouncyCastleProvider() { + return bcFipsProvider; + } + @Override public T getAlgorithmProvider(Class clazz, String algorithm) { Object o = providers.get(algorithm); diff --git a/crypto/fips1402/src/main/java/org/keycloak/crypto/fips/FIPSRsaKeyEncryptionJWEAlgorithmProvider.java b/crypto/fips1402/src/main/java/org/keycloak/crypto/fips/FIPSRsaKeyEncryptionJWEAlgorithmProvider.java new file mode 100644 index 0000000000..a9a23c08e5 --- /dev/null +++ b/crypto/fips1402/src/main/java/org/keycloak/crypto/fips/FIPSRsaKeyEncryptionJWEAlgorithmProvider.java @@ -0,0 +1,56 @@ +package org.keycloak.crypto.fips; + +import java.security.Key; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.KeyUnwrapperUsingSecureRandom; +import org.bouncycastle.crypto.KeyWrapperUsingSecureRandom; +import org.bouncycastle.crypto.asymmetric.AsymmetricRSAPrivateKey; +import org.bouncycastle.crypto.asymmetric.AsymmetricRSAPublicKey; +import org.bouncycastle.crypto.fips.FipsRSA; +import org.keycloak.jose.jwe.JWEKeyStorage; +import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider; +import org.keycloak.jose.jwe.enc.JWEEncryptionProvider; + +/** + * Fips note: Based on https://downloads.bouncycastle.org/fips-java/BC-FJA-UserGuide-1.0.2.pdf, Section 4 + * There are no direct public/private key ciphers available in approved mode. Available ciphers are + * restricted to use for key wrapping and key transport, see section 7 and section 8 for details. + * Our solution is to pull out the CEK signature and encryption keys , encode them separately , and then + */ +public class FIPSRsaKeyEncryptionJWEAlgorithmProvider implements JWEAlgorithmProvider { + + private final FipsRSA.WrapParameters wrapParameters; + + public FIPSRsaKeyEncryptionJWEAlgorithmProvider(FipsRSA.WrapParameters wrapParameters) { + this.wrapParameters = wrapParameters; + } + + @Override + public byte[] decodeCek(byte[] encodedCek, Key privateKey) throws Exception { + AsymmetricRSAPrivateKey rsaPrivateKey = + new AsymmetricRSAPrivateKey(FipsRSA.ALGORITHM, privateKey.getEncoded()); + + FipsRSA.KeyWrapOperatorFactory wrapFact = + new FipsRSA.KeyWrapOperatorFactory(); + KeyUnwrapperUsingSecureRandom unwrapper = + wrapFact.createKeyUnwrapper(rsaPrivateKey, wrapParameters) + .withSecureRandom(SecureRandom.getInstance("DEFAULT")); + return unwrapper.unwrap(encodedCek, 0, encodedCek.length); + } + + + @Override + public byte[] encodeCek(JWEEncryptionProvider encryptionProvider, JWEKeyStorage keyStorage, Key publicKey) throws Exception { + AsymmetricRSAPublicKey rsaPubKey = + new AsymmetricRSAPublicKey(FipsRSA.ALGORITHM, publicKey.getEncoded()); + byte[] inputKeyBytes = keyStorage.getCekBytes(); + FipsRSA.KeyWrapOperatorFactory wrapFact = + new FipsRSA.KeyWrapOperatorFactory(); + + KeyWrapperUsingSecureRandom wrapper = + wrapFact.createKeyWrapper(rsaPubKey, wrapParameters).withSecureRandom( SecureRandom.getInstance("DEFAULT")); + return wrapper.wrap(inputKeyBytes, 0, inputKeyBytes.length); + } + +} diff --git a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402HmacTest.java b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402HmacTest.java index a07307ac3e..b3cb5f6f5c 100644 --- a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402HmacTest.java +++ b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402HmacTest.java @@ -7,8 +7,11 @@ import javax.crypto.SecretKeyFactory; import javax.crypto.spec.SecretKeySpec; import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; import org.junit.Test; import org.keycloak.common.util.BouncyIntegration; +import org.keycloak.common.util.Environment; import org.keycloak.jose.HmacTest; import org.keycloak.jose.jws.JWSBuilder; import org.keycloak.jose.jws.JWSInput; @@ -21,6 +24,12 @@ import org.keycloak.jose.jws.crypto.HMACProvider; */ public class FIPS1402HmacTest extends HmacTest { + @Before + public void before() { + // Run this test just if java is in FIPS mode + Assume.assumeTrue("Java is not in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode()); + } + @Test public void testHmacSignaturesFIPS() throws Exception { // 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 index cab849bafe..7bb1367f28 100644 --- 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 @@ -60,6 +60,8 @@ public class FIPS1402KeyPairVerifierTest extends KeyPairVerifierTest { "jLsXjc2CPf/lwNFqsVl7dlPNmg=="; } + + @Before public void before() { // Run this test just if java is in FIPS mode Assume.assumeTrue("Java is not in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode()); diff --git a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402SecureRandomTest.java b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402SecureRandomTest.java index 32ae1dd026..004c20c4ab 100644 --- a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402SecureRandomTest.java +++ b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402SecureRandomTest.java @@ -2,6 +2,8 @@ package org.keycloak.crypto.fips.test; import java.security.SecureRandom; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.fips.FipsStatus; import org.jboss.logging.Logger; import org.junit.Assert; import org.junit.Assume; @@ -32,6 +34,8 @@ public class FIPS1402SecureRandomTest { public void testSecureRandom() throws Exception { logger.info(CryptoIntegration.dumpJavaSecurityProviders()); + logger.infof("BC FIPS approved mode: %b, FIPS Status: %s", CryptoServicesRegistrar.isInApprovedOnlyMode(), FipsStatus.getStatusMessage()); + SecureRandom sc1 = new SecureRandom(); logger.infof(dumpSecureRandom("new SecureRandom()", sc1)); 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 2d6336e6d8..611883a0c2 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 @@ -2,9 +2,8 @@ package org.keycloak.crypto.fips.test; 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.common.crypto.CryptoConstants; import org.keycloak.crypto.fips.FIPSAesKeyWrapAlgorithmProvider; import org.keycloak.common.crypto.CryptoIntegration; import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider; @@ -20,7 +19,7 @@ public class FIPS1402UnitTest { @Test public void testFips() throws Exception { - JWEAlgorithmProvider jweAlg = CryptoIntegration.getProvider().getAlgorithmProvider(JWEAlgorithmProvider.class, CryptoProviderTypes.AES_KEY_WRAP_ALGORITHM_PROVIDER); + JWEAlgorithmProvider jweAlg = CryptoIntegration.getProvider().getAlgorithmProvider(JWEAlgorithmProvider.class, CryptoConstants.A128KW); Assert.assertEquals(jweAlg.getClass(), FIPSAesKeyWrapAlgorithmProvider.class); } } diff --git a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/PemUtilsBCFIPSTest.java b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/PemUtilsBCFIPSTest.java index 215b45d8b4..68f57026d2 100644 --- a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/PemUtilsBCFIPSTest.java +++ b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/PemUtilsBCFIPSTest.java @@ -1,6 +1,9 @@ package org.keycloak.crypto.fips.test; +import org.junit.Assume; +import org.junit.Before; import org.junit.Test; +import org.keycloak.common.util.Environment; import org.keycloak.common.util.PemUtils; import java.security.NoSuchAlgorithmException; @@ -9,6 +12,12 @@ import static org.junit.Assert.assertEquals; public class PemUtilsBCFIPSTest { + @Before + public void before() { + // Run this test just if java is in FIPS mode + Assume.assumeTrue("Java is not in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode()); + } + @Test public void testGenerateThumbprintSha1() throws NoSuchAlgorithmException { String[] test = new String[] {"abcdefg"}; diff --git a/docs/fips.md b/docs/fips.md index ed97e6d82c..8a817054a1 100644 --- a/docs/fips.md +++ b/docs/fips.md @@ -38,4 +38,20 @@ cd keycloak-999-SNAPSHOT/bin ``` NOTE: Right now, server should start, and I am able to create admin user on `http://localhost:8080`, but I am not able to finish -login to the admin console. However the Keycloak uses bouncycastle-fips libraries and the `CryptoIntegration` uses `FIPS1402Provider`. More fixes are required to have Keycloak server working... \ No newline at end of file +login to the admin console. However the Keycloak uses bouncycastle-fips libraries and the `CryptoIntegration` uses `FIPS1402Provider`. More fixes are required to have Keycloak server working... + +Run the tests in the FIPS environment +------------------------------------- +This instruction is about running automated tests on the FIPS enabled RHEL 8.6 system with the FIPS enabled OpenJDK 11. + +So far only the unit tests inside the `crypto` module are supported. More effort is needed to have whole testsuite passing. + +First it is needed to build the project (See above). Then run the tests in the `crypto` module. +``` +mvn clean install -f crypto +``` + +The tests should work also with the BouncyCastle approved mode, which is more strict in the used crypto algorithms +``` +mvn clean install -f crypto -Dorg.bouncycastle.fips.approved_only=true +``` diff --git a/services/src/main/java/org/keycloak/crypto/RsaCekManagementProvider.java b/services/src/main/java/org/keycloak/crypto/RsaCekManagementProvider.java index 50af9a22b3..a075f9a107 100644 --- a/services/src/main/java/org/keycloak/crypto/RsaCekManagementProvider.java +++ b/services/src/main/java/org/keycloak/crypto/RsaCekManagementProvider.java @@ -17,10 +17,9 @@ package org.keycloak.crypto; +import org.keycloak.common.crypto.CryptoIntegration; import org.keycloak.jose.jwe.JWEConstants; import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider; -import org.keycloak.jose.jwe.alg.RsaKeyEncryption256JWEAlgorithmProvider; -import org.keycloak.jose.jwe.alg.RsaKeyEncryptionJWEAlgorithmProvider; import org.keycloak.models.KeycloakSession; public class RsaCekManagementProvider implements CekManagementProvider { @@ -35,15 +34,12 @@ public class RsaCekManagementProvider implements CekManagementProvider { @Override public JWEAlgorithmProvider jweAlgorithmProvider() { - String jcaAlgorithmName = null; - if (JWEConstants.RSA1_5.equals(jweAlgorithmName)) { - jcaAlgorithmName = "RSA/ECB/PKCS1Padding"; - } else if (JWEConstants.RSA_OAEP.equals(jweAlgorithmName)) { - jcaAlgorithmName = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"; - } else if (JWEConstants.RSA_OAEP_256.equals(jweAlgorithmName)) { - return new RsaKeyEncryption256JWEAlgorithmProvider("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); + if (JWEConstants.RSA1_5.equals(jweAlgorithmName) || JWEConstants.RSA_OAEP.equals(jweAlgorithmName) || + JWEConstants.RSA_OAEP_256.equals(jweAlgorithmName)) { + return CryptoIntegration.getProvider().getAlgorithmProvider(JWEAlgorithmProvider.class, jweAlgorithmName); + } else { + return null; } - return new RsaKeyEncryptionJWEAlgorithmProvider(jcaAlgorithmName); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/AuthorizationTokenEncryptionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/AuthorizationTokenEncryptionTest.java index b6fd1e8ee4..7353b65a10 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/AuthorizationTokenEncryptionTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/AuthorizationTokenEncryptionTest.java @@ -230,12 +230,7 @@ public class AuthorizationTokenEncryptionTest extends AbstractTestRealmKeycloakT } private JWEAlgorithmProvider getJweAlgorithmProvider(String algAlgorithm) { - JWEAlgorithmProvider jweAlgorithmProvider = null; - if (JWEConstants.RSA1_5.equals(algAlgorithm) || JWEConstants.RSA_OAEP.equals(algAlgorithm) || - JWEConstants.RSA_OAEP_256.equals(algAlgorithm)) { - jweAlgorithmProvider = new RsaCekManagementProvider(null, algAlgorithm).jweAlgorithmProvider(); - } - return jweAlgorithmProvider; + return new RsaCekManagementProvider(null, algAlgorithm).jweAlgorithmProvider(); } private JWEEncryptionProvider getJweEncryptionProvider(String encAlgorithm) { JWEEncryptionProvider jweEncryptionProvider = null; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/IdTokenEncryptionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/IdTokenEncryptionTest.java index 5fbf02fb08..67c418243d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/IdTokenEncryptionTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/IdTokenEncryptionTest.java @@ -262,12 +262,7 @@ public class IdTokenEncryptionTest extends AbstractTestRealmKeycloakTest { } private JWEAlgorithmProvider getJweAlgorithmProvider(String algAlgorithm) { - JWEAlgorithmProvider jweAlgorithmProvider = null; - if (JWEConstants.RSA1_5.equals(algAlgorithm) || JWEConstants.RSA_OAEP.equals(algAlgorithm) || - JWEConstants.RSA_OAEP_256.equals(algAlgorithm)) { - jweAlgorithmProvider = new RsaCekManagementProvider(null, algAlgorithm).jweAlgorithmProvider(); - } - return jweAlgorithmProvider; + return new RsaCekManagementProvider(null, algAlgorithm).jweAlgorithmProvider(); } private JWEEncryptionProvider getJweEncryptionProvider(String encAlgorithm) { JWEEncryptionProvider jweEncryptionProvider = null; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/UserInfoTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/UserInfoTest.java index 5c4edf11f9..4c231348c6 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/UserInfoTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/UserInfoTest.java @@ -386,12 +386,7 @@ public class UserInfoTest extends AbstractKeycloakTest { } private JWEAlgorithmProvider getJweAlgorithmProvider(String algAlgorithm) { - JWEAlgorithmProvider jweAlgorithmProvider = null; - if (JWEConstants.RSA1_5.equals(algAlgorithm) || JWEConstants.RSA_OAEP.equals(algAlgorithm) || - JWEConstants.RSA_OAEP_256.equals(algAlgorithm)) { - jweAlgorithmProvider = new RsaCekManagementProvider(null, algAlgorithm).jweAlgorithmProvider(); - } - return jweAlgorithmProvider; + return new RsaCekManagementProvider(null, algAlgorithm).jweAlgorithmProvider(); } private JWEEncryptionProvider getJweEncryptionProvider(String encAlgorithm) { JWEEncryptionProvider jweEncryptionProvider = null;