Extract BC from keycloak-common (#13064)

Closes #12855
This commit is contained in:
David Anderson 2022-07-16 02:36:07 -05:00 committed by GitHub
parent 89028613d8
commit 8ce10df6da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 810 additions and 217 deletions

View file

@ -42,14 +42,7 @@
</keycloak.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>

View file

@ -0,0 +1,60 @@
/*
* 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.common.crypto;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
/**
* The Class CertificateUtils provides utility functions for generation of V1 and V3 {@link java.security.cert.X509Certificate}
*
*/
public interface 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
*/
public X509Certificate generateV3Certificate(KeyPair keyPair, PrivateKey caPrivateKey, X509Certificate caCert,
String subject) throws Exception;
/**
* 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
*/
public X509Certificate generateV1SelfSignedCertificate(KeyPair caKeyPair, String subject);
public X509Certificate generateV1SelfSignedCertificate(KeyPair caKeyPair, String subject, BigInteger serialNumber);
}

View file

@ -25,4 +25,22 @@ public interface CryptoProvider {
* @return
*/
<T> T getAlgorithmProvider(Class<T> clazz, String algorithm);
/**
* Get CertificateUtils implementation. Returned implementation can be dependent according to if we have
* non-fips bouncycastle or fips bouncycastle on the classpath.
*
* @return
*/
public CertificateUtilsProvider getCertificateUtils();
/**
* Get PEMUtils implementation. Returned implementation can be dependent according to if we have
* non-fips bouncycastle or fips bouncycastle on the classpath.
*
* @return
*/
public PemUtilsProvider getPemUtils();
}

View file

@ -0,0 +1,170 @@
/*
* 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.common.crypto;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import org.keycloak.common.util.Base64;
import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.DerUtils;
import org.keycloak.common.util.PemException;
/**
* Utility classes to extract PublicKey, PrivateKey, and X509Certificate from openssl generated PEM files
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public abstract class PemUtilsProvider {
public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
public static final String END_CERT = "-----END CERTIFICATE-----";
/**
* Decode a X509 Certificate from a PEM string
*
* @param cert
* @return
* @throws Exception
*/
public X509Certificate decodeCertificate(String cert) {
if (cert == null) {
return null;
}
try {
byte[] der = pemToDer(cert);
ByteArrayInputStream bis = new ByteArrayInputStream(der);
return DerUtils.decodeCertificate(bis);
} catch (Exception e) {
throw new PemException(e);
}
}
/**
* Decode a Public Key from a PEM string
*
* @param pem
* @return
* @throws Exception
*/
public PublicKey decodePublicKey(String pem) {
return decodePublicKey(pem, "RSA");
}
/**
* Decode a Public Key from a PEM string
* @param pem The pem encoded pblic key
* @param type The type of the key (RSA, EC,...)
* @return The public key or null
*/
public PublicKey decodePublicKey(String pem, String type) {
if (pem == null) {
return null;
}
try {
byte[] der = pemToDer(pem);
return DerUtils.decodePublicKey(der, type);
} catch (Exception e) {
throw new PemException(e);
}
}
/**
* Decode a Private Key from a PEM string
*
* @param pem
* @return
* @throws Exception
*/
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);
}
}
/**
* Encode a Key to a PEM string
*
* @param key
* @return
* @throws Exception
*/
public String encodeKey(Key key) {
return encode(key);
}
/**
* Encode a X509 Certificate to a PEM string
*
* @param certificate
* @return
*/
public String encodeCertificate(Certificate certificate) {
return encode(certificate);
}
public byte[] pemToDer(String pem) {
try {
pem = removeBeginEnd(pem);
return Base64.decode(pem);
} catch (IOException ioe) {
throw new PemException(ioe);
}
}
public String removeBeginEnd(String pem) {
pem = pem.replaceAll("-----BEGIN (.*)-----", "");
pem = pem.replaceAll("-----END (.*)----", "");
pem = pem.replaceAll("\r\n", "");
pem = pem.replaceAll("\n", "");
return pem.trim();
}
public String generateThumbprint(String[] certChain, String encoding) throws NoSuchAlgorithmException{
return Base64Url.encode(generateThumbprintBytes(certChain, encoding));
}
private byte[] generateThumbprintBytes(String[] certChain, String encoding) throws NoSuchAlgorithmException {
return MessageDigest.getInstance(encoding).digest(pemToDer(certChain[0]));
}
protected abstract String encode(Object obj);
}

View file

@ -17,38 +17,24 @@
package org.keycloak.common.util;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v1CertificateBuilder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;
import org.keycloak.common.crypto.CryptoIntegration;
/**
* The Class CertificateUtils provides utility functions for generation of V1 and V3 {@link java.security.cert.X509Certificate}
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @author <a href="mailto:giriraj.sharma27@gmail.com">Giriraj Sharma</a>
* @version $Revision: 2 $
*/
public class CertificateUtils {
static {
CryptoIntegration.init(ClassLoader.getSystemClassLoader());
}
/**
* Generates version 3 {@link java.security.cert.X509Certificate}.
*
@ -61,57 +47,10 @@ public class CertificateUtils {
*
* @throws Exception the exception
*/
public static X509Certificate generateV3Certificate(KeyPair keyPair, PrivateKey caPrivateKey, X509Certificate caCert,
String subject) throws Exception {
try {
X500Name subjectDN = new X500Name("CN=" + subject);
// Serial Number
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
BigInteger serialNumber = BigInteger.valueOf(Math.abs(random.nextInt()));
// Validity
Date notBefore = new Date(System.currentTimeMillis());
Date notAfter = new Date(System.currentTimeMillis() + (((1000L * 60 * 60 * 24 * 30)) * 12) * 3);
// SubjectPublicKeyInfo
SubjectPublicKeyInfo subjPubKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
X509v3CertificateBuilder certGen = new X509v3CertificateBuilder(new X500Name(caCert.getSubjectDN().getName()),
serialNumber, notBefore, notAfter, subjectDN, subjPubKeyInfo);
JcaX509ExtensionUtils x509ExtensionUtils = new JcaX509ExtensionUtils();
// Subject Key Identifier
certGen.addExtension(Extension.subjectKeyIdentifier, false,
x509ExtensionUtils.createSubjectKeyIdentifier(subjPubKeyInfo));
// Authority Key Identifier
certGen.addExtension(Extension.authorityKeyIdentifier, false,
x509ExtensionUtils.createAuthorityKeyIdentifier(subjPubKeyInfo));
// Key Usage
certGen.addExtension(Extension.keyUsage, false, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign
| KeyUsage.cRLSign));
// Extended Key Usage
KeyPurposeId[] EKU = new KeyPurposeId[2];
EKU[0] = KeyPurposeId.id_kp_emailProtection;
EKU[1] = KeyPurposeId.id_kp_serverAuth;
certGen.addExtension(Extension.extendedKeyUsage, false, new ExtendedKeyUsage(EKU));
// Basic Constraints
certGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(0));
// Content Signer
ContentSigner sigGen = new JcaContentSignerBuilder("SHA1WithRSAEncryption").setProvider(BouncyIntegration.PROVIDER).build(caPrivateKey);
// Certificate
return new JcaX509CertificateConverter().setProvider(BouncyIntegration.PROVIDER).getCertificate(certGen.build(sigGen));
} catch (Exception e) {
throw new RuntimeException("Error creating X509v3Certificate.", e);
}
public static X509Certificate generateV3Certificate(KeyPair keyPair, PrivateKey caPrivateKey,
X509Certificate caCert, String subject) throws Exception {
return CryptoIntegration.getProvider().getCertificateUtils().generateV3Certificate(keyPair, caPrivateKey,
caCert, subject);
}
/**
@ -125,42 +64,11 @@ public class CertificateUtils {
* @throws Exception the exception
*/
public static X509Certificate generateV1SelfSignedCertificate(KeyPair caKeyPair, String subject) {
return generateV1SelfSignedCertificate(caKeyPair, subject, BigInteger.valueOf(System.currentTimeMillis()));
}
return CryptoIntegration.getProvider().getCertificateUtils().generateV1SelfSignedCertificate(caKeyPair, subject);
}
public static X509Certificate generateV1SelfSignedCertificate(KeyPair caKeyPair, String subject, BigInteger serialNumber) {
try {
X500Name subjectDN = new X500Name("CN=" + subject);
Date validityStartDate = new Date(System.currentTimeMillis() - 100000);
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, 10);
Date validityEndDate = new Date(calendar.getTime().getTime());
SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo.getInstance(caKeyPair.getPublic().getEncoded());
X509v1CertificateBuilder builder = new X509v1CertificateBuilder(subjectDN, serialNumber, validityStartDate,
validityEndDate, subjectDN, subPubKeyInfo);
X509CertificateHolder holder = builder.build(createSigner(caKeyPair.getPrivate()));
return new JcaX509CertificateConverter().getCertificate(holder);
} catch (Exception e) {
throw new RuntimeException("Error creating X509v1Certificate.", e);
}
}
/**
* Creates the content signer for generation of Version 1 {@link java.security.cert.X509Certificate}.
*
* @param privateKey the private key
*
* @return the content signer
*/
public static ContentSigner createSigner(PrivateKey privateKey) {
try {
JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
.setProvider(BouncyIntegration.PROVIDER);
return signerBuilder.build(privateKey);
} catch (Exception e) {
throw new RuntimeException("Could not create content signer.", e);
}
return CryptoIntegration.getProvider().getCertificateUtils().generateV1SelfSignedCertificate(caKeyPair, subject, serialNumber);
}
}

View file

@ -17,31 +17,28 @@
package org.keycloak.common.util;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import org.keycloak.common.crypto.CryptoIntegration;
/**
* Utility classes to extract PublicKey, PrivateKey, and X509Certificate from openssl generated PEM files
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public final class PemUtils {
public class PemUtils {
public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
public static final String END_CERT = "-----END CERTIFICATE-----";
private PemUtils() {
static {
CryptoIntegration.init(ClassLoader.getSystemClassLoader());
}
/**
@ -52,19 +49,10 @@ public final class PemUtils {
* @throws Exception
*/
public static X509Certificate decodeCertificate(String cert) {
if (cert == null) {
return null;
}
try {
byte[] der = pemToDer(cert);
ByteArrayInputStream bis = new ByteArrayInputStream(der);
return DerUtils.decodeCertificate(bis);
} catch (Exception e) {
throw new PemException(e);
}
return CryptoIntegration.getProvider().getPemUtils().decodeCertificate(cert);
}
/**
* Decode a Public Key from a PEM string
*
@ -73,7 +61,7 @@ public final class PemUtils {
* @throws Exception
*/
public static PublicKey decodePublicKey(String pem) {
return decodePublicKey(pem, "RSA");
return CryptoIntegration.getProvider().getPemUtils().decodePublicKey(pem);
}
/**
@ -82,19 +70,11 @@ public final class PemUtils {
* @param type The type of the key (RSA, EC,...)
* @return The public key or null
*/
public static PublicKey decodePublicKey(String pem, String type) {
if (pem == null) {
return null;
}
try {
byte[] der = pemToDer(pem);
return DerUtils.decodePublicKey(der, type);
} catch (Exception e) {
throw new PemException(e);
}
public static PublicKey decodePublicKey(String pem, String type){
return CryptoIntegration.getProvider().getPemUtils().decodePublicKey(pem, type);
}
/**
* Decode a Private Key from a PEM string
*
@ -102,19 +82,11 @@ public final class PemUtils {
* @return
* @throws Exception
*/
public static 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);
}
public static PrivateKey decodePrivateKey(String pem){
return CryptoIntegration.getProvider().getPemUtils().decodePrivateKey(pem);
}
/**
* Encode a Key to a PEM string
*
@ -122,8 +94,8 @@ public final class PemUtils {
* @return
* @throws Exception
*/
public static String encodeKey(Key key) {
return encode(key);
public static String encodeKey(Key key){
return CryptoIntegration.getProvider().getPemUtils().encodeKey(key);
}
/**
@ -132,51 +104,20 @@ public final class PemUtils {
* @param certificate
* @return
*/
public static String encodeCertificate(Certificate certificate) {
return encode(certificate);
public static String encodeCertificate(Certificate certificate){
return CryptoIntegration.getProvider().getPemUtils().encodeCertificate(certificate);
}
private static String encode(Object obj) {
if (obj == null) {
return null;
}
try {
StringWriter writer = new StringWriter();
JcaPEMWriter pemWriter = new JcaPEMWriter(writer);
pemWriter.writeObject(obj);
pemWriter.flush();
pemWriter.close();
String s = writer.toString();
return PemUtils.removeBeginEnd(s);
} catch (Exception e) {
throw new PemException(e);
}
public static byte[] pemToDer(String pem){
return CryptoIntegration.getProvider().getPemUtils().pemToDer(pem);
}
public static byte[] pemToDer(String pem) {
try {
pem = removeBeginEnd(pem);
return Base64.decode(pem);
} catch (IOException ioe) {
throw new PemException(ioe);
}
public static String removeBeginEnd(String pem){
return CryptoIntegration.getProvider().getPemUtils().removeBeginEnd(pem);
}
public static String removeBeginEnd(String pem) {
pem = pem.replaceAll("-----BEGIN (.*)-----", "");
pem = pem.replaceAll("-----END (.*)----", "");
pem = pem.replaceAll("\r\n", "");
pem = pem.replaceAll("\n", "");
return pem.trim();
}
public static String generateThumbprint(String[] certChain, String encoding) throws NoSuchAlgorithmException {
return Base64Url.encode(generateThumbprintBytes(certChain, encoding));
}
static byte[] generateThumbprintBytes(String[] certChain, String encoding) throws NoSuchAlgorithmException {
return MessageDigest.getInstance(encoding).digest(pemToDer(certChain[0]));
public static String generateThumbprint(String[] certChain, String encoding) throws NoSuchAlgorithmException{
return CryptoIntegration.getProvider().getPemUtils().generateThumbprint(certChain, encoding);
}
}

View file

@ -0,0 +1,169 @@
/*
* 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.def;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v1CertificateBuilder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.keycloak.common.util.BouncyIntegration;
import org.keycloak.common.crypto.CertificateUtilsProvider;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;
/**
* The Class CertificateUtils provides utility functions for generation of V1 and V3 {@link java.security.cert.X509Certificate}
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @author <a href="mailto:giriraj.sharma27@gmail.com">Giriraj Sharma</a>
* @version $Revision: 2 $
*/
public class BCCertificateUtilsProvider 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
*/
public X509Certificate generateV3Certificate(KeyPair keyPair, PrivateKey caPrivateKey, X509Certificate caCert,
String subject) throws Exception {
try {
X500Name subjectDN = new X500Name("CN=" + subject);
// Serial Number
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
BigInteger serialNumber = BigInteger.valueOf(Math.abs(random.nextInt()));
// Validity
Date notBefore = new Date(System.currentTimeMillis());
Date notAfter = new Date(System.currentTimeMillis() + (((1000L * 60 * 60 * 24 * 30)) * 12) * 3);
// SubjectPublicKeyInfo
SubjectPublicKeyInfo subjPubKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
X509v3CertificateBuilder certGen = new X509v3CertificateBuilder(new X500Name(caCert.getSubjectDN().getName()),
serialNumber, notBefore, notAfter, subjectDN, subjPubKeyInfo);
JcaX509ExtensionUtils x509ExtensionUtils = new JcaX509ExtensionUtils();
// Subject Key Identifier
certGen.addExtension(Extension.subjectKeyIdentifier, false,
x509ExtensionUtils.createSubjectKeyIdentifier(subjPubKeyInfo));
// Authority Key Identifier
certGen.addExtension(Extension.authorityKeyIdentifier, false,
x509ExtensionUtils.createAuthorityKeyIdentifier(subjPubKeyInfo));
// Key Usage
certGen.addExtension(Extension.keyUsage, false, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign
| KeyUsage.cRLSign));
// Extended Key Usage
KeyPurposeId[] EKU = new KeyPurposeId[2];
EKU[0] = KeyPurposeId.id_kp_emailProtection;
EKU[1] = KeyPurposeId.id_kp_serverAuth;
certGen.addExtension(Extension.extendedKeyUsage, false, new ExtendedKeyUsage(EKU));
// Basic Constraints
certGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(0));
// Content Signer
ContentSigner sigGen = new JcaContentSignerBuilder("SHA1WithRSAEncryption").setProvider(BouncyIntegration.PROVIDER).build(caPrivateKey);
// Certificate
return new JcaX509CertificateConverter().setProvider(BouncyIntegration.PROVIDER).getCertificate(certGen.build(sigGen));
} 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
*/
public X509Certificate generateV1SelfSignedCertificate(KeyPair caKeyPair, String subject) {
return generateV1SelfSignedCertificate(caKeyPair, subject, BigInteger.valueOf(System.currentTimeMillis()));
}
public X509Certificate generateV1SelfSignedCertificate(KeyPair caKeyPair, String subject, BigInteger serialNumber) {
try {
X500Name subjectDN = new X500Name("CN=" + subject);
Date validityStartDate = new Date(System.currentTimeMillis() - 100000);
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, 10);
Date validityEndDate = new Date(calendar.getTime().getTime());
SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo.getInstance(caKeyPair.getPublic().getEncoded());
X509v1CertificateBuilder builder = new X509v1CertificateBuilder(subjectDN, serialNumber, validityStartDate,
validityEndDate, subjectDN, subPubKeyInfo);
X509CertificateHolder holder = builder.build(createSigner(caKeyPair.getPrivate()));
return new JcaX509CertificateConverter().getCertificate(holder);
} catch (Exception e) {
throw new RuntimeException("Error creating X509v1Certificate.", e);
}
}
/**
* Creates the content signer for generation of Version 1 {@link java.security.cert.X509Certificate}.
*
* @param privateKey the private key
*
* @return the content signer
*/
private ContentSigner createSigner(PrivateKey privateKey) {
try {
JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
.setProvider(BouncyIntegration.PROVIDER);
return signerBuilder.build(privateKey);
} catch (Exception e) {
throw new RuntimeException("Could not create content signer.", e);
}
}
}

View file

@ -0,0 +1,60 @@
/*
* 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.def;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.keycloak.common.util.PemException;
import org.keycloak.common.crypto.PemUtilsProvider;
import java.io.StringWriter;
/**
* Encodes Key or Certificates to PEM format string
*
* @author <a href="mailto:david.anderson@redhat.com">David Anderson</a>
* @version $Revision: 1 $
*/
public class BCPemUtilsProvider extends PemUtilsProvider {
/**
* Encode object to JCA PEM String using BC libraries
*
* @param obj
* @return The encoded PEM string
*/
@Override
protected String encode(Object obj) {
if (obj == null) {
return null;
}
try {
StringWriter writer = new StringWriter();
JcaPEMWriter pemWriter = new JcaPEMWriter(writer);
pemWriter.writeObject(obj);
pemWriter.flush();
pemWriter.close();
String s = writer.toString();
return removeBeginEnd(s);
} catch (Exception e) {
throw new PemException(e);
}
}
}

View file

@ -9,6 +9,8 @@ import java.util.function.Supplier;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.keycloak.common.crypto.CryptoProvider;
import org.keycloak.common.crypto.CryptoProviderTypes;
import org.keycloak.common.crypto.CertificateUtilsProvider;
import org.keycloak.common.crypto.PemUtilsProvider;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -35,4 +37,15 @@ public class DefaultCryptoProvider implements CryptoProvider {
}
return clazz.cast(o);
}
@Override
public CertificateUtilsProvider getCertificateUtils() {
return new BCCertificateUtilsProvider();
}
@Override
public PemUtilsProvider getPemUtils() {
return new BCPemUtilsProvider();
}
}

View file

@ -0,0 +1,26 @@
package org.keycloak.crypto.def.test;
import org.junit.Test;
import java.security.NoSuchAlgorithmException;
import static org.junit.Assert.assertEquals;
public class PemUtilsBCTest {
@Test
public void testGenerateThumbprintSha1() throws NoSuchAlgorithmException {
String[] test = new String[] {"abcdefg"};
String encoded = org.keycloak.common.util.PemUtils.generateThumbprint(test, "SHA-1");
assertEquals(27, encoded.length());
}
@Test
public void testGenerateThumbprintSha256() throws NoSuchAlgorithmException {
String[] test = new String[] {"abcdefg"};
String encoded = org.keycloak.common.util.PemUtils.generateThumbprint(test, "SHA-256");
assertEquals(43, encoded.length());
}
}

View file

@ -0,0 +1,168 @@
/*
* 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.fips;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v1CertificateBuilder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.keycloak.common.util.BouncyIntegration;
import org.keycloak.common.crypto.CertificateUtilsProvider;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;
/**
* The Class CertificateUtils provides utility functions for generation of V1 and V3 {@link java.security.cert.X509Certificate}
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @author <a href="mailto:giriraj.sharma27@gmail.com">Giriraj Sharma</a>
* @version $Revision: 2 $
*/
public class BCFIPSCertificateUtilsProvider 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
*/
public X509Certificate generateV3Certificate(KeyPair keyPair, PrivateKey caPrivateKey, X509Certificate caCert,
String subject) throws Exception {
try {
X500Name subjectDN = new X500Name("CN=" + subject);
// Serial Number
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
BigInteger serialNumber = BigInteger.valueOf(Math.abs(random.nextInt()));
// Validity
Date notBefore = new Date(System.currentTimeMillis());
Date notAfter = new Date(System.currentTimeMillis() + (((1000L * 60 * 60 * 24 * 30)) * 12) * 3);
// SubjectPublicKeyInfo
SubjectPublicKeyInfo subjPubKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
X509v3CertificateBuilder certGen = new X509v3CertificateBuilder(new X500Name(caCert.getSubjectDN().getName()),
serialNumber, notBefore, notAfter, subjectDN, subjPubKeyInfo);
JcaX509ExtensionUtils x509ExtensionUtils = new JcaX509ExtensionUtils();
// Subject Key Identifier
certGen.addExtension(Extension.subjectKeyIdentifier, false,
x509ExtensionUtils.createSubjectKeyIdentifier(subjPubKeyInfo));
// Authority Key Identifier
certGen.addExtension(Extension.authorityKeyIdentifier, false,
x509ExtensionUtils.createAuthorityKeyIdentifier(subjPubKeyInfo));
// Key Usage
certGen.addExtension(Extension.keyUsage, false, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign
| KeyUsage.cRLSign));
// Extended Key Usage
KeyPurposeId[] EKU = new KeyPurposeId[2];
EKU[0] = KeyPurposeId.id_kp_emailProtection;
EKU[1] = KeyPurposeId.id_kp_serverAuth;
certGen.addExtension(Extension.extendedKeyUsage, false, new ExtendedKeyUsage(EKU));
// Basic Constraints
certGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(0));
// Content Signer
ContentSigner sigGen = new JcaContentSignerBuilder("SHA1WithRSAEncryption").setProvider(BouncyIntegration.PROVIDER).build(caPrivateKey);
// Certificate
return new JcaX509CertificateConverter().setProvider(BouncyIntegration.PROVIDER).getCertificate(certGen.build(sigGen));
} 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
*/
public X509Certificate generateV1SelfSignedCertificate(KeyPair caKeyPair, String subject) {
return generateV1SelfSignedCertificate(caKeyPair, subject, BigInteger.valueOf(System.currentTimeMillis()));
}
public X509Certificate generateV1SelfSignedCertificate(KeyPair caKeyPair, String subject, BigInteger serialNumber) {
try {
X500Name subjectDN = new X500Name("CN=" + subject);
Date validityStartDate = new Date(System.currentTimeMillis() - 100000);
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, 10);
Date validityEndDate = new Date(calendar.getTime().getTime());
SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo.getInstance(caKeyPair.getPublic().getEncoded());
X509v1CertificateBuilder builder = new X509v1CertificateBuilder(subjectDN, serialNumber, validityStartDate,
validityEndDate, subjectDN, subPubKeyInfo);
X509CertificateHolder holder = builder.build(createSigner(caKeyPair.getPrivate()));
return new JcaX509CertificateConverter().getCertificate(holder);
} catch (Exception e) {
throw new RuntimeException("Error creating X509v1Certificate.", e);
}
}
/**
* Creates the content signer for generation of Version 1 {@link java.security.cert.X509Certificate}.
*
* @param privateKey the private key
*
* @return the content signer
*/
private ContentSigner createSigner(PrivateKey privateKey) {
try {
JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
.setProvider(BouncyIntegration.PROVIDER);
return signerBuilder.build(privateKey);
} catch (Exception e) {
throw new RuntimeException("Could not create content signer.", e);
}
}
}

View file

@ -0,0 +1,60 @@
/*
* 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.fips;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.keycloak.common.util.PemException;
import org.keycloak.common.crypto.PemUtilsProvider;
import java.io.StringWriter;
/**
* Encodes Key or Certificates to PEM format string
*
* @author <a href="mailto:david.anderson@redhat.com">David Anderson</a>
* @version $Revision: 1 $
*/
public class BCFIPSPemUtilsProvider extends PemUtilsProvider {
/**
* Encode object to JCA PEM String using BC FIPS libraries
*
* @param obj
* @return The encoded PEM string
*/
@Override
protected String encode(Object obj) {
if (obj == null) {
return null;
}
try {
StringWriter writer = new StringWriter();
JcaPEMWriter pemWriter = new JcaPEMWriter(writer);
pemWriter.writeObject(obj);
pemWriter.flush();
pemWriter.close();
String s = writer.toString();
return removeBeginEnd(s);
} catch (Exception e) {
throw new PemException(e);
}
}
}

View file

@ -10,6 +10,8 @@ import java.util.function.Supplier;
import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider;
import org.keycloak.common.crypto.CryptoProvider;
import org.keycloak.common.crypto.CryptoProviderTypes;
import org.keycloak.common.crypto.CertificateUtilsProvider;
import org.keycloak.common.crypto.PemUtilsProvider;
/**
@ -39,4 +41,14 @@ public class FIPS1402Provider implements CryptoProvider {
}
return clazz.cast(o);
}
@Override
public CertificateUtilsProvider getCertificateUtils() {
return new BCFIPSCertificateUtilsProvider();
}
@Override
public PemUtilsProvider getPemUtils() {
return new BCFIPSPemUtilsProvider();
}
}

View file

@ -1,26 +1,13 @@
package org.keycloak.common.util;
package org.keycloak.crypto.fips.test;
import org.junit.Test;
import org.keycloak.common.util.PemUtils;
import java.security.NoSuchAlgorithmException;
import static org.junit.Assert.assertEquals;
public class PemUtilsTest {
@Test
public void testGenerateThumbprintBytesSha1() throws NoSuchAlgorithmException {
String[] test = new String[] {"abcdefg"};
byte[] digest = PemUtils.generateThumbprintBytes(test, "SHA-1");
assertEquals(20, digest.length);
}
@Test
public void testGenerateThumbprintBytesSha256() throws NoSuchAlgorithmException {
String[] test = new String[] {"abcdefg"};
byte[] digest = PemUtils.generateThumbprintBytes(test, "SHA-256");
assertEquals(32, digest.length);
}
public class PemUtilsBCFIPSTest {
@Test
public void testGenerateThumbprintSha1() throws NoSuchAlgorithmException {

View file

@ -24,7 +24,6 @@
<artifact name="${org.keycloak:keycloak-common}"/>
</resources>
<dependencies>
<module name="org.bouncycastle" />
<module name="javax.api"/>
<module name="javax.activation.api"/>
<module name="org.jboss.logging"/>

View file

@ -21,7 +21,6 @@
</resources>
<dependencies>
<module name="org.bouncycastle" />
<module name="javax.api"/>
<module name="javax.activation.api"/>
<module name="org.jboss.logging"/>

View file

@ -21,7 +21,6 @@
</resources>
<dependencies>
<module name="org.bouncycastle" />
<module name="javax.api"/>
<module name="javax.activation.api"/>
<module name="org.jboss.logging"/>

View file

@ -114,6 +114,16 @@
<artifactId>keycloak-common</artifactId>
</dependency>
<!-- FIXME: Adding BC since removed from common, this will need to be removed -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>io.quarkus</groupId>

View file

@ -30,6 +30,7 @@ import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusCondition;
import org.keycloak.operator.crds.v2alpha1.deployment.ValueOrSecret;
import java.util.HashSet;
import java.util.List;
import java.util.Set;