parent
89028613d8
commit
8ce10df6da
19 changed files with 810 additions and 217 deletions
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
170
common/src/main/java/org/keycloak/common/crypto/PemUtilsProvider.java
Executable file
170
common/src/main/java/org/keycloak/common/crypto/PemUtilsProvider.java
Executable 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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
return CryptoIntegration.getProvider().getCertificateUtils().generateV1SelfSignedCertificate(caKeyPair, subject, serialNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,18 +49,9 @@ public final class PemUtils {
|
|||
* @throws Exception
|
||||
*/
|
||||
public static X509Certificate decodeCertificate(String cert) {
|
||||
if (cert == null) {
|
||||
return null;
|
||||
return CryptoIntegration.getProvider().getPemUtils().decodeCertificate(cert);
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,17 +71,9 @@ public final class PemUtils {
|
|||
* @return The public key or null
|
||||
*/
|
||||
public static PublicKey decodePublicKey(String pem, String type){
|
||||
if (pem == null) {
|
||||
return null;
|
||||
return CryptoIntegration.getProvider().getPemUtils().decodePublicKey(pem, type);
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -103,17 +83,9 @@ public final class PemUtils {
|
|||
* @throws Exception
|
||||
*/
|
||||
public static PrivateKey decodePrivateKey(String pem){
|
||||
if (pem == null) {
|
||||
return null;
|
||||
return CryptoIntegration.getProvider().getPemUtils().decodePrivateKey(pem);
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] der = pemToDer(pem);
|
||||
return DerUtils.decodePrivateKey(der);
|
||||
} catch (Exception e) {
|
||||
throw new PemException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a Key to a PEM string
|
||||
|
@ -123,7 +95,7 @@ public final class PemUtils {
|
|||
* @throws Exception
|
||||
*/
|
||||
public static String encodeKey(Key key){
|
||||
return encode(key);
|
||||
return CryptoIntegration.getProvider().getPemUtils().encodeKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,50 +105,19 @@ public final class PemUtils {
|
|||
* @return
|
||||
*/
|
||||
public static String encodeCertificate(Certificate certificate){
|
||||
return encode(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);
|
||||
}
|
||||
return CryptoIntegration.getProvider().getPemUtils().encodeCertificate(certificate);
|
||||
}
|
||||
|
||||
public static byte[] pemToDer(String pem){
|
||||
try {
|
||||
pem = removeBeginEnd(pem);
|
||||
return Base64.decode(pem);
|
||||
} catch (IOException ioe) {
|
||||
throw new PemException(ioe);
|
||||
}
|
||||
return CryptoIntegration.getProvider().getPemUtils().pemToDer(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();
|
||||
return CryptoIntegration.getProvider().getPemUtils().removeBeginEnd(pem);
|
||||
}
|
||||
|
||||
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]));
|
||||
return CryptoIntegration.getProvider().getPemUtils().generateThumbprint(certChain, encoding);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
60
crypto/default/src/main/java/org/keycloak/crypto/def/BCPemUtilsProvider.java
Executable file
60
crypto/default/src/main/java/org/keycloak/crypto/def/BCPemUtilsProvider.java
Executable 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
|
@ -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"/>
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
</resources>
|
||||
|
||||
<dependencies>
|
||||
<module name="org.bouncycastle" />
|
||||
<module name="javax.api"/>
|
||||
<module name="javax.activation.api"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
</resources>
|
||||
|
||||
<dependencies>
|
||||
<module name="org.bouncycastle" />
|
||||
<module name="javax.api"/>
|
||||
<module name="javax.activation.api"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue