Unit tests in "crypto/fips1402" passing on RHEL 8.6 with BC FIPS approved mode. Cleanup (#13406)
Closes #13128
This commit is contained in:
parent
6f7d20f752
commit
7e925bfbff
36 changed files with 323 additions and 280 deletions
|
@ -0,0 +1,19 @@
|
|||
package org.keycloak.common.crypto;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
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";
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
package org.keycloak.common.crypto;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class CryptoProviderTypes {
|
||||
|
||||
public static final String BC_SECURITY_PROVIDER = "bc-provider";
|
||||
|
||||
public static final String AES_KEY_WRAP_ALGORITHM_PROVIDER = "aes-keywrap-alg";
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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}.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -17,16 +17,18 @@
|
|||
|
||||
package org.keycloak.jose.jwe;
|
||||
|
||||
import org.keycloak.common.crypto.CryptoConstants;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
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";
|
||||
|
|
|
@ -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<String, JWEEncryptionProvider> 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<String, JWEAlgorithmProvider> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String, Object> 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> T getAlgorithmProvider(Class<T> clazz, String algorithm) {
|
||||
Object o = providers.get(algorithm);
|
||||
public Provider getBouncyCastleProvider() {
|
||||
return bcProvider;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T getAlgorithmProvider(Class<T> 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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -34,34 +34,12 @@
|
|||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<!-- TODO: These exclusions can be removed once we remove dependency of keycloak-core on bouncycastle -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<scope>test</scope>
|
||||
<type>test-jar</type>
|
||||
<!-- TODO: These exclusions can be removed once we remove dependency of keycloak-core on bouncycastle -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -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<String, Object> 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> T getAlgorithmProvider(Class<T> clazz, String algorithm) {
|
||||
Object o = providers.get(algorithm);
|
||||
|
|
|
@ -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<FipsRSA.WrapParameters> 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<FipsRSA.WrapParameters> wrapper =
|
||||
wrapFact.createKeyWrapper(rsaPubKey, wrapParameters).withSecureRandom( SecureRandom.getInstance("DEFAULT"));
|
||||
return wrapper.wrap(inputKeyBytes, 0, inputKeyBytes.length);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 {
|
||||
//
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"};
|
||||
|
|
18
docs/fips.md
18
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...
|
||||
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
|
||||
```
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue