Introduce crypto module using Wildfly Elytron (#14415)

Closes #12702
This commit is contained in:
David Anderson 2022-09-27 01:53:46 -05:00 committed by GitHub
parent be2deb0517
commit a8db79a68c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 2146 additions and 186 deletions

View file

@ -19,4 +19,5 @@ public class CryptoConstants {
/** Name of Java security provider used with fips BouncyCastle. Should be used in FIPS environment */ /** Name of Java security provider used with fips BouncyCastle. Should be used in FIPS environment */
public static final String BCFIPS_PROVIDER_ID = "BCFIPS"; public static final String BCFIPS_PROVIDER_ID = "BCFIPS";
} }

View file

@ -25,7 +25,8 @@ public class CryptoIntegration {
synchronized (lock) { synchronized (lock) {
if (cryptoProvider == null) { if (cryptoProvider == null) {
cryptoProvider = detectProvider(classLoader); cryptoProvider = detectProvider(classLoader);
logger.debugv("BouncyCastle provider: {0}", BouncyIntegration.PROVIDER); logger.debugv("java security provider: {0}", BouncyIntegration.PROVIDER);
} }
} }
} }
@ -54,7 +55,7 @@ public class CryptoIntegration {
throw new IllegalStateException("Multiple crypto providers loaded with the classLoader: " + classLoader + throw new IllegalStateException("Multiple crypto providers loaded with the classLoader: " + classLoader +
". Make sure only one cryptoProvider available on the classpath. Available providers: " +foundProviders); ". Make sure only one cryptoProvider available on the classpath. Available providers: " +foundProviders);
} else { } else {
logger.infof("Detected crypto provider: %s", foundProviders.get(0).getClass().getName()); logger.debugf("Detected crypto provider: %s", foundProviders.get(0).getClass().getName());
return foundProviders.get(0); return foundProviders.get(0);
} }
} }

View file

@ -1,8 +1,27 @@
package org.keycloak.common.crypto; package org.keycloak.common.crypto;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider; import java.security.Provider;
import java.security.Signature;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertStore;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
import java.security.spec.ECParameterSpec; import java.security.spec.ECParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import org.keycloak.common.util.KeystoreUtil.KeystoreFormat;
/** /**
* Abstraction to handle differences between the APIs for non-fips and fips mode * Abstraction to handle differences between the APIs for non-fips and fips mode
* *
@ -58,4 +77,24 @@ public interface CryptoProvider {
*/ */
ECParameterSpec createECParams(String curveName); ECParameterSpec createECParams(String curveName);
KeyPairGenerator getKeyPairGen(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException;
KeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException;
Cipher getAesCbcCipher() throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException;
Cipher getAesGcmCipher() throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException;
SecretKeyFactory getSecretKeyFact(String keyAlgorithm) throws NoSuchAlgorithmException, NoSuchProviderException;
KeyStore getKeyStore(KeystoreFormat format) throws KeyStoreException, NoSuchProviderException;
CertificateFactory getX509CertFactory() throws CertificateException, NoSuchProviderException;
CertStore getCertStore(CollectionCertStoreParameters collectionCertStoreParameters) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException;
CertPathBuilder getCertPathBuilder() throws NoSuchAlgorithmException, NoSuchProviderException;
Signature getSignature(String sigAlgName) throws NoSuchAlgorithmException, NoSuchProviderException;
} }

View file

@ -19,7 +19,6 @@ package org.keycloak.common.util;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.common.crypto.CryptoIntegration; import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.crypto.CryptoConstants;
import java.security.Provider; import java.security.Provider;
import java.security.Security; import java.security.Security;
@ -37,7 +36,8 @@ public class BouncyIntegration {
private static String loadProvider() { private static String loadProvider() {
Provider provider = CryptoIntegration.getProvider().getBouncyCastleProvider(); Provider provider = CryptoIntegration.getProvider().getBouncyCastleProvider();
if (provider == null) { if (provider == null) {
throw new RuntimeException("Failed to load required security provider: BouncyCastleProvider or BouncyCastleFipsProvider"); return Security.getProviders()[0].getName();
// throw new RuntimeException("Failed to load required security provider: BouncyCastleProvider or BouncyCastleFipsProvider");
} }
if (Security.getProvider(provider.getName()) == null) { if (Security.getProvider(provider.getName()) == null) {
Security.addProvider(provider); Security.addProvider(provider);

View file

@ -30,6 +30,8 @@ import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import org.keycloak.common.crypto.CryptoIntegration;
/** /**
* Extract PrivateKey, PublicKey, and X509Certificate from a DER encoded byte array or file. Usually * Extract PrivateKey, PublicKey, and X509Certificate from a DER encoded byte array or file. Usually
* generated from openssl * generated from openssl
@ -52,7 +54,7 @@ public final class DerUtils {
PKCS8EncodedKeySpec spec = PKCS8EncodedKeySpec spec =
new PKCS8EncodedKeySpec(keyBytes); new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA", BouncyIntegration.PROVIDER); KeyFactory kf =CryptoIntegration.getProvider().getKeyFactory("RSA");
return kf.generatePrivate(spec); return kf.generatePrivate(spec);
} }
@ -63,12 +65,12 @@ public final class DerUtils {
public static PublicKey decodePublicKey(byte[] der, String type) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException { public static PublicKey decodePublicKey(byte[] der, String type) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
X509EncodedKeySpec spec = X509EncodedKeySpec spec =
new X509EncodedKeySpec(der); new X509EncodedKeySpec(der);
KeyFactory kf = KeyFactory.getInstance(type, BouncyIntegration.PROVIDER); KeyFactory kf = CryptoIntegration.getProvider().getKeyFactory(type);
return kf.generatePublic(spec); return kf.generatePublic(spec);
} }
public static X509Certificate decodeCertificate(InputStream is) throws Exception { public static X509Certificate decodeCertificate(InputStream is) throws Exception {
CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyIntegration.PROVIDER); CertificateFactory cf = CryptoIntegration.getProvider().getX509CertFactory();
X509Certificate cert = (X509Certificate) cf.generateCertificate(is); X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
is.close(); is.close();
return cert; return cert;
@ -77,7 +79,7 @@ public final class DerUtils {
public static PrivateKey decodePrivateKey(byte[] der) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException { public static PrivateKey decodePrivateKey(byte[] der) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
PKCS8EncodedKeySpec spec = PKCS8EncodedKeySpec spec =
new PKCS8EncodedKeySpec(der); new PKCS8EncodedKeySpec(der);
KeyFactory kf = KeyFactory.getInstance("RSA", BouncyIntegration.PROVIDER); KeyFactory kf = CryptoIntegration.getProvider().getKeyFactory("RSA");
return kf.generatePrivate(spec); return kf.generatePrivate(spec);
} }
} }

View file

@ -17,8 +17,6 @@
package org.keycloak.common.util; package org.keycloak.common.util;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key; import java.security.Key;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.KeyPair; import java.security.KeyPair;
@ -30,6 +28,11 @@ import java.security.PublicKey;
import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.RSAPublicKeySpec; import java.security.spec.RSAPublicKeySpec;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.keycloak.common.crypto.CryptoIntegration;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
@ -46,7 +49,7 @@ public class KeyUtils {
public static KeyPair generateRsaKeyPair(int keysize) { public static KeyPair generateRsaKeyPair(int keysize) {
try { try {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", BouncyIntegration.PROVIDER); KeyPairGenerator generator = CryptoIntegration.getProvider().getKeyPairGen("RSA");
generator.initialize(keysize); generator.initialize(keysize);
KeyPair keyPair = generator.generateKeyPair(); KeyPair keyPair = generator.generateKeyPair();
return keyPair; return keyPair;

View file

@ -18,6 +18,7 @@
package org.keycloak.common.util; package org.keycloak.common.util;
import org.keycloak.common.constants.GenericConstants; import org.keycloak.common.constants.GenericConstants;
import org.keycloak.common.crypto.CryptoIntegration;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -65,12 +66,7 @@ public class KeystoreUtil {
InputStream stream = FindFile.findFile(keystoreFile); InputStream stream = FindFile.findFile(keystoreFile);
try { try {
KeyStore keyStore = null; KeyStore keyStore = CryptoIntegration.getProvider().getKeyStore(format);
if (format == KeystoreFormat.JKS) {
keyStore = KeyStore.getInstance(format.toString());
} else {
keyStore = KeyStore.getInstance(format.toString(), BouncyIntegration.PROVIDER);
}
keyStore.load(stream, storePassword.toCharArray()); keyStore.load(stream, storePassword.toCharArray());
PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, keyPassword.toCharArray()); PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, keyPassword.toCharArray());

View file

@ -34,7 +34,7 @@ import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import org.keycloak.common.util.BouncyIntegration; import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.jose.jwe.JWE; import org.keycloak.jose.jwe.JWE;
import org.keycloak.jose.jwe.JWEKeyStorage; import org.keycloak.jose.jwe.JWEKeyStorage;
import org.keycloak.jose.jwe.JWEUtils; import org.keycloak.jose.jwe.JWEUtils;
@ -117,15 +117,14 @@ public abstract class AesCbcHmacShaEncryptionProvider implements JWEEncryptionPr
private byte[] encryptBytes(byte[] contentBytes, byte[] ivBytes, Key aesKey) throws GeneralSecurityException { private byte[] encryptBytes(byte[] contentBytes, byte[] ivBytes, Key aesKey) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", BouncyIntegration.PROVIDER); Cipher cipher = CryptoIntegration.getProvider().getAesCbcCipher();
AlgorithmParameterSpec ivParamSpec = new IvParameterSpec(ivBytes); AlgorithmParameterSpec ivParamSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, aesKey, ivParamSpec); cipher.init(Cipher.ENCRYPT_MODE, aesKey, ivParamSpec);
return cipher.doFinal(contentBytes); return cipher.doFinal(contentBytes);
} }
private byte[] decryptBytes(byte[] encryptedBytes, byte[] ivBytes, Key aesKey) throws GeneralSecurityException { private byte[] decryptBytes(byte[] encryptedBytes, byte[] ivBytes, Key aesKey) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", BouncyIntegration.PROVIDER); Cipher cipher = CryptoIntegration.getProvider().getAesCbcCipher();
AlgorithmParameterSpec ivParamSpec = new IvParameterSpec(ivBytes); AlgorithmParameterSpec ivParamSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParamSpec); cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParamSpec);
return cipher.doFinal(encryptedBytes); return cipher.doFinal(encryptedBytes);

View file

@ -27,7 +27,7 @@ import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import org.keycloak.common.util.BouncyIntegration; import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.jose.jwe.JWE; import org.keycloak.jose.jwe.JWE;
import org.keycloak.jose.jwe.JWEKeyStorage; import org.keycloak.jose.jwe.JWEKeyStorage;
import org.keycloak.jose.jwe.JWEUtils; import org.keycloak.jose.jwe.JWEUtils;
@ -89,7 +89,7 @@ public abstract class AesGcmEncryptionProvider implements JWEEncryptionProvider
} }
private byte[] encryptBytes(byte[] contentBytes, byte[] ivBytes, Key aesKey, byte[] aad) throws GeneralSecurityException { private byte[] encryptBytes(byte[] contentBytes, byte[] ivBytes, Key aesKey, byte[] aad) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", BouncyIntegration.PROVIDER); Cipher cipher = CryptoIntegration.getProvider().getAesGcmCipher();
GCMParameterSpec gcmParams = new GCMParameterSpec(AUTH_TAG_SIZE_BYTE * 8, ivBytes); GCMParameterSpec gcmParams = new GCMParameterSpec(AUTH_TAG_SIZE_BYTE * 8, ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, aesKey, gcmParams); cipher.init(Cipher.ENCRYPT_MODE, aesKey, gcmParams);
cipher.updateAAD(aad); cipher.updateAAD(aad);
@ -99,7 +99,7 @@ public abstract class AesGcmEncryptionProvider implements JWEEncryptionProvider
} }
private byte[] decryptBytes(byte[] encryptedBytes, byte[] ivBytes, Key aesKey, byte[] aad) throws GeneralSecurityException { private byte[] decryptBytes(byte[] encryptedBytes, byte[] ivBytes, Key aesKey, byte[] aad) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", BouncyIntegration.PROVIDER); Cipher cipher = CryptoIntegration.getProvider().getAesGcmCipher();
GCMParameterSpec gcmParams = new GCMParameterSpec(AUTH_TAG_SIZE_BYTE * 8, ivBytes); GCMParameterSpec gcmParams = new GCMParameterSpec(AUTH_TAG_SIZE_BYTE * 8, ivBytes);
cipher.init(Cipher.DECRYPT_MODE, aesKey, gcmParams); cipher.init(Cipher.DECRYPT_MODE, aesKey, gcmParams);
cipher.updateAAD(aad); cipher.updateAAD(aad);

View file

@ -104,7 +104,7 @@ public class JWKParser {
ECParameterSpec params = CryptoIntegration.getProvider().createECParams(name); ECParameterSpec params = CryptoIntegration.getProvider().createECParams(name);
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params); ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
KeyFactory kf = KeyFactory.getInstance("ECDSA"); KeyFactory kf = CryptoIntegration.getProvider().getKeyFactory("ECDSA");
return kf.generatePublic(pubKeySpec); return kf.generatePublic(pubKeySpec);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);

View file

@ -52,13 +52,13 @@ public abstract class JWETest {
@ClassRule @ClassRule
public static CryptoInitRule cryptoInitRule = new CryptoInitRule(); public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
private static final String PAYLOAD = "Hello world! How are you man? I hope you are fine. This is some quite a long text, which is much longer than just simple 'Hello World'"; protected static final String PAYLOAD = "Hello world! How are you man? I hope you are fine. This is some quite a long text, which is much longer than just simple 'Hello World'";
private static final byte[] HMAC_SHA256_KEY = new byte[] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 13, 14, 15, 16 }; protected static final byte[] HMAC_SHA256_KEY = new byte[] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 13, 14, 15, 16 };
private static final byte[] AES_128_KEY = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; protected static final byte[] AES_128_KEY = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
private static final byte[] HMAC_SHA512_KEY = new byte[] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; protected static final byte[] HMAC_SHA512_KEY = new byte[] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
private static final byte[] AES_256_KEY = new byte[] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; protected static final byte[] AES_256_KEY = new byte[] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
@Test @Test
public void testDirect_Aes128CbcHmacSha256() throws Exception { public void testDirect_Aes128CbcHmacSha256() throws Exception {
@ -79,7 +79,7 @@ public abstract class JWETest {
} }
private void testDirectEncryptAndDecrypt(Key aesKey, Key hmacKey, String encAlgorithm, String payload, boolean sysout) throws Exception { protected void testDirectEncryptAndDecrypt(Key aesKey, Key hmacKey, String encAlgorithm, String payload, boolean sysout) throws Exception {
JWEHeader jweHeader = new JWEHeader(JWEConstants.DIRECT, encAlgorithm, null); JWEHeader jweHeader = new JWEHeader(JWEConstants.DIRECT, encAlgorithm, null);
JWE jwe = new JWE() JWE jwe = new JWE()
.header(jweHeader) .header(jweHeader)

View file

@ -23,10 +23,10 @@ import java.util.List;
import org.junit.ClassRule; import org.junit.ClassRule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.BouncyIntegration;
import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.PemUtils;
import org.keycloak.crypto.JavaAlgorithm; import org.keycloak.crypto.JavaAlgorithm;
import org.keycloak.crypto.KeyType;
import org.keycloak.common.crypto.CryptoIntegration; import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.rule.CryptoInitRule; import org.keycloak.rule.CryptoInitRule;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
@ -61,7 +61,7 @@ public abstract class JWKTest {
@Test @Test
public void publicRs256() throws Exception { public void publicRs256() throws Exception {
KeyPair keyPair = KeyPairGenerator.getInstance("RSA", BouncyIntegration.PROVIDER ).generateKeyPair(); KeyPair keyPair = CryptoIntegration.getProvider().getKeyPairGen(KeyType.RSA).generateKeyPair();
PublicKey publicKey = keyPair.getPublic(); PublicKey publicKey = keyPair.getPublic();
X509Certificate certificate = generateV1SelfSignedCertificate(keyPair, "Test"); X509Certificate certificate = generateV1SelfSignedCertificate(keyPair, "Test");
@ -96,7 +96,7 @@ public abstract class JWKTest {
@Test @Test
public void publicRs256Chain() throws Exception { public void publicRs256Chain() throws Exception {
KeyPair keyPair = KeyPairGenerator.getInstance("RSA", BouncyIntegration.PROVIDER).generateKeyPair(); KeyPair keyPair = CryptoIntegration.getProvider().getKeyPairGen(KeyType.RSA).generateKeyPair();
PublicKey publicKey = keyPair.getPublic(); PublicKey publicKey = keyPair.getPublic();
List<X509Certificate> certificates = Arrays.asList(generateV1SelfSignedCertificate(keyPair, "Test"), generateV1SelfSignedCertificate(keyPair, "Intermediate")); List<X509Certificate> certificates = Arrays.asList(generateV1SelfSignedCertificate(keyPair, "Test"), generateV1SelfSignedCertificate(keyPair, "Intermediate"));
@ -137,7 +137,7 @@ public abstract class JWKTest {
@Test @Test
public void publicEs256() throws Exception { public void publicEs256() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", BouncyIntegration.PROVIDER); KeyPairGenerator keyGen = CryptoIntegration.getProvider().getKeyPairGen(KeyType.EC);
SecureRandom randomGen = new SecureRandom(); SecureRandom randomGen = new SecureRandom();
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1"); ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");
keyGen.initialize(ecSpec, randomGen); keyGen.initialize(ecSpec, randomGen);

View file

@ -1,11 +1,28 @@
package org.keycloak.crypto.def; package org.keycloak.crypto.def;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider; import java.security.Provider;
import java.security.Security; import java.security.Security;
import java.security.Signature;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertStore;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
import java.security.spec.ECParameterSpec; import java.security.spec.ECParameterSpec;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECNamedCurveSpec; import org.bouncycastle.jce.spec.ECNamedCurveSpec;
@ -16,6 +33,9 @@ import org.keycloak.common.crypto.ECDSACryptoProvider;
import org.keycloak.common.crypto.CertificateUtilsProvider; import org.keycloak.common.crypto.CertificateUtilsProvider;
import org.keycloak.common.crypto.PemUtilsProvider; import org.keycloak.common.crypto.PemUtilsProvider;
import org.keycloak.common.crypto.UserIdentityExtractorProvider; import org.keycloak.common.crypto.UserIdentityExtractorProvider;
import org.keycloak.common.util.BouncyIntegration;
import org.keycloak.common.util.KeystoreUtil.KeystoreFormat;
import org.keycloak.crypto.JavaAlgorithm;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -85,4 +105,65 @@ public class DefaultCryptoProvider implements CryptoProvider {
return clazz.cast(new BCOCSPProvider()); return clazz.cast(new BCOCSPProvider());
} }
@Override
public KeyPairGenerator getKeyPairGen(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
return KeyPairGenerator.getInstance(algorithm, BouncyIntegration.PROVIDER);
}
@Override
public KeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
return KeyFactory.getInstance(algorithm, BouncyIntegration.PROVIDER);
}
@Override
public Cipher getAesCbcCipher() throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
return Cipher.getInstance("AES/CBC/PKCS7Padding", BouncyIntegration.PROVIDER);
}
@Override
public Cipher getAesGcmCipher() throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
return Cipher.getInstance("AES/GCM/NoPadding", BouncyIntegration.PROVIDER);
}
@Override
public SecretKeyFactory getSecretKeyFact(String keyAlgorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
return SecretKeyFactory.getInstance(keyAlgorithm, BouncyIntegration.PROVIDER);
}
@Override
public KeyStore getKeyStore(KeystoreFormat format) throws KeyStoreException, NoSuchProviderException {
if (format == KeystoreFormat.JKS) {
return KeyStore.getInstance(format.toString());
} else {
return KeyStore.getInstance(format.toString(), BouncyIntegration.PROVIDER);
}
}
@Override
public CertificateFactory getX509CertFactory() throws CertificateException, NoSuchProviderException {
return CertificateFactory.getInstance("X.509", BouncyIntegration.PROVIDER);
}
@Override
public CertStore getCertStore(CollectionCertStoreParameters certStoreParams) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException {
return CertStore.getInstance("Collection", certStoreParams, BouncyIntegration.PROVIDER);
}
@Override
public CertPathBuilder getCertPathBuilder() throws NoSuchAlgorithmException, NoSuchProviderException {
return CertPathBuilder.getInstance("PKIX", BouncyIntegration.PROVIDER);
}
@Override
public Signature getSignature(String sigAlgName) throws NoSuchAlgorithmException, NoSuchProviderException {
return Signature.getInstance(JavaAlgorithm.getJavaAlgorithm(sigAlgName), BouncyIntegration.PROVIDER);
}
} }

72
crypto/elytron/pom.xml Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0"?>
<!--
~ Copyright 2022 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-crypto-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>999-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-crypto-elytron</artifactId>
<name>Keycloak Crypto Wildfly Elytron Integration</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi-private</artifactId>
</dependency>
<dependency>
<groupId>org.wildfly.security</groupId>
<artifactId>wildfly-elytron</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,48 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron;
import java.security.Key;
import javax.crypto.Cipher;
import org.keycloak.jose.jwe.JWEKeyStorage;
import org.keycloak.jose.jwe.JWEKeyStorage.KeyUse;
import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider;
import org.keycloak.jose.jwe.enc.JWEEncryptionProvider;
/**
* @author <a href="mailto:david.anderson@redhat.com">David Anderson</a>
*/
public class AesKeyWrapAlgorithmProvider implements JWEAlgorithmProvider {
@Override
public byte[] decodeCek(byte[] encodedCek, Key encryptionKey) throws Exception {
Cipher cipher = Cipher.getInstance("AESWrap_128");
cipher.init(Cipher.UNWRAP_MODE, encryptionKey);
return cipher.unwrap(encodedCek, "AES", Cipher.SECRET_KEY).getEncoded();
}
@Override
public byte[] encodeCek(JWEEncryptionProvider encryptionProvider, JWEKeyStorage keyStorage, Key encryptionKey) throws Exception {
Cipher cipher = Cipher.getInstance("AESWrap_128");
cipher.init(Cipher.WRAP_MODE, encryptionKey);
return cipher.wrap(keyStorage.getCEKKey(KeyUse.ENCRYPTION, false));
}
}

View file

@ -0,0 +1,313 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron;
import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.time.DateTimeException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.security.auth.x500.X500Principal;
import org.keycloak.common.crypto.CertificateUtilsProvider;
import org.wildfly.security.asn1.ASN1;
import org.wildfly.security.asn1.DERDecoder;
import org.wildfly.security.x500.X500;
import org.wildfly.security.x500.cert.AuthorityKeyIdentifierExtension;
import org.wildfly.security.x500.cert.BasicConstraintsExtension;
import org.wildfly.security.x500.cert.CertificatePoliciesExtension;
import org.wildfly.security.x500.cert.CertificatePoliciesExtension.PolicyInformation;
import org.wildfly.security.x500.cert.ExtendedKeyUsageExtension;
import org.wildfly.security.x500.cert.KeyUsage;
import org.wildfly.security.x500.cert.KeyUsageExtension;
import org.wildfly.security.x500.cert.SubjectKeyIdentifierExtension;
import org.wildfly.security.x500.cert.X509CertificateBuilder;
import org.wildfly.security.x500.cert.X509CertificateExtension;
/**
* The Class CertificateUtils provides utility functions for generation
* and usage of X.509 certificates
*
* @author <a href="mailto:david.anderson@redhat.com">David Anderson</a>
*/
public class ElytronCertificateUtils implements CertificateUtilsProvider {
/**
* Generates version 3 {@link java.security.cert.X509Certificate}.
*
* @param keyPair the key pair
* @param caPrivateKey the CA private key
* @param caCert the CA certificate
* @param subject the subject name
*
* @return the x509 certificate
*
* @throws Exception the exception
*/
@Override
public X509Certificate generateV3Certificate(KeyPair keyPair, PrivateKey caPrivateKey,
X509Certificate caCert,
String subject) throws Exception {
try {
X500Principal subjectdn = subjectToX500Principle(subject);
X500Principal issuerdn = subjectdn;
if (caCert != null) {
issuerdn = caCert.getSubjectX500Principal();
}
// Validity
ZonedDateTime notBefore = ZonedDateTime.ofInstant(new Date(System.currentTimeMillis()).toInstant(),
ZoneId.systemDefault());
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, 3);
Date validityEndDate = new Date(calendar.getTime().getTime());
ZonedDateTime notAfter = ZonedDateTime.ofInstant(validityEndDate.toInstant(),
ZoneId.systemDefault());
// Serial Number
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
BigInteger serialNumber = BigInteger.valueOf(Math.abs(random.nextInt()));
// Extended Key Usage
ArrayList<String> ekuList = new ArrayList<String>();
ekuList.add(X500.OID_KP_EMAIL_PROTECTION);
ekuList.add(X500.OID_KP_SERVER_AUTH);
X509CertificateBuilder cbuilder = new X509CertificateBuilder()
.setSubjectDn(subjectdn)
.setIssuerDn(issuerdn)
.setNotValidBefore(notBefore)
.setNotValidAfter(notAfter)
.setSigningKey(keyPair.getPrivate())
.setPublicKey(keyPair.getPublic())
.setSerialNumber(serialNumber)
.setSignatureAlgorithmName("SHA256withRSA")
.setSigningKey(caPrivateKey)
// Subject Key Identifier Extension
.addExtension(new SubjectKeyIdentifierExtension(keyPair.getPublic().getEncoded()))
// Authority Key Identifier
.addExtension(new AuthorityKeyIdentifierExtension(keyPair.getPublic().getEncoded(), null, null))
// Key Usage
.addExtension(
new KeyUsageExtension(KeyUsage.digitalSignature, KeyUsage.keyCertSign, KeyUsage.cRLSign))
.addExtension(new ExtendedKeyUsageExtension(false, ekuList))
// Basic Constraints
.addExtension(new BasicConstraintsExtension(true, true, 0));
return cbuilder.build();
} catch (Exception e) {
throw new RuntimeException("Error creating X509v3Certificate.", e);
}
}
/**
* Generate version 1 self signed {@link java.security.cert.X509Certificate}..
*
* @param caKeyPair the CA key pair
* @param subject the subject name
*
* @return the x509 certificate
*
* @throws Exception the exception
*/
@Override
public X509Certificate generateV1SelfSignedCertificate(KeyPair caKeyPair, String subject) {
return generateV1SelfSignedCertificate(caKeyPair, subject, BigInteger.valueOf(System.currentTimeMillis()));
}
@Override
public X509Certificate generateV1SelfSignedCertificate(KeyPair caKeyPair, String subject,
BigInteger serialNumber) {
try {
X500Principal subjectdn = subjectToX500Principle(subject);
ZonedDateTime notBefore = ZonedDateTime.ofInstant(
(new Date(System.currentTimeMillis() - 100000)).toInstant(),
ZoneId.systemDefault());
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, 10);
Date validityEndDate = new Date(calendar.getTime().getTime());
ZonedDateTime notAfter = ZonedDateTime.ofInstant(validityEndDate.toInstant(),
ZoneId.systemDefault());
X509CertificateBuilder cbuilder = new X509CertificateBuilder()
.setSubjectDn(subjectdn)
.setIssuerDn(subjectdn)
.setNotValidBefore(notBefore)
.setNotValidAfter(notAfter)
.setSigningKey(caKeyPair.getPrivate())
.setPublicKey(caKeyPair.getPublic())
.setSerialNumber(serialNumber)
.setSignatureAlgorithmName("SHA256withRSA");
return cbuilder.build();
} catch (Exception e) {
throw new RuntimeException("Error creating X509v1Certificate.", e);
}
}
// Some subject names will not conform to the RFC format
private static X500Principal subjectToX500Principle(String subject) {
if(!subject.startsWith("CN=")) {
subject = "CN="+subject;
}
return new X500Principal(subject);
}
@Override
public List<String> getCertificatePolicyList(X509Certificate cert) throws GeneralSecurityException {
byte[] policy = cert.getExtensionValue("2.5.29.32");
System.out.println("Policy: " + new String(policy));
DERDecoder decPolicy = new DERDecoder(policy);
int type = decPolicy.peekType();
System.out.println("type " + type);
DERDecoder der = new DERDecoder(decPolicy.decodeOctetString());
List<String> policyList =new ArrayList<>();
while (der.hasNextElement()) {
switch (der.peekType()) {
case ASN1.SEQUENCE_TYPE:
der.startSequence();
break;
case ASN1.OBJECT_IDENTIFIER_TYPE:
policyList.add(der.decodeObjectIdentifier());
der.endSequence();
break;
default:
der.skipElement();
}
}
return policyList;
}
@Override
public List<String> getCRLDistributionPoints(X509Certificate cert) throws IOException {
byte[] data = cert.getExtensionValue(CRL_DISTRIBUTION_POINTS_OID);
if (data == null) {
return Collections.emptyList();
}
List<String> distPointUrls = new ArrayList<>();
DERDecoder der = new DERDecoder(data);
der = new DERDecoder(der.decodeOctetString());
while ( der.hasNextElement() ) {
switch (der.peekType()) {
case ASN1.SEQUENCE_TYPE:
der.startSequence();
break;
case ASN1.UTF8_STRING_TYPE:
distPointUrls.add(der.decodeUtf8String());
break;
case 0xa0:
der.decodeImplicit(0xa0);
byte[] edata = der.decodeOctetString();
while(!Character.isLetterOrDigit(edata[0])) {
edata = Arrays.copyOfRange(edata, 1, edata.length);
}
distPointUrls.add(new String(edata));
break;
default:
der.skipElement();
}
}
return distPointUrls;
}
@Override
public X509Certificate createServicesTestCertificate(String dn, Date startDate, Date expiryDate, KeyPair keyPair,
String... certificatePolicyOid) {
try {
X500Principal subjectdn = subjectToX500Principle(dn);
X500Principal issuerdn = subjectToX500Principle(dn);
ZonedDateTime notValidBefore = ZonedDateTime.ofInstant(startDate.toInstant(), ZoneId.systemDefault());
ZonedDateTime notValidAfter = ZonedDateTime.ofInstant(expiryDate.toInstant(), ZoneId.systemDefault());
X509CertificateBuilder cbuilder = new X509CertificateBuilder()
.setSubjectDn(subjectdn)
.setIssuerDn(issuerdn)
.setNotValidBefore(notValidBefore)
.setNotValidAfter(notValidAfter)
.setSigningKey(keyPair.getPrivate())
.setPublicKey(keyPair.getPublic())
.addExtension(createPoliciesExtension(certificatePolicyOid))
.setSignatureAlgorithmName("SHA256withRSA");
return cbuilder.build();
} catch ( DateTimeException | CertificateException e ) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private X509CertificateExtension createPoliciesExtension(String[] certificatePolicyOid) {
List<PolicyInformation> policyList = new ArrayList<>();
for(String policyOid : certificatePolicyOid) {
policyList.add(new PolicyInformation(policyOid));
}
return new CertificatePoliciesExtension(false, policyList);
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron;
import java.io.IOException;
import java.math.BigInteger;
import org.keycloak.common.crypto.ECDSACryptoProvider;
import org.wildfly.security.asn1.DERDecoder;
import org.wildfly.security.asn1.DEREncoder;
/**
* @author <a href="mailto:david.anderson@redhat.com">David Anderson</a>
*/
public class ElytronECDSACryptoProvider implements ECDSACryptoProvider {
@Override
public byte[] concatenatedRSToASN1DER(final byte[] signature, int signLength) throws IOException {
int len = signLength / 2;
int arraySize = len + 1;
byte[] r = new byte[arraySize];
byte[] s = new byte[arraySize];
System.arraycopy(signature, 0, r, 1, len);
System.arraycopy(signature, len, s, 1, len);
BigInteger rBigInteger = new BigInteger(r);
BigInteger sBigInteger = new BigInteger(s);
DEREncoder seq = new DEREncoder();
seq.startSequence();
seq.encodeInteger(rBigInteger);
seq.encodeInteger(sBigInteger);
return seq.getEncoded();
}
@Override
public byte[] asn1derToConcatenatedRS(final byte[] derEncodedSignatureValue, int signLength) throws IOException {
int len = signLength / 2;
DERDecoder der = new DERDecoder(derEncodedSignatureValue);
der.startSequence();
byte[] r = der.decodeInteger().toByteArray();
byte[] s = der.decodeInteger().toByteArray();
byte[] concatenatedSignatureValue = new byte[signLength];
System.arraycopy(r, 0, concatenatedSignatureValue, 0, len);
System.arraycopy(s, 0, concatenatedSignatureValue, len, len);
return concatenatedSignatureValue;
}
}

View file

@ -0,0 +1,160 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron;
import java.io.IOException;
import java.net.URI;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CRLReason;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import javax.net.ssl.TrustManagerFactory;
import org.jboss.logging.Logger;
import org.keycloak.models.KeycloakSession;
import org.keycloak.utils.OCSPProvider;
import org.wildfly.security.asn1.ASN1;
import org.wildfly.security.asn1.DERDecoder;
import org.wildfly.security.ssl.X509RevocationTrustManager;
import org.wildfly.security.x500.X500;
/**
* @author <a href="mailto:david.anderson@redhat.com">David Anderson</a>
*/
public class ElytronOCSPProvider extends OCSPProvider {
private final static Logger logger = Logger.getLogger(ElytronOCSPProvider.class.getName());
/**
* Requests certificate revocation status using OCSP.
*
* @param cert the certificate to be checked
* @param issuerCertificate the issuer certificate
* @param responderURIs the OCSP responder URIs
* @param responderCert the OCSP responder certificate
* @param date if null, the current time is used.
* @return a revocation status
* @throws CertPathValidatorException
*/
@Override
protected OCSPRevocationStatus check(KeycloakSession session, X509Certificate cert, X509Certificate issuerCertificate, List<URI> responderURIs, X509Certificate responderCert, Date date) throws CertPathValidatorException {
if (responderURIs == null || responderURIs.size() == 0)
throw new IllegalArgumentException("Need at least one responder");
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null,"pass".toCharArray());
trustStore.setCertificateEntry("trust", cert);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
X509RevocationTrustManager trustMgr = X509RevocationTrustManager.builder()
.setOcspResponderCert(responderCert)
.setTrustStore(trustStore)
.setTrustManagerFactory(trustManagerFactory)
.build()
;
X509Certificate[] certs = { cert };
trustMgr.checkClientTrusted(certs, cert.getType());
} catch (NoSuchAlgorithmException | CertificateException | IOException | KeyStoreException e) {
logger.warn("OSCP Response check failed.", e);
return unknownStatus();
}
return new OCSPRevocationStatus() {
@Override
public RevocationStatus getRevocationStatus() {
return RevocationStatus.GOOD;
}
@Override
public Date getRevocationTime() {
return null;
}
@Override
public CRLReason getRevocationReason() {
return null;
}
};
}
/**
* Extracts OCSP responder URI from X509 AIA v3 extension, if available. There can be
* multiple responder URIs encoded in the certificate.
*
* @param cert
* @return a list of available responder URIs.
* @throws CertificateEncodingException
*/
@Override
protected List<String> getResponderURIs(X509Certificate cert) throws CertificateEncodingException {
LinkedList<String> responderURIs = new LinkedList<>();
byte[] authinfob = cert.getExtensionValue(X500.OID_PE_AUTHORITY_INFO_ACCESS);
DERDecoder der = new DERDecoder(authinfob);
der = new DERDecoder(der.decodeOctetString());
while ( der.hasNextElement() ) {
switch (der.peekType()) {
case ASN1.SEQUENCE_TYPE:
der.startSequence();
break;
case ASN1.OBJECT_IDENTIFIER_TYPE:
String oid = der.decodeObjectIdentifier();
if ("1.3.6.1.5.5.7.48.1".equals(oid)) {
byte[] uri = der.drainElementValue();
responderURIs.add(new String(uri));
}
break;
case ASN1.IA5_STRING_TYPE:
break;
case ASN1.UTF8_STRING_TYPE:
responderURIs.add(der.decodeUtf8String());
break;
case 0xa0:
der.decodeImplicit(0xa0);
byte[] edata = der.decodeOctetString();
while(!Character.isLetterOrDigit(edata[0])) {
edata = Arrays.copyOfRange(edata, 1, edata.length);
}
responderURIs.add(new String(edata));
break;
default:
der.skipElement();
}
}
return responderURIs;
}
}

View file

@ -0,0 +1,70 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron;
import java.security.Key;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.util.Base64;
import org.jboss.logging.Logger;
import org.keycloak.common.crypto.PemUtilsProvider;
import org.keycloak.common.util.DerUtils;
import org.keycloak.common.util.PemException;
/**
* @author <a href="mailto:david.anderson@redhat.com">David Anderson</a>
*/
public class ElytronPEMUtilsProvider extends PemUtilsProvider {
Logger log = Logger.getLogger(ElytronPEMUtilsProvider.class);
@Override
protected String encode(Object obj) {
String encoded = null;
if(obj instanceof Key ) {
byte[] b = ((Key)obj).getEncoded();
encoded = Base64.getEncoder().encodeToString(b);
} else if(obj instanceof Certificate) {
byte[] c;
try {
c = ((Certificate)obj).getEncoded();
encoded = Base64.getEncoder().encodeToString(c);
} catch (CertificateEncodingException e) {
log.warn("Failed to encoded certificate.", e);
throw new RuntimeException(e);
}
}
return encoded;
}
@Override
public PrivateKey decodePrivateKey(String pem) {
if (pem == null) {
return null;
}
try {
byte[] der = pemToDer(pem);
return DerUtils.decodePrivateKey(der);
} catch (Exception e) {
throw new PemException(e);
}
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron;
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;
/**
* @author <a href="mailto:david.anderson@redhat.com">David Anderson</a>
*/
public class ElytronRsaKeyEncryption256JWEAlgorithmProvider extends ElytronRsaKeyEncryptionJWEAlgorithmProvider {
public ElytronRsaKeyEncryption256JWEAlgorithmProvider(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);
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron;
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;
/**
* @author <a href="mailto:david.anderson@redhat.com">David Anderson</a>
*/
public class ElytronRsaKeyEncryptionJWEAlgorithmProvider implements JWEAlgorithmProvider {
private final String jcaAlgorithmName;
public ElytronRsaKeyEncryptionJWEAlgorithmProvider(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);
}
}

View file

@ -0,0 +1,189 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import org.jboss.logging.Logger;
import org.keycloak.common.crypto.UserIdentityExtractor;
import org.keycloak.common.crypto.UserIdentityExtractorProvider;
import org.wildfly.security.asn1.ASN1;
import org.wildfly.security.asn1.DERDecoder;
import org.wildfly.security.asn1.OidsUtil;
import org.wildfly.security.x500.GeneralName;
import org.wildfly.security.x500.principal.X500AttributePrincipalDecoder;
/**
* @author <a href="mailto:david.anderson@redhat.com">David Anderson</a>
*/
public class ElytronUserIdentityExtractorProvider extends UserIdentityExtractorProvider {
private static final Logger logger = Logger.getLogger(ElytronUserIdentityExtractorProvider.class.getName());
class X500NameRDNExtractorElytronProvider extends X500NameRDNExtractor {
private String x500NameStyle;
Function<X509Certificate[],Principal> x500Name;
public X500NameRDNExtractorElytronProvider(String attrName, Function<X509Certificate[], Principal> x500Name) {
//this.x500NameStyle = BCStyle.INSTANCE.attrNameToOID(attrName);
this.x500NameStyle = OidsUtil.attributeNameToOid(OidsUtil.Category.RDN, attrName);
this.x500Name = x500Name;
}
@Override
public Object extractUserIdentity(X509Certificate[] certs) {
if (certs == null || certs.length == 0)
throw new IllegalArgumentException();
Principal name = x500Name.apply(certs);
X500AttributePrincipalDecoder xDecoder = new X500AttributePrincipalDecoder(x500NameStyle);
String cn = xDecoder.apply(name);
return cn;
}
}
/**
* Extracts the subject identifier from the subjectAltName extension.
*/
class SubjectAltNameExtractorEltronProvider extends SubjectAltNameExtractor {
// User Principal Name. Used typically by Microsoft in certificates for
// Smart Card Login
private static final String UPN_OID = "1.3.6.1.4.1.311.20.2.3";
private final int generalName;
/**
* Creates a new instance
*
* @param generalName an integer representing the general name. See
* {@link X509Certificate#getSubjectAlternativeNames()}
*/
SubjectAltNameExtractorEltronProvider(int generalName) {
this.generalName = generalName;
}
@Override
public Object extractUserIdentity(X509Certificate[] certs) {
if (certs == null || certs.length == 0) {
throw new IllegalArgumentException();
}
String subjectName = null;
logger.info("SubjPrinc " + certs[0].getSubjectX500Principal());
Collection<List<?>> subjectAlternativeNames;
try {
subjectAlternativeNames = certs[0].getSubjectAlternativeNames();
if (subjectAlternativeNames == null) {
return null;
}
for (List<?> sbjAltName : subjectAlternativeNames) {
if (sbjAltName == null)
continue;
Integer nameType = (Integer) sbjAltName.get(0);
if (nameType == generalName) {
logger.info("sbjAltName Type " + nameType);
logger.info("sbjAltName[1]: " + sbjAltName.get(1));
Object sbjObj = sbjAltName.get(1);
switch (nameType) {
case GeneralName.RFC_822_NAME:
case GeneralName.DNS_NAME:
case GeneralName.DIRECTORY_NAME:
subjectName = (String) sbjObj;
break;
case GeneralName.OTHER_NAME:
DERDecoder derDecoder = new DERDecoder((byte[])sbjObj);
derDecoder.startSequence();
boolean upnOidFound = false;
while (derDecoder.hasNextElement() && !upnOidFound) {
int asn1Type = derDecoder.peekType();
logger.info("ASN.1 Type: " + derDecoder.peekType());
switch (asn1Type) {
case ASN1.OBJECT_IDENTIFIER_TYPE:
String oid = derDecoder.decodeObjectIdentifier();
logger.info("OID: " + oid);
if(UPN_OID.equals(oid)) {
derDecoder.decodeImplicit(160);
byte[] sb = derDecoder.drainElementValue();
while(!Character.isLetterOrDigit(sb[0])) {
sb = Arrays.copyOfRange(sb, 1, sb.length);
}
subjectName = new String(sb, "UTF-8");
upnOidFound = true;
}
break;
case ASN1.UTF8_STRING_TYPE:
subjectName = derDecoder.decodeUtf8String();
break;
case ASN1.PRINTABLE_STRING_TYPE:
subjectName = derDecoder.decodePrintableString();
break;
case ASN1.UNIVERSAL_STRING_TYPE:
subjectName = derDecoder.decodeUniversalString();
break;
case ASN1.OCTET_STRING_TYPE:
subjectName = derDecoder.decodeOctetStringAsString();
break;
}
}
logger.info("Subject Alt Name: " + subjectName);
}
}
}
} catch (CertificateParsingException | UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return subjectName;
}
}
@Override
public UserIdentityExtractor getX500NameExtractor(String identifier, Function<X509Certificate[], Principal> x500Name) {
return new X500NameRDNExtractorElytronProvider(identifier, x500Name);
}
@Override
public SubjectAltNameExtractor getSubjectAltNameExtractor(int generalName) {
return new SubjectAltNameExtractorEltronProvider(generalName);
}
}

View file

@ -0,0 +1,170 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Signature;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertStore;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import org.keycloak.common.crypto.CertificateUtilsProvider;
import org.keycloak.common.crypto.CryptoConstants;
import org.keycloak.common.crypto.CryptoProvider;
import org.keycloak.common.crypto.ECDSACryptoProvider;
import org.keycloak.common.crypto.PemUtilsProvider;
import org.keycloak.common.crypto.UserIdentityExtractorProvider;
import org.keycloak.common.util.KeystoreUtil.KeystoreFormat;
import org.keycloak.crypto.JavaAlgorithm;
public class WildFlyElytronProvider implements CryptoProvider {
private Map<String, Object> providers = new ConcurrentHashMap<>();
public WildFlyElytronProvider() {
providers.put(CryptoConstants.A128KW, new AesKeyWrapAlgorithmProvider());
providers.put(CryptoConstants.RSA1_5, new ElytronRsaKeyEncryptionJWEAlgorithmProvider("RSA/ECB/PKCS1Padding"));
providers.put(CryptoConstants.RSA_OAEP, new ElytronRsaKeyEncryptionJWEAlgorithmProvider("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"));
providers.put(CryptoConstants.RSA_OAEP_256, new ElytronRsaKeyEncryption256JWEAlgorithmProvider("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"));
}
@Override
public Provider getBouncyCastleProvider() {
return null;
}
@Override
public <T> T getAlgorithmProvider(Class<T> clazz, String algorithm) {
Object o = providers.get(algorithm);
if (o == null) {
throw new IllegalArgumentException("Not found provider of algorithm type: " + algorithm);
}
return clazz.cast(o);
}
@Override
public CertificateUtilsProvider getCertificateUtils() {
return new ElytronCertificateUtils();
}
@Override
public PemUtilsProvider getPemUtils() {
return new ElytronPEMUtilsProvider();
}
@Override
public <T> T getOCSPProver(Class<T> clazz) {
return clazz.cast(new ElytronOCSPProvider());
}
@Override
public UserIdentityExtractorProvider getIdentityExtractorProvider() {
return new ElytronUserIdentityExtractorProvider();
}
@Override
public ECDSACryptoProvider getEcdsaCryptoProvider() {
return new ElytronECDSACryptoProvider();
}
@Override
public ECParameterSpec createECParams(String curveName) {
AlgorithmParameters params;
try {
params = AlgorithmParameters.getInstance("EC");
params.init(new ECGenParameterSpec(curveName));
return params.getParameterSpec(ECParameterSpec.class);
} catch (Exception e) {
throw new RuntimeException("Failed to generate EC parameter spec", e);
}
}
@Override
public KeyPairGenerator getKeyPairGen(String algorithm) throws NoSuchAlgorithmException {
return KeyPairGenerator.getInstance(algorithm);
}
@Override
public KeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException {
if("ECDSA".equals(algorithm)) {
// ECDSA is not a listed JavaSE KeyFactory algorithm
// see https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html#cipher-algorithm-names
algorithm = "EC";
}
return KeyFactory.getInstance(algorithm);
}
@Override
public Cipher getAesCbcCipher() throws NoSuchAlgorithmException, NoSuchPaddingException {
return Cipher.getInstance("AES/CBC/PKCS5Padding");
}
@Override
public Cipher getAesGcmCipher() throws NoSuchAlgorithmException, NoSuchPaddingException {
return Cipher.getInstance("AES/GCM/NoPadding");
}
@Override
public SecretKeyFactory getSecretKeyFact(String keyAlgorithm) throws NoSuchAlgorithmException {
return SecretKeyFactory.getInstance(keyAlgorithm);
}
@Override
public KeyStore getKeyStore(KeystoreFormat format) throws KeyStoreException {
return KeyStore.getInstance(format.toString());
}
@Override
public CertificateFactory getX509CertFactory() throws CertificateException {
return CertificateFactory.getInstance("X.509");
}
@Override
public CertStore getCertStore(CollectionCertStoreParameters certStoreParams) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
return CertStore.getInstance("Collection", certStoreParams);
}
@Override
public CertPathBuilder getCertPathBuilder() throws NoSuchAlgorithmException {
return CertPathBuilder.getInstance("PKIX");
}
@Override
public Signature getSignature(String sigAlgName) throws NoSuchAlgorithmException {
return Signature.getInstance(JavaAlgorithm.getJavaAlgorithm(sigAlgName));
}
}

View file

@ -0,0 +1,18 @@
#
# Copyright 2022 Red Hat, Inc. and/or its affiliates
# and other contributors as indicated by the @author tags.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
org.keycloak.crypto.elytron.WildFlyElytronProvider

View file

@ -0,0 +1,88 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron.test;
import static org.junit.Assert.assertArrayEquals;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import javax.security.auth.x500.X500Principal;
import org.junit.Test;
import org.keycloak.crypto.elytron.ElytronCertificateUtils;
import org.wildfly.security.x500.GeneralName;
import org.wildfly.security.x500.cert.CRLDistributionPoint;
import org.wildfly.security.x500.cert.CRLDistributionPoint.DistributionPointName;
import org.wildfly.security.x500.cert.CRLDistributionPoint.FullNameDistributionPointName;
import org.wildfly.security.x500.cert.CRLDistributionPointsExtension;
import org.wildfly.security.x500.cert.X509CertificateBuilder;
/**
* @author <a href="mailto:david.anderson@redhat.com">David Anderson</a>
*/
public class CRLDistributionPointTest {
@Test
public void getCrlDistPoint() throws CertificateException, NoSuchAlgorithmException, IOException {
X509Certificate cert = createCRLcert();
List<String> expect = new ArrayList<>();
expect.add("http://crl.test.com");
ElytronCertificateUtils bcutil = new ElytronCertificateUtils();
List<String> crldp = bcutil.getCRLDistributionPoints(cert);
assertArrayEquals(expect.toArray(), crldp.toArray());
}
private X509Certificate createCRLcert() throws CertificateException, NoSuchAlgorithmException {
X500Principal dn = new X500Principal("CN=testuser,OU=UNIT,O=TST");
List<CRLDistributionPoint> distributionPoints = new ArrayList<>();
List<GeneralName> fullName = new ArrayList<>();
fullName.add(new GeneralName.URIName("http://crl.test.com"));
DistributionPointName distributionPoint = new FullNameDistributionPointName(fullName);
CRLDistributionPoint arg0 = new CRLDistributionPoint(distributionPoint, null, null);
distributionPoints.add(arg0);
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").genKeyPair();
X509CertificateBuilder cbuilder = new X509CertificateBuilder()
.setSubjectDn(dn)
.setIssuerDn(dn)
.setSigningKey(keyPair.getPrivate())
.setPublicKey(keyPair.getPublic())
.addExtension(new CRLDistributionPointsExtension(false, distributionPoints))
.setSignatureAlgorithmName("SHA256withRSA");
return cbuilder.build();
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron.test;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.junit.Test;
import org.keycloak.jose.JWETest;
import org.keycloak.jose.jwe.JWEConstants;
public class ElytronCryptoJWETest extends JWETest {
@Test
public void testDirect_Aes256CbcHmacSha512() throws Exception {
final SecretKey aesKey = new SecretKeySpec(AES_256_KEY, "AES");
final SecretKey hmacKey = new SecretKeySpec(HMAC_SHA512_KEY, "HMACSHA2");
testDirectEncryptAndDecrypt(aesKey, hmacKey, JWEConstants.A256CBC_HS512, PAYLOAD, true);
}
@Override
public void testAesKW_Aes128CbcHmacSha256() throws Exception {
// Skipping this test, this test fails when not using BC provider.
}
}

View file

@ -0,0 +1,24 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron.test;
import org.keycloak.jose.jwk.JWKTest;
public class ElytronCryptoJWKTest extends JWKTest {
}

View file

@ -0,0 +1,23 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron.test;
import org.keycloak.RSAVerifierTest;
public class ElytronCryptoRSAVerifierTest extends RSAVerifierTest {
}

View file

@ -0,0 +1,53 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron.test;
import java.security.SecureRandom;
import java.util.UUID;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.jose.HmacTest;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.HMACProvider;
/**
* @author <a href="mailto:david.anderson@redhat.com">David Anderson</a>
*/
public class ElytronHmacTest extends HmacTest {
@Test
public void testHmacSignaturesUsingKeyGen() throws Exception {
KeyGenerator keygen = KeyGenerator.getInstance("HmacSHA256");
SecureRandom random = SecureRandom.getInstance("NativePRNG");
random.setSeed(UUID.randomUUID().toString().getBytes());
keygen.init(random);
SecretKey secret = keygen.generateKey();
String encoded = new JWSBuilder().content("12345678901234567890".getBytes())
.hmac256(secret);
System.out.println("length: " + encoded.length());
JWSInput input = new JWSInput(encoded);
Assert.assertTrue(HMACProvider.verify(input, secret));
}
}

View file

@ -0,0 +1,24 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron.test;
import org.keycloak.util.JWKSUtilsTest;
public class ElytronJWKSUtilsTest extends JWKSUtilsTest {
}

View file

@ -0,0 +1,78 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron.test;
import org.junit.Before;
import org.keycloak.KeyPairVerifierTest;
/**
* Test with default security provider and non-fips bouncycastle
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ElytronKeyPairVerifierTest extends KeyPairVerifierTest {
@Before
public void initPrivKeys() {
// The parent private key is not in a supported format, using a PKCS#8 formatted key
privateKey1 = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKtWsK5O0CtuBpnM" +
"vWG+HTG0vmZzujQ2o9WdheQu+BzCILcGMsbDW0YQaglpcO5JpGWWhubnckGGPHfd" +
"Q2/7nP9QwbiTK0FbGF41UqcvoaCqU1psxoV88s8IXyQCAqeyLv00yj6foqdJjxh5" +
"SZ5z+na+M7Y2OxIBVxYRAxWEnfUvAgMBAAECgYB+Y7yBWHIHF2qXGYi6CVvPxtyN" +
"BuFcktHYShLyeBNeY3VujYv3QzSZQpJ1zuoXXQuARMHOovyNiVAhu357pMfx9wSk" +
"oKNSXKrQx/+9Vt9lI1pXJxjXedPOjbuI/JZAcrk0u4nOfXG/HGtR5cjoDZYWkYQE" +
"tsePCnHlZAb0D7axwQJBAO92f00Tvkc9NU/EGqwR3bPXRMqSX0JnG7XRBvLeJBCZ" +
"YsQn0s2bLdpy8qsTeAyJg1ZvrEc8qIio5HVqzsvbhpMCQQC3K9A6UK+vmQCNWqsQ" +
"pdqWPRPN7CPB67FzSmyS8CtMjY6jTvSHrkamggotz2N/5QDr1xG2q7A/3dpkq1bT" +
"pTx1AkAXZjjiSz+Yrn57IOqKTeSgIjTypoLwdirbBWXsbZCQnqxsBogu1y8P3ZOg" +
"6/IbJ4TR+W+YNnExiW9pmdpDSVxJAkEAplTq6YmLf/F4RuQmox94tyUPbtcYQWg9" +
"42uZ3HSrXQDOng18kBj5nwpHJAJHYEQb6g2K0E5n5hcX0oKkfdx2YQJAcSKAmFiD" +
"7KQ6+vVqJlQwVPvYdTSOeZB7YVV6S4b4slS3ZObsa0yNMWgal/QnCtW5k3f185gC" +
"Wj6dOLGB5btfxg==";
// The parent private key is not in a supported format, using a PKCS#8 formatted key
privateKey2048 = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDhXcyk6e4qx0Ft" +
"HVTM2Mr2jmZ4QxDizlWnKSG/UOmpOKUo6IQftVD9e2M3HDTKOcUGKUKekrrI32YM" +
"QdsETNpGO12uBWGQh6OJpcUE/kwGFRDmX27wTchkLcTynAONUXRn27RHiUZ5SDaT" +
"o740q6aoi/GgsJO4bTN/ndty0axF3nsu4WKmAKQ3LG+xw6G6WxsQxisvCVvlswaD" +
"H2/5EKfC+6c0RvtEr7mnM/6IfUxF6NGxf5TuH3MdGfaBCEiXtLMb0Zd5u49avgor" +
"B8wdbLQ8S5Cux9ZFeb391MRq9t82S/kTnfNZeNbJ/3d7ORWEFfRiheZ/pQ7s8CW+" +
"yuOejdO7AgMBAAECggEBALmIIA5wK0t6aGls6UAPBeA+0SsWg1NE7IzGNusqsIJI" +
"iOeJrCPygC9+IerfxLHrJ0FwPFERmMX/7CIRIT6ECnohK3k1IuH6WG7cUrtOosWr" +
"GBOf41PfpSab63STbfUsZrmNzPfLkoIMKioXdmIkIfrF4vEYDTSaafgYu+3loX6O" +
"I7zQgIaziJSD30iheFzm79VTSEHknvwGKdaKeQIAG4E2QMuAipz0Ggfgvkw7HfMO" +
"rOYd996r37ZXhfs2IPlDKLJa0AFpCkQhjmRHjxFOejrE3eG8bjz8PCQ7aAAFItD8" +
"4l3ce6m/jCWaZJzXGj3cJpXjiGraLYaxTWKbp3fENbkCgYEA8J+S8+SqvzzGD7wK" +
"7cb/cYWlSxDRUSZ77x0iNcxMkdrXcrvFpGEYcJWDhrygcn8/+81LC8/JHvWJFfhy" +
"yqQpJqmu8mTy/FtTnf26eYdYqR9QevLBCXOrg65c6M528gss5Oy7f/6Tq8AgTpJk" +
"mIOZ/Z4bGL1BubmuXETeHcdEAp8CgYEA78SiAdXzouaclMlvHWE/ch9EeTSpqJKP" +
"fmWOUDP7e/oY38pJRgJZO2nYaNEgpjepDwjuX49VMWDdJjtw+rYL1MT7rGuiJaRR" +
"3YmV08thLGlakU1iWjvT1LOYuq4OGj5/AkKcDGjEqCGxclqvPtNF83IWoNexxLqh" +
"Au6tT0/mVWUCgYEAmHVC8u1Lkme7RnTqp8WSTCdVl75MIZK0q8hVyKhtS2zRXYzD" +
"qWcryQmykEgrkOA3dh+ZER7SW59PAHCuqt5ghHK2ujZkDqj+zffZku7CqkWBBKWS" +
"0Z5Mad6sV4WZr7qM829bTbnLbuMIlUAEJO4dP6hRmtcvMbIIW8X2xf9fhBkCgYEA" +
"gJqnivSHSckIE4Y34zpWHZBH2fs1RQXXkaRHQR2gtk7fKKoHw1VfJ08OlKoXKRCR" +
"zU6tDPSEbYfXFrqrTs52ahl+JG1W+3m3r2wswP1Fkdywh19KcbvFU0FBml/hkJIU" +
"7dFsgftv//6SfxPFC52m131KRdtrrmmsEzaSHwhsM0ECgYBWwq+Su4ftErsKKYNJ" +
"Zx0Aq8cBGqSOz8+h4oQRjxi7rN/Rn1NAzmW4L0TAFqZhxK6xZFx1zP70dgbnxcWe" +
"Zer7cRS4Vsn4uNvxhYGB4+NIcOhL/r7/7OoHVvm5Cn+NgVthCXnRQ9E9MX66XV5C" +
"jLsXjc2CPf/lwNFqsVl7dlPNmg==";
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron.test;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import javax.security.auth.x500.X500Principal;
import org.junit.Test;
import org.keycloak.crypto.elytron.ElytronOCSPProvider;
import org.wildfly.security.x500.GeneralName;
import org.wildfly.security.x500.cert.AccessDescription;
import org.wildfly.security.x500.cert.AuthorityInformationAccessExtension;
import org.wildfly.security.x500.cert.X509CertificateBuilder;
/**
* @author <a href="mailto:david.anderson@redhat.com">David Anderson</a>
*/
public class ElytronOCSPPoviderTest extends ElytronOCSPProvider {
@Test
public void responderURITest() throws NoSuchAlgorithmException, CertificateException, URISyntaxException, KeyStoreException, IOException, CertPathValidatorException, InvalidAlgorithmParameterException {
X509Certificate cert = createCert();
List<String> luri = getResponderURIs(cert);
assertEquals(1, luri.size());
assertEquals("http://test.localhost/check", luri.get(0));
}
private X509Certificate createCert() throws NoSuchAlgorithmException, CertificateException {
X500Principal dn = new X500Principal("CN=testuser,OU=UNIT,O=TST");
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").genKeyPair();
List<AccessDescription> accessDescriptions = new ArrayList<>();
String accessMethodId = "1.3.6.1.5.5.7.48.1";
GeneralName accessLocation = new GeneralName.URIName("http://test.localhost/check");
AccessDescription adesc = new AccessDescription(accessMethodId, accessLocation);
accessDescriptions.add(adesc);
X509CertificateBuilder cbuilder = new X509CertificateBuilder()
.setSubjectDn(dn)
.setIssuerDn(dn)
.setSigningKey(keyPair.getPrivate())
.setPublicKey(keyPair.getPublic())
.addExtension(new AuthorityInformationAccessExtension(accessDescriptions))
.setSignatureAlgorithmName("SHA256withRSA");
return cbuilder.build();
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron.test;
import static org.junit.Assert.assertEquals;
import java.security.NoSuchAlgorithmException;
import org.junit.ClassRule;
import org.junit.Test;
import org.keycloak.rule.CryptoInitRule;
public class ElytronPemUtilsTest {
@ClassRule
public static CryptoInitRule cinit = new CryptoInitRule();
@Test
public void testGenerateThumbprintSha1() throws NoSuchAlgorithmException {
String[] test = new String[] {"abcdefg"};
String encoded = org.keycloak.common.util.PemUtils.generateThumbprint(test, "SHA-1");
assertEquals(27, encoded.length());
}
@Test
public void testGenerateThumbprintSha256() throws NoSuchAlgorithmException {
String[] test = new String[] {"abcdefg"};
String encoded = org.keycloak.common.util.PemUtils.generateThumbprint(test, "SHA-256");
assertEquals(43, encoded.length());
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron.test;
import org.keycloak.RSAVerifierTest;
public class ElytronRSAVerifierTest extends RSAVerifierTest {
}

View file

@ -0,0 +1,58 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto.elytron.test;
import java.security.SecureRandom;
import org.jboss.logging.Logger;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.rule.CryptoInitRule;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ElytronSecureRandomTest {
@ClassRule
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
protected static final Logger logger = Logger.getLogger(ElytronSecureRandomTest.class);
@Test
public void testSecureRandom() throws Exception {
logger.info(CryptoIntegration.dumpJavaSecurityProviders());
SecureRandom sc1 = new SecureRandom();
logger.infof(dumpSecureRandom("new SecureRandom()", sc1));
SecureRandom sc3 = SecureRandom.getInstance("SHA1PRNG");
logger.infof(dumpSecureRandom("SecureRandom.getInstance(\"SHA1PRNG\")", sc3));
Assert.assertEquals("SHA1PRNG", sc3.getAlgorithm());
}
private String dumpSecureRandom(String prefix, SecureRandom secureRandom) {
StringBuilder builder = new StringBuilder(prefix + ": algorithm: " + secureRandom.getAlgorithm() + ", provider: " + secureRandom.getProvider() + ", random numbers: ");
for (int i=0; i < 5; i++) {
builder.append(secureRandom.nextInt(1000) + ", ");
}
return builder.toString();
}
}

View file

@ -1,5 +1,12 @@
package org.keycloak.crypto.fips; package org.keycloak.crypto.fips;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider; import java.security.Provider;
import java.security.spec.ECField; import java.security.spec.ECField;
import java.security.spec.ECFieldF2m; import java.security.spec.ECFieldF2m;
@ -8,9 +15,19 @@ import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint; import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve; import java.security.spec.EllipticCurve;
import java.security.Security; import java.security.Security;
import java.security.Signature;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertStore;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import org.bouncycastle.asn1.x9.ECNamedCurveTable; import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.fips.FipsRSA; import org.bouncycastle.crypto.fips.FipsRSA;
@ -23,6 +40,9 @@ import org.keycloak.common.crypto.CryptoConstants;
import org.keycloak.common.crypto.CertificateUtilsProvider; import org.keycloak.common.crypto.CertificateUtilsProvider;
import org.keycloak.common.crypto.PemUtilsProvider; import org.keycloak.common.crypto.PemUtilsProvider;
import org.keycloak.common.crypto.UserIdentityExtractorProvider; import org.keycloak.common.crypto.UserIdentityExtractorProvider;
import org.keycloak.common.util.BouncyIntegration;
import org.keycloak.common.util.KeystoreUtil.KeystoreFormat;
import org.keycloak.crypto.JavaAlgorithm;
/** /**
@ -118,4 +138,62 @@ public class FIPS1402Provider implements CryptoProvider {
public <T> T getOCSPProver(Class<T> clazz) { public <T> T getOCSPProver(Class<T> clazz) {
return clazz.cast(new BCFIPSOCSPProvider()); return clazz.cast(new BCFIPSOCSPProvider());
} }
@Override
public KeyPairGenerator getKeyPairGen(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
return KeyPairGenerator.getInstance(algorithm, BouncyIntegration.PROVIDER);
}
@Override
public KeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
return KeyFactory.getInstance(algorithm , BouncyIntegration.PROVIDER);
}
@Override
public Cipher getAesCbcCipher() throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
return Cipher.getInstance("AES/CBC/PKCS7Padding", BouncyIntegration.PROVIDER);
}
@Override
public Cipher getAesGcmCipher() throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
return Cipher.getInstance("AES/GCM/NoPadding", BouncyIntegration.PROVIDER);
}
@Override
public SecretKeyFactory getSecretKeyFact(String keyAlgorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
return SecretKeyFactory.getInstance(keyAlgorithm, BouncyIntegration.PROVIDER);
}
@Override
public KeyStore getKeyStore(KeystoreFormat format) throws KeyStoreException, NoSuchProviderException {
if (format == KeystoreFormat.JKS) {
return KeyStore.getInstance(format.toString());
} else {
return KeyStore.getInstance(format.toString(), BouncyIntegration.PROVIDER);
}
}
@Override
public CertificateFactory getX509CertFactory() throws CertificateException, NoSuchProviderException {
return CertificateFactory.getInstance("X.509", BouncyIntegration.PROVIDER);
}
@Override
public CertStore getCertStore(CollectionCertStoreParameters certStoreParams) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException {
return CertStore.getInstance("Collection", certStoreParams, BouncyIntegration.PROVIDER);
}
@Override
public CertPathBuilder getCertPathBuilder() throws NoSuchAlgorithmException, NoSuchProviderException {
return CertPathBuilder.getInstance("PKIX", BouncyIntegration.PROVIDER);
}
@Override
public Signature getSignature(String sigAlgName) throws NoSuchAlgorithmException, NoSuchProviderException {
return Signature.getInstance(JavaAlgorithm.getJavaAlgorithm(sigAlgName), BouncyIntegration.PROVIDER);
}
} }

View file

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

View file

@ -38,10 +38,18 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>${keycloak.crypto.artifactId}</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.apache.httpcomponents</groupId> <groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId> <artifactId>httpclient</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
@ -67,9 +75,9 @@
<artifact>org.keycloak:keycloak-core</artifact> <artifact>org.keycloak:keycloak-core</artifact>
<includes> <includes>
<include>org/keycloak/util/**</include> <include>org/keycloak/util/**</include>
<include>org/keycloak/crypto/**</include>
<include>org/keycloak/json/**</include> <include>org/keycloak/json/**</include>
<include>org/keycloak/jose/jws/**</include> <include>org/keycloak/jose/**</include>
<include>org/keycloak/jose/jwk/**</include>
<include>org/keycloak/representations/adapters/config/**</include> <include>org/keycloak/representations/adapters/config/**</include>
<include>org/keycloak/representations/adapters/action/**</include> <include>org/keycloak/representations/adapters/action/**</include>
<include>org/keycloak/representations/AccessTokenResponse.class</include> <include>org/keycloak/representations/AccessTokenResponse.class</include>
@ -92,24 +100,7 @@
<include>org/keycloak/TokenCategory.class</include> <include>org/keycloak/TokenCategory.class</include>
</includes> </includes>
</filter> </filter>
<filter>
<artifact>org.keycloak:keycloak-common</artifact>
<includes>
<include>org/keycloak/common/util/**</include>
</includes>
</filter>
<filter>
<artifact>org.bouncycastle:bcprov-jdk15on</artifact>
<includes>
<include>**/**</include>
</includes>
</filter>
<filter>
<artifact>org.bouncycastle:bcpkix-jdk15on</artifact>
<excludes>
<exclude>**/**</exclude>
</excludes>
</filter>
<filter> <filter>
<artifact>com.fasterxml.jackson.core:jackson-core</artifact> <artifact>com.fasterxml.jackson.core:jackson-core</artifact>
<includes> <includes>

View file

@ -27,6 +27,7 @@ import org.keycloak.client.admin.cli.aesh.AeshEnhancer;
import org.keycloak.client.admin.cli.aesh.Globals; import org.keycloak.client.admin.cli.aesh.Globals;
import org.keycloak.client.admin.cli.aesh.ValveInputStream; import org.keycloak.client.admin.cli.aesh.ValveInputStream;
import org.keycloak.client.admin.cli.commands.KcAdmCmd; import org.keycloak.client.admin.cli.commands.KcAdmCmd;
import org.keycloak.common.crypto.CryptoIntegration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -38,6 +39,8 @@ public class KcAdmMain {
public static void main(String [] args) { public static void main(String [] args) {
CryptoIntegration.init(KcAdmMain.class.getClassLoader());
Globals.stdin = new ValveInputStream(); Globals.stdin = new ValveInputStream();
Settings settings = new SettingsBuilder() Settings settings = new SettingsBuilder()

View file

@ -38,6 +38,14 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>${keycloak.crypto.artifactId}</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.apache.httpcomponents</groupId> <groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId> <artifactId>httpclient</artifactId>
@ -66,9 +74,9 @@
<artifact>org.keycloak:keycloak-core</artifact> <artifact>org.keycloak:keycloak-core</artifact>
<includes> <includes>
<include>org/keycloak/util/**</include> <include>org/keycloak/util/**</include>
<include>org/keycloak/crypto/**</include>
<include>org/keycloak/json/**</include> <include>org/keycloak/json/**</include>
<include>org/keycloak/jose/jws/**</include> <include>org/keycloak/jose/**</include>
<include>org/keycloak/jose/jwk/**</include>
<include>org/keycloak/representations/adapters/config/**</include> <include>org/keycloak/representations/adapters/config/**</include>
<include>org/keycloak/representations/AccessTokenResponse.class</include> <include>org/keycloak/representations/AccessTokenResponse.class</include>
<include>org/keycloak/representations/idm/ClientRepresentation.class</include> <include>org/keycloak/representations/idm/ClientRepresentation.class</include>
@ -84,6 +92,7 @@
<artifact>org.keycloak:keycloak-common</artifact> <artifact>org.keycloak:keycloak-common</artifact>
<includes> <includes>
<include>org/keycloak/common/util/**</include> <include>org/keycloak/common/util/**</include>
<include>org/keycloak/common/crypto/**</include>
</includes> </includes>
</filter> </filter>
<filter> <filter>

View file

@ -11,6 +11,7 @@ import org.keycloak.client.registration.cli.aesh.AeshEnhancer;
import org.keycloak.client.registration.cli.aesh.ValveInputStream; import org.keycloak.client.registration.cli.aesh.ValveInputStream;
import org.keycloak.client.registration.cli.aesh.Globals; import org.keycloak.client.registration.cli.aesh.Globals;
import org.keycloak.client.registration.cli.commands.KcRegCmd; import org.keycloak.client.registration.cli.commands.KcRegCmd;
import org.keycloak.common.crypto.CryptoIntegration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -22,6 +23,8 @@ public class KcRegMain {
public static void main(String [] args) { public static void main(String [] args) {
CryptoIntegration.init(KcRegMain.class.getClassLoader());
Globals.stdin = new ValveInputStream(); Globals.stdin = new ValveInputStream();
Settings settings = new SettingsBuilder() Settings settings = new SettingsBuilder()

View file

@ -35,8 +35,8 @@
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.httpcomponents</groupId> <groupId>org.keycloak</groupId>
<artifactId>httpclient</artifactId> <artifactId>${keycloak.crypto.artifactId}</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>

View file

@ -1610,6 +1610,11 @@
<artifactId>keycloak-crypto-fips1402</artifactId> <artifactId>keycloak-crypto-fips1402</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-crypto-elytron</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-cli</artifactId> <artifactId>keycloak-admin-cli</artifactId>

View file

@ -10,23 +10,18 @@ import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.StringReader;
import java.security.KeyPair;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Scanner; import java.util.Scanner;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.Arrays;
import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.keycloak.common.crypto.CryptoIntegration; import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.crypto.CryptoProvider; import org.keycloak.common.crypto.CryptoProvider;
import org.keycloak.common.util.Base64; import org.keycloak.common.util.Base64;
import org.keycloak.common.util.DerUtils; import org.keycloak.common.util.DerUtils;
import org.keycloak.common.util.PemUtils;
import org.keycloak.dom.saml.v2.assertion.NameIDType; import org.keycloak.dom.saml.v2.assertion.NameIDType;
import org.keycloak.dom.saml.v2.assertion.SubjectType.STSubType; import org.keycloak.dom.saml.v2.assertion.SubjectType.STSubType;
import org.keycloak.dom.saml.v2.protocol.ResponseType; import org.keycloak.dom.saml.v2.protocol.ResponseType;
@ -66,7 +61,7 @@ public class AssertionUtilTest {
byte[] validSignature = Base64.decode(signatureElement.getTextContent()); byte[] validSignature = Base64.decode(signatureElement.getTextContent());
// change the signature value slightly // change the signature value slightly
byte[] invalidSignature = Arrays.clone(validSignature); byte[] invalidSignature = Arrays.copyOf(validSignature, validSignature.length);
invalidSignature[0] ^= invalidSignature[0]; invalidSignature[0] ^= invalidSignature[0];
signatureElement.setTextContent(Base64.encodeBytes(invalidSignature)); signatureElement.setTextContent(Base64.encodeBytes(invalidSignature));
@ -107,6 +102,7 @@ public class AssertionUtilTest {
} }
private PrivateKey extractPrivateKey() throws IOException { private PrivateKey extractPrivateKey() throws IOException {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
try (Scanner sc = new Scanner(getEncryptedIdTestFileInputStream())) { try (Scanner sc = new Scanner(getEncryptedIdTestFileInputStream())) {
while (sc.hasNextLine()) { while (sc.hasNextLine()) {
@ -124,11 +120,7 @@ public class AssertionUtilTest {
} }
} }
assertNotEquals("PEM certificate not found in test data", 0, sb.length()); assertNotEquals("PEM certificate not found in test data", 0, sb.length());
PEMParser pp = new PEMParser(new StringReader(sb.toString())); return PemUtils.decodePrivateKey(sb.toString());
PEMKeyPair pemKeyPair = (PEMKeyPair) pp.readObject();
KeyPair kp = new JcaPEMKeyConverter().getKeyPair(pemKeyPair);
pp.close();
return kp.getPrivate();
} }
} }

View file

@ -17,8 +17,8 @@
package org.keycloak.credential.hash; package org.keycloak.credential.hash;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.util.Base64; import org.keycloak.common.util.Base64;
import org.keycloak.common.util.BouncyIntegration;
import org.keycloak.models.PasswordPolicy; import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.credential.PasswordCredentialModel; import org.keycloak.models.credential.PasswordCredentialModel;
@ -126,7 +126,8 @@ public class Pbkdf2PasswordHashProvider implements PasswordHashProvider {
private SecretKeyFactory getSecretKeyFactory() { private SecretKeyFactory getSecretKeyFactory() {
try { try {
return SecretKeyFactory.getInstance(pbkdf2Algorithm, BouncyIntegration.PROVIDER); return CryptoIntegration.getProvider().getSecretKeyFact(pbkdf2Algorithm);
} catch (NoSuchAlgorithmException | NoSuchProviderException e) { } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
throw new RuntimeException("PBKDF2 algorithm not found", e); throw new RuntimeException("PBKDF2 algorithm not found", e);
} }

View file

@ -18,28 +18,8 @@
package org.keycloak.authentication.authenticators.x509; package org.keycloak.authentication.authenticators.x509;
import org.apache.http.client.methods.HttpGet; import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.CERTIFICATE_POLICY_MODE_ANY;
import org.keycloak.common.crypto.CryptoConstants;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.utils.OCSPProvider;
import org.keycloak.common.util.BouncyIntegration;
import org.keycloak.common.util.Time;
import org.keycloak.connections.httpclient.HttpClientProvider;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.saml.common.exceptions.ProcessingException;
import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
import org.keycloak.services.ServicesLogger;
import org.keycloak.truststore.TruststoreProvider;
import org.keycloak.utils.CRLUtils;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.security.auth.x500.X500Principal;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.File; import java.io.File;
@ -49,19 +29,19 @@ import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.cert.CRLException;
import java.security.cert.CertPathBuilder; import java.security.cert.CertPathBuilder;
import java.security.cert.CertPathValidatorException; import java.security.cert.CertPathValidatorException;
import java.security.cert.CertStore; import java.security.cert.CertStore;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters; import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.CRLException;
import java.security.cert.PKIXBuilderParameters; import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathBuilderResult; import java.security.cert.PKIXCertPathBuilderResult;
import java.security.cert.TrustAnchor; import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.security.cert.X509CertSelector;
import java.security.cert.X509CRL; import java.security.cert.X509CRL;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -73,11 +53,30 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.security.auth.x500.X500Principal;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils;
import org.keycloak.common.crypto.CryptoIntegration;
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.CERTIFICATE_POLICY_MODE_ANY; import org.keycloak.common.util.Time;
import org.keycloak.connections.httpclient.HttpClientProvider;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.saml.common.exceptions.ProcessingException;
import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
import org.keycloak.services.ServicesLogger;
import org.keycloak.truststore.TruststoreProvider;
import org.keycloak.utils.CRLUtils;
import org.keycloak.utils.OCSPProvider;
/** /**
* @author <a href="mailto:pnalyvayko@agi.com">Peter Nalyvayko</a> * @author <a href="mailto:pnalyvayko@agi.com">Peter Nalyvayko</a>
@ -643,12 +642,11 @@ public class CertificateValidator {
for (X509Certificate clientCert : certChain) { for (X509Certificate clientCert : certChain) {
intermediateCerts.add(clientCert); intermediateCerts.add(clientCert);
} }
CertStore intermediateCertStore = CertStore.getInstance("Collection", CertStore intermediateCertStore = CryptoIntegration.getProvider().getCertStore(new CollectionCertStoreParameters(intermediateCerts));
new CollectionCertStoreParameters(intermediateCerts), BouncyIntegration.PROVIDER);
pkixParams.addCertStore(intermediateCertStore); pkixParams.addCertStore(intermediateCertStore);
// Build and verify the certification chain // Build and verify the certification chain
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", BouncyIntegration.PROVIDER); CertPathBuilder builder = CryptoIntegration.getProvider().getCertPathBuilder();
PKIXCertPathBuilderResult result = PKIXCertPathBuilderResult result =
(PKIXCertPathBuilderResult) builder.build(pkixParams); (PKIXCertPathBuilderResult) builder.build(pkixParams);
return result; return result;

View file

@ -1,7 +1,8 @@
package org.keycloak.protocol.docker.installation.compose; package org.keycloak.protocol.docker.installation.compose;
import org.keycloak.common.util.BouncyIntegration; import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.util.CertificateUtils; import org.keycloak.common.util.CertificateUtils;
import org.keycloak.crypto.KeyType;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.KeyPairGenerator; import java.security.KeyPairGenerator;
@ -24,9 +25,8 @@ public class DockerComposeCertsDirectory {
public DockerComposeCertsDirectory(final String directoryName, final Certificate realmCert, final String registryCertFilename, final String registryKeyFilename, final String idpCertTrustChainFilename, final String realmName) { public DockerComposeCertsDirectory(final String directoryName, final Certificate realmCert, final String registryCertFilename, final String registryKeyFilename, final String idpCertTrustChainFilename, final String realmName) {
this.directoryName = directoryName; this.directoryName = directoryName;
final KeyPairGenerator keyGen;
try { try {
keyGen = KeyPairGenerator.getInstance("RSA", BouncyIntegration.PROVIDER); final KeyPairGenerator keyGen = CryptoIntegration.getProvider().getKeyPairGen(KeyType.RSA);
keyGen.initialize(2048, new SecureRandom()); keyGen.initialize(2048, new SecureRandom());
final KeyPair keypair = keyGen.generateKeyPair(); final KeyPair keypair = keyGen.generateKeyPair();

View file

@ -23,9 +23,10 @@ import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
import javax.ws.rs.NotAcceptableException; import javax.ws.rs.NotAcceptableException;
import javax.ws.rs.NotFoundException; import javax.ws.rs.NotFoundException;
import org.keycloak.common.util.BouncyIntegration; import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.PemUtils;
import org.keycloak.common.util.StreamUtil; import org.keycloak.common.util.StreamUtil;
import org.keycloak.common.util.KeystoreUtil.KeystoreFormat;
import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType; import org.keycloak.events.admin.ResourceType;
import org.keycloak.jose.jwk.JSONWebKeySet; import org.keycloak.jose.jwk.JSONWebKeySet;
@ -228,9 +229,7 @@ public class ClientAttributeCertificateResource {
PrivateKey privateKey = null; PrivateKey privateKey = null;
X509Certificate certificate = null; X509Certificate certificate = null;
try { try {
KeyStore keyStore = null; KeyStore keyStore = CryptoIntegration.getProvider().getKeyStore(KeystoreFormat.valueOf(keystoreFormat));
if (keystoreFormat.equals("JKS")) keyStore = KeyStore.getInstance("JKS");
else keyStore = KeyStore.getInstance(keystoreFormat, BouncyIntegration.PROVIDER);
keyStore.load(inputParts.get(0).getBody(InputStream.class, null), storePassword); keyStore.load(inputParts.get(0).getBody(InputStream.class, null), storePassword);
try { try {
privateKey = (PrivateKey)keyStore.getKey(keyAlias, keyPassword); privateKey = (PrivateKey)keyStore.getKey(keyAlias, keyPassword);
@ -332,9 +331,7 @@ public class ClientAttributeCertificateResource {
private byte[] getKeystore(KeyStoreConfig config, String privatePem, String certPem) { private byte[] getKeystore(KeyStoreConfig config, String privatePem, String certPem) {
try { try {
String format = config.getFormat(); String format = config.getFormat();
KeyStore keyStore; KeyStore keyStore = CryptoIntegration.getProvider().getKeyStore(KeystoreFormat.valueOf(format));
if (format.equals("JKS")) keyStore = KeyStore.getInstance("JKS");
else keyStore = KeyStore.getInstance(format, BouncyIntegration.PROVIDER);
keyStore.load(null, null); keyStore.load(null, null);
String keyAlias = config.getKeyAlias(); String keyAlias = config.getKeyAlias();
if (keyAlias == null) keyAlias = client.getClientId(); if (keyAlias == null) keyAlias = client.getClientId();

View file

@ -24,7 +24,7 @@ import java.util.Set;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.jboss.logging.Logger.Level; import org.jboss.logging.Logger.Level;
import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.common.util.BouncyIntegration; import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.util.PemException; import org.keycloak.common.util.PemException;
import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.PemUtils;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
@ -186,11 +186,11 @@ public class NginxProxySslClientCertificateLookup extends AbstractClientCertific
// Adding the list of intermediate certificates + end user certificate // Adding the list of intermediate certificates + end user certificate
intermediateCerts.add(end_user_auth_cert); intermediateCerts.add(end_user_auth_cert);
CollectionCertStoreParameters intermediateCA_userCert = new CollectionCertStoreParameters(intermediateCerts); CollectionCertStoreParameters intermediateCA_userCert = new CollectionCertStoreParameters(intermediateCerts);
CertStore intermediateCertStore = CertStore.getInstance("Collection", intermediateCA_userCert, BouncyIntegration.PROVIDER); CertStore intermediateCertStore = CryptoIntegration.getProvider().getCertStore(intermediateCA_userCert);
pkixParams.addCertStore(intermediateCertStore); pkixParams.addCertStore(intermediateCertStore);
// Build and verify the certification chain (revocation status excluded) // Build and verify the certification chain (revocation status excluded)
CertPathBuilder certPathBuilder = CertPathBuilder.getInstance("PKIX",BouncyIntegration.PROVIDER); CertPathBuilder certPathBuilder = CryptoIntegration.getProvider().getCertPathBuilder();
CertPath certPath = certPathBuilder.build(pkixParams).getCertPath(); CertPath certPath = certPathBuilder.build(pkixParams).getCertPath();
log.debug("Certification path building OK, and contains " + certPath.getCertificates().size() + " X509 Certificates"); log.debug("Certification path building OK, and contains " + certPath.getCertificates().size() + " X509 Certificates");

View file

@ -6,7 +6,7 @@ import org.junit.Assert;
import org.junit.ClassRule; import org.junit.ClassRule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.common.crypto.CryptoIntegration; import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.util.BouncyIntegration; import org.keycloak.crypto.KeyType;
import org.keycloak.rule.CryptoInitRule; import org.keycloak.rule.CryptoInitRule;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
@ -34,7 +34,7 @@ public class CertificateValidatorTest {
*/ */
@Test @Test
public void testValidityOfCertificatesSuccess() throws GeneralSecurityException { public void testValidityOfCertificatesSuccess() throws GeneralSecurityException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", BouncyIntegration.PROVIDER); KeyPairGenerator kpg = CryptoIntegration.getProvider().getKeyPairGen(KeyType.RSA);
kpg.initialize(512); kpg.initialize(512);
KeyPair keyPair = kpg.generateKeyPair(); KeyPair keyPair = kpg.generateKeyPair();
X509Certificate certificate = CryptoIntegration.getProvider().getCertificateUtils() X509Certificate certificate = CryptoIntegration.getProvider().getCertificateUtils()
@ -60,7 +60,7 @@ public class CertificateValidatorTest {
*/ */
@Test @Test
public void testValidityOfCertificatesNotValidYet() throws GeneralSecurityException { public void testValidityOfCertificatesNotValidYet() throws GeneralSecurityException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", BouncyIntegration.PROVIDER); KeyPairGenerator kpg = CryptoIntegration.getProvider().getKeyPairGen(KeyType.RSA);
kpg.initialize(512); kpg.initialize(512);
KeyPair keyPair = kpg.generateKeyPair(); KeyPair keyPair = kpg.generateKeyPair();
X509Certificate certificate = CryptoIntegration.getProvider().getCertificateUtils() X509Certificate certificate = CryptoIntegration.getProvider().getCertificateUtils()
@ -87,7 +87,7 @@ public class CertificateValidatorTest {
*/ */
@Test @Test
public void testValidityOfCertificatesHasExpired() throws GeneralSecurityException { public void testValidityOfCertificatesHasExpired() throws GeneralSecurityException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", BouncyIntegration.PROVIDER); KeyPairGenerator kpg = CryptoIntegration.getProvider().getKeyPairGen(KeyType.RSA);
kpg.initialize(512); kpg.initialize(512);
KeyPair keyPair = kpg.generateKeyPair(); KeyPair keyPair = kpg.generateKeyPair();
X509Certificate certificate = CryptoIntegration.getProvider().getCertificateUtils() X509Certificate certificate = CryptoIntegration.getProvider().getCertificateUtils()
@ -330,9 +330,7 @@ public class CertificateValidatorTest {
private void testCertificatePolicyValidation(String expectedPolicy, String mode, String... certificatePolicyOid) private void testCertificatePolicyValidation(String expectedPolicy, String mode, String... certificatePolicyOid)
throws GeneralSecurityException throws GeneralSecurityException
{ {
KeyPairGenerator kpg = CryptoIntegration.getProvider().getKeyPairGen(KeyType.RSA);
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", BouncyIntegration.PROVIDER);
kpg.initialize(512); kpg.initialize(512);
KeyPair keyPair = kpg.generateKeyPair(); KeyPair keyPair = kpg.generateKeyPair();
X509Certificate certificate = CryptoIntegration.getProvider().getCertificateUtils() X509Certificate certificate = CryptoIntegration.getProvider().getCertificateUtils()

View file

@ -33,9 +33,10 @@ import org.junit.Before;
import org.junit.ClassRule; import org.junit.ClassRule;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.keycloak.common.util.BouncyIntegration; import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.util.CertificateUtils; import org.keycloak.common.util.CertificateUtils;
import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.PemUtils;
import org.keycloak.crypto.KeyType;
import org.keycloak.protocol.docker.installation.DockerComposeYamlInstallationProvider; import org.keycloak.protocol.docker.installation.DockerComposeYamlInstallationProvider;
import org.keycloak.rule.CryptoInitRule; import org.keycloak.rule.CryptoInitRule;
@ -49,8 +50,7 @@ public class DockerComposeYamlInstallationProviderTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
final KeyPairGenerator keyGen; final KeyPairGenerator keyGen = CryptoIntegration.getProvider().getKeyPairGen(KeyType.RSA);
keyGen = KeyPairGenerator.getInstance("RSA", BouncyIntegration.PROVIDER);
keyGen.initialize(2048, new SecureRandom()); keyGen.initialize(2048, new SecureRandom());
final KeyPair keypair = keyGen.generateKeyPair(); final KeyPair keypair = keyGen.generateKeyPair();

View file

@ -61,6 +61,12 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency> <dependency>
<groupId>commons-configuration</groupId> <groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId> <artifactId>commons-configuration</artifactId>

View file

@ -1,7 +1,8 @@
package org.keycloak.testsuite.util; package org.keycloak.testsuite.util;
import org.keycloak.common.util.BouncyIntegration; import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.crypto.KeyStatus; import org.keycloak.crypto.KeyStatus;
import org.keycloak.crypto.KeyType;
import org.keycloak.crypto.KeyUse; import org.keycloak.crypto.KeyUse;
import org.keycloak.representations.idm.KeysMetadataRepresentation; import org.keycloak.representations.idm.KeysMetadataRepresentation;
@ -22,7 +23,7 @@ public class KeyUtils {
public static PublicKey publicKeyFromString(String key) { public static PublicKey publicKeyFromString(String key) {
try { try {
KeyFactory kf = KeyFactory.getInstance("RSA", BouncyIntegration.PROVIDER); KeyFactory kf = CryptoIntegration.getProvider().getKeyFactory(KeyType.RSA);
byte[] encoded = Base64.getDecoder().decode(key); byte[] encoded = Base64.getDecoder().decode(key);
return kf.generatePublic(new X509EncodedKeySpec(encoded)); return kf.generatePublic(new X509EncodedKeySpec(encoded));
} catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchProviderException e) { } catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchProviderException e) {
@ -32,7 +33,7 @@ public class KeyUtils {
public static PrivateKey privateKeyFromString(String key) { public static PrivateKey privateKeyFromString(String key) {
try { try {
KeyFactory kf = KeyFactory.getInstance("RSA", BouncyIntegration.PROVIDER); KeyFactory kf = CryptoIntegration.getProvider().getKeyFactory(KeyType.RSA);
byte[] encoded = Base64.getDecoder().decode(key); byte[] encoded = Base64.getDecoder().decode(key);
return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded)); return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded));
} catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchProviderException e) { } catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchProviderException e) {

View file

@ -29,8 +29,8 @@ import javax.ws.rs.core.Response;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.util.Base64; import org.keycloak.common.util.Base64;
import org.keycloak.common.util.BouncyIntegration;
import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.crypto.Algorithm; import org.keycloak.crypto.Algorithm;
import org.keycloak.crypto.JavaAlgorithm; import org.keycloak.crypto.JavaAlgorithm;
@ -188,8 +188,7 @@ public class TokenSignatureUtil {
private static Signature getSignature(String sigAlgName) { private static Signature getSignature(String sigAlgName) {
try { try {
// use Bouncy Castle for signature verification intentionally Signature signature = CryptoIntegration.getProvider().getSignature(JavaAlgorithm.getJavaAlgorithm(sigAlgName));
Signature signature = Signature.getInstance(JavaAlgorithm.getJavaAlgorithm(sigAlgName), BouncyIntegration.PROVIDER);
return signature; return signature;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);

View file

@ -61,7 +61,6 @@ import org.hamcrest.Matchers;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule; import org.junit.Rule;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException; import org.keycloak.OAuthErrorException;
@ -71,9 +70,9 @@ import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
import org.keycloak.client.registration.Auth; import org.keycloak.client.registration.Auth;
import org.keycloak.client.registration.ClientRegistration; import org.keycloak.client.registration.ClientRegistration;
import org.keycloak.client.registration.ClientRegistrationException; import org.keycloak.client.registration.ClientRegistrationException;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.util.Base64; import org.keycloak.common.util.Base64;
import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.BouncyIntegration;
import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.KeycloakUriBuilder; import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.common.util.Time; import org.keycloak.common.util.Time;
@ -400,14 +399,14 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
private PrivateKey decodePrivateKey(byte[] der, String algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException { private PrivateKey decodePrivateKey(byte[] der, String algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(der); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(der);
String keyAlg = getKeyAlgorithmFromJwaAlgorithm(algorithm); String keyAlg = getKeyAlgorithmFromJwaAlgorithm(algorithm);
KeyFactory kf = KeyFactory.getInstance(keyAlg, BouncyIntegration.PROVIDER); KeyFactory kf = CryptoIntegration.getProvider().getKeyFactory(keyAlg);
return kf.generatePrivate(spec); return kf.generatePrivate(spec);
} }
private PublicKey decodePublicKey(byte[] der, String algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException { private PublicKey decodePublicKey(byte[] der, String algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
X509EncodedKeySpec spec = new X509EncodedKeySpec(der); X509EncodedKeySpec spec = new X509EncodedKeySpec(der);
String keyAlg = getKeyAlgorithmFromJwaAlgorithm(algorithm); String keyAlg = getKeyAlgorithmFromJwaAlgorithm(algorithm);
KeyFactory kf = KeyFactory.getInstance(keyAlg, BouncyIntegration.PROVIDER); KeyFactory kf = CryptoIntegration.getProvider().getKeyFactory(keyAlg);
return kf.generatePublic(spec); return kf.generatePublic(spec);
} }

View file

@ -43,15 +43,16 @@ import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.authentication.AuthenticationFlowError; import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator; import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
import org.keycloak.common.constants.ServiceAccountConstants; import org.keycloak.common.constants.ServiceAccountConstants;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.util.Base64; import org.keycloak.common.util.Base64;
import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.BouncyIntegration;
import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.KeycloakUriBuilder; import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.common.util.KeystoreUtil; import org.keycloak.common.util.KeystoreUtil;
import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.PemUtils;
import org.keycloak.common.util.Time; import org.keycloak.common.util.Time;
import org.keycloak.common.util.UriUtils; import org.keycloak.common.util.UriUtils;
import org.keycloak.common.util.KeystoreUtil.KeystoreFormat;
import org.keycloak.constants.ServiceUrlConstants; import org.keycloak.constants.ServiceUrlConstants;
import org.keycloak.crypto.Algorithm; import org.keycloak.crypto.Algorithm;
import org.keycloak.crypto.ECDSASignatureProvider; import org.keycloak.crypto.ECDSASignatureProvider;
@ -1382,7 +1383,7 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
} }
private static KeyStore getKeystore(InputStream is, String storePassword, String format) throws Exception { private static KeyStore getKeystore(InputStream is, String storePassword, String format) throws Exception {
KeyStore keyStore = format.equals("JKS") ? KeyStore.getInstance(format) : KeyStore.getInstance(format, BouncyIntegration.PROVIDER); KeyStore keyStore = CryptoIntegration.getProvider().getKeyStore(KeystoreFormat.valueOf(format));
keyStore.load(is, storePassword.toCharArray()); keyStore.load(is, storePassword.toCharArray());
return keyStore; return keyStore;
} }
@ -1455,14 +1456,14 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
private static PrivateKey decodePrivateKey(byte[] der, String algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException { private static PrivateKey decodePrivateKey(byte[] der, String algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(der); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(der);
String keyAlg = getKeyAlgorithmFromJwaAlgorithm(algorithm); String keyAlg = getKeyAlgorithmFromJwaAlgorithm(algorithm);
KeyFactory kf = KeyFactory.getInstance(keyAlg, BouncyIntegration.PROVIDER); KeyFactory kf = CryptoIntegration.getProvider().getKeyFactory(keyAlg);
return kf.generatePrivate(spec); return kf.generatePrivate(spec);
} }
private static PublicKey decodePublicKey(byte[] der, String algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException { private static PublicKey decodePublicKey(byte[] der, String algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
X509EncodedKeySpec spec = new X509EncodedKeySpec(der); X509EncodedKeySpec spec = new X509EncodedKeySpec(der);
String keyAlg = getKeyAlgorithmFromJwaAlgorithm(algorithm); String keyAlg = getKeyAlgorithmFromJwaAlgorithm(algorithm);
KeyFactory kf = KeyFactory.getInstance(keyAlg, BouncyIntegration.PROVIDER); KeyFactory kf = CryptoIntegration.getProvider().getKeyFactory(keyAlg);
return kf.generatePublic(spec); return kf.generatePublic(spec);
} }