parent
be2deb0517
commit
a8db79a68c
56 changed files with 2146 additions and 186 deletions
|
@ -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";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
72
crypto/elytron/pom.xml
Normal 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>
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
|
@ -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==";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
5
pom.xml
5
pom.xml
|
@ -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>
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue