Not possible to login in FIPS enabled RHEL 8.6. Support for parsing PEM private keys in BCFIPS module in both traditional and PKCS8 format (#14008)

Closes #13994
This commit is contained in:
Marek Posolda 2022-08-30 22:33:12 +02:00 committed by GitHub
parent 6ad71557de
commit 19daf2b375
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 291 additions and 107 deletions

View file

@ -40,9 +40,6 @@ import org.keycloak.common.util.PemException;
*/
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
@ -104,18 +101,7 @@ public abstract class PemUtilsProvider {
* @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);
}
}
public abstract PrivateKey decodePrivateKey(String pem);
/**

View file

@ -37,6 +37,11 @@ public class PemUtils {
public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
public static final String END_CERT = "-----END CERTIFICATE-----";
public static final String BEGIN_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----";
public static final String END_PRIVATE_KEY = "-----END PRIVATE KEY-----";
public static final String BEGIN_RSA_PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----";
public static final String END_RSA_PRIVATE_KEY = "-----END RSA PRIVATE KEY-----";
/**
* Decode a X509 Certificate from a PEM string
*
@ -112,6 +117,20 @@ public class PemUtils {
return CryptoIntegration.getProvider().getPemUtils().removeBeginEnd(pem);
}
public static String addPrivateKeyBeginEnd(String privateKeyPem) {
return new StringBuilder(PemUtils.BEGIN_PRIVATE_KEY + "\n")
.append(privateKeyPem)
.append("\n" + PemUtils.END_PRIVATE_KEY)
.toString();
}
public static String addRsaPrivateKeyBeginEnd(String privateKeyPem) {
return new StringBuilder(PemUtils.BEGIN_RSA_PRIVATE_KEY + "\n")
.append(privateKeyPem)
.append("\n" + PemUtils.END_RSA_PRIVATE_KEY)
.toString();
}
public static String generateThumbprint(String[] certChain, String encoding) throws NoSuchAlgorithmException{
return CryptoIntegration.getProvider().getPemUtils().generateThumbprint(certChain, encoding);
}

View file

@ -50,13 +50,65 @@ public abstract class KeyPairVerifierTest {
+ "LMD9RZHcsIdfSnG7xVNBQZpf4ZCSFO3RbIH7b//+kn8TxQudptd9SkXba65prBM2\n" + "kh8IbDNBAoGAVsKvkruH7RK7CimDSWcdAKvHARqkjs/PoeKEEY8Yu6zf0Z9TQM5l\n"
+ "uC9EwBamYcSusWRcdcz+9HYG58XFnmXq+3EUuFbJ+Ljb8YWBgePjSHDoS/6+/+zq\n" + "B1b5uQp/jYFbYQl50UPRPTF+ul1eQoy7F43Ngj3/5cDRarFZe3ZTzZo=\n"
+ "-----END RSA PRIVATE KEY-----";
String publicKey2048 = "-----BEGIN PUBLIC KEY-----\n" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4V3MpOnuKsdBbR1UzNjK\n"
+ "9o5meEMQ4s5Vpykhv1DpqTilKOiEH7VQ/XtjNxw0yjnFBilCnpK6yN9mDEHbBEza\n" + "RjtdrgVhkIejiaXFBP5MBhUQ5l9u8E3IZC3E8pwDjVF0Z9u0R4lGeUg2k6O+NKum\n"
+ "qIvxoLCTuG0zf53bctGsRd57LuFipgCkNyxvscOhulsbEMYrLwlb5bMGgx9v+RCn\n" + "wvunNEb7RK+5pzP+iH1MRejRsX+U7h9zHRn2gQhIl7SzG9GXebuPWr4KKwfMHWy0\n"
+ "PEuQrsfWRXm9/dTEavbfNkv5E53zWXjWyf93ezkVhBX0YoXmf6UO7PAlvsrjno3T\n" + "uwIDAQAB\n" + "-----END PUBLIC KEY-----";
@Test
public void verify() throws Exception {
public void verifyWithPrivateKeysInTraditionalRSAFormat() throws Exception {
verifyImpl(this.privateKey1, this.privateKey2048);
}
@Test
public void verifyWithPrivateKeysInPKCS8Format() throws Exception {
String 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==";
String 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==";
verifyImpl(privateKey1, privateKey2048);
}
protected void verifyImpl(String privateKey1, String privateKey2048) throws Exception {
KeyPairVerifier.verify(privateKey1, publicKey1);
KeyPairVerifier.verify(privateKey2048, publicKey2048);

View file

@ -0,0 +1,136 @@
package org.keycloak.util;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.keycloak.common.util.CertificateUtils;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.PemUtils;
import org.keycloak.rule.CryptoInitRule;
import static org.junit.Assert.assertEquals;
public abstract class PemUtilsTest {
@ClassRule
public static CryptoInitRule cryptoInitRule = 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());
}
@Test
public void testEncodeAndDecodeGeneratedObjects() {
KeyPair keyPair = KeyUtils.generateRsaKeyPair(2048);
Certificate certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, "FooBar");
// Test encoding/decoding private key
String encodedPrivateKey = PemUtils.encodeKey(keyPair.getPrivate());
PrivateKey decodedPrivateKey = PemUtils.decodePrivateKey(encodedPrivateKey);
assertEquals(decodedPrivateKey, keyPair.getPrivate());
// Test encoding/decoding public key
String encodedPublicKey = PemUtils.encodeKey(keyPair.getPublic());
PublicKey decodedPublicKey = PemUtils.decodePublicKey(encodedPublicKey);
assertEquals(decodedPublicKey, keyPair.getPublic());
// Test encoding/decoding certificate
String encodedCertificate = PemUtils.encodeCertificate(certificate);
Certificate decodedCertificate = PemUtils.decodeCertificate(encodedCertificate);
assertEquals(decodedCertificate, certificate);
}
@Test
public void testDecodeObjectsInPEMFormat() {
String privateKey1 = "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=";
String publicKey1 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB";
String cert1 = "MIICnTCCAYUCBgFPPLDaTzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdjbGllbnQxMB4XDTE1MDgxNzE3MjI0N1oXDTI1MDgxNzE3MjQyN1owEjEQMA4GA1UEAwwHY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIUjjgv+V3s96O+Za9002Lp/trtGuHBeaeVL9dFKMKzO2MPqdRmHB4PqNlDdd28Rwf5Xn6iWdFpyUKOnI/yXDLhdcuFpR0sMNK/C9Lt+hSpPFLuzDqgtPgDotlMxiHIWDOZ7g9/gPYNXbNvjv8nSiyqoguoCQiiafW90bPHsiVLdP7ZIUwCcfi1qQm7FhxRJ1NiW5dvUkuCnnWEf0XR+Wzc5eC9EgB0taLFiPsSEIlWMm5xlahYyXkPdNOqZjiRnrTWm5Y4uk8ZcsD/KbPTf/7t7cQXipVaswgjdYi1kK2/zRwOhg1QwWFX/qmvdd+fLxV0R6VqRDhn7Qep2cxwMxLsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAKE6OA46sf20bz8LZPoiNsqRwBUDkaMGXfnob7s/hJZIIwDEx0IAQ3uKsG7q9wb+aA6s+v7S340zb2k3IxuhFaHaZpAd4CyR5cn1FHylbzoZ7rI/3ASqHDqpljdJaFqPH+m7nZWtyDvtZf+gkZ8OjsndwsSBK1d/jMZPp29qYbl1+XfO7RCp/jDqro/R3saYFaIFiEZPeKn1hUJn6BO48vxH1xspSu9FmlvDOEAOz4AuM58z4zRMP49GcFdCWr1wkonJUHaSptJaQwmBwLFUkCbE5I1ixGMb7mjEud6Y5jhfzJiZMo2U8RfcjNbrN0diZl3jB6LQIwESnhYSghaTjNQ==";
String cert2 = "MIICnTCCAYUCBgFPPQDGxTANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdjbGllbnQxMB4XDTE1MDgxNzE4NTAwNVoXDTI1MDgxNzE4NTE0NVowEjEQMA4GA1UEAwwHY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMMw3PaBffWxgS2PYSDDBp6As+cNvv9kt2C4f/RDAGmvSIHPFev9kuQiKs3Oaws3ZsV4JG3qHEuYgnh9W4vfe3DwNwtD1bjL5FYBhPBFTw0lAQECYxaBHnkjHwUKp957FqdSPPICm3LjmTcEdlH+9dpp9xHCMbbiNiWDzWI1xSxC8Fs2d0hwz1sd+Q4QeTBPIBWcPM+ICZtNG5MN+ORfayu4X+Me5d0tXG2fQO//rAevk1i5IFjKZuOjTwyKB5SJIY4b8QTeg0g/50IU7Ht00Pxw6CK02dHS+FvXHasZlD3ckomqCDjStTBWdhJo5dST0CbOqalkkpLlCCbGA1yEQRsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAUIMeJ+EAo8eNpCG/nXImacjrKakbFnZYBGD/gqeTGaZynkX+jgBSructTHR83zSH+yELEhsAy+3BfK4EEihp+PEcRnK2fASVkHste8AQ7rlzC+HGGirlwrVhWCdizNUCGK80DE537IZ7nmZw6LFG9P5/Q2MvCsOCYjRUvMkukq6TdXBXR9tETwZ+0gpSfsOxjj0ZF7ftTRUSzx4rFfcbM9fRNdVizdOuKGc8HJPA5lLOxV6CyaYIvi3y5RlQI1OHeS34lE4w9CNPRFa/vdxXvN7ClyzA0HMFNWxBN7pC/Ht/FbhSvaAagJBHg+vCrcY5C26Oli7lAglf/zZrwUPs0w==";
// RSA key in the "traditional" PKCS1 format
String privateKey2 = "-----BEGIN RSA PRIVATE KEY-----\n" +
"MIICXAIBAAKBgQCjcGqTkOq0CR3rTx0ZSQSIdTrDrFAYl29611xN8aVgMQIWtDB/\n" +
"lD0W5TpKPuU9iaiG/sSn/VYt6EzN7Sr332jj7cyl2WrrHI6ujRswNy4HojMuqtfa\n" +
"b5FFDpRmCuvl35fge18OvoQTJELhhJ1EvJ5KUeZiuJ3u3YyMnxxXzLuKbQIDAQAB\n" +
"AoGAPrNDz7TKtaLBvaIuMaMXgBopHyQd3jFKbT/tg2Fu5kYm3PrnmCoQfZYXFKCo\n" +
"ZUFIS/G1FBVWWGpD/MQ9tbYZkKpwuH+t2rGndMnLXiTC296/s9uix7gsjnT4Naci\n" +
"5N6EN9pVUBwQmGrYUTHFc58ThtelSiPARX7LSU2ibtJSv8ECQQDWBRrrAYmbCUN7\n" +
"ra0DFT6SppaDtvvuKtb+mUeKbg0B8U4y4wCIK5GH8EyQSwUWcXnNBO05rlUPbifs\n" +
"DLv/u82lAkEAw39sTJ0KmJJyaChqvqAJ8guulKlgucQJ0Et9ppZyet9iVwNKX/aW\n" +
"9UlwGBMQdafQ36nd1QMEA8AbAw4D+hw/KQJBANJbHDUGQtk2hrSmZNoV5HXB9Uiq\n" +
"7v4N71k5ER8XwgM5yVGs2tX8dMM3RhnBEtQXXs9LW1uJZSOQcv7JGXNnhN0CQBZe\n" +
"nzrJAWxh3XtznHtBfsHWelyCYRIAj4rpCHCmaGUM6IjCVKFUawOYKp5mmAyObkUZ\n" +
"f8ue87emJLEdynC1CLkCQHduNjP1hemAGWrd6v8BHhE3kKtcK6KHsPvJR5dOfzbd\n" +
"HAqVePERhISfN6cwZt5p8B3/JUwSR8el66DF7Jm57BM=\n" +
"-----END RSA PRIVATE KEY-----";
testPrivateKeyEncodeDecode(privateKey1);
testPublicKeyEncodeDecode(publicKey1);
testPrivateKeyEncodeDecode(PemUtils.removeBeginEnd(privateKey2).replace("\n", ""));
testCertificateEncodeDecode(cert1);
testCertificateEncodeDecode(cert2);
}
@Test
public void testPrivateKeyInPKCS8Format() {
String privateKeyPkcs8 = "-----BEGIN PRIVATE KEY-----\n" +
"MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKNwapOQ6rQJHetP\n" +
"HRlJBIh1OsOsUBiXb3rXXE3xpWAxAha0MH+UPRblOko+5T2JqIb+xKf9Vi3oTM3t\n" +
"KvffaOPtzKXZauscjq6NGzA3LgeiMy6q19pvkUUOlGYK6+Xfl+B7Xw6+hBMkQuGE\n" +
"nUS8nkpR5mK4ne7djIyfHFfMu4ptAgMBAAECgYA+s0PPtMq1osG9oi4xoxeAGikf\n" +
"JB3eMUptP+2DYW7mRibc+ueYKhB9lhcUoKhlQUhL8bUUFVZYakP8xD21thmQqnC4\n" +
"f63asad0ycteJMLb3r+z26LHuCyOdPg1pyLk3oQ32lVQHBCYathRMcVznxOG16VK\n" +
"I8BFfstJTaJu0lK/wQJBANYFGusBiZsJQ3utrQMVPpKmloO2++4q1v6ZR4puDQHx\n" +
"TjLjAIgrkYfwTJBLBRZxec0E7TmuVQ9uJ+wMu/+7zaUCQQDDf2xMnQqYknJoKGq+\n" +
"oAnyC66UqWC5xAnQS32mlnJ632JXA0pf9pb1SXAYExB1p9Dfqd3VAwQDwBsDDgP6\n" +
"HD8pAkEA0lscNQZC2TaGtKZk2hXkdcH1SKru/g3vWTkRHxfCAznJUaza1fx0wzdG\n" +
"GcES1Bdez0tbW4llI5By/skZc2eE3QJAFl6fOskBbGHde3Oce0F+wdZ6XIJhEgCP\n" +
"iukIcKZoZQzoiMJUoVRrA5gqnmaYDI5uRRl/y57zt6YksR3KcLUIuQJAd242M/WF\n" +
"6YAZat3q/wEeETeQq1wrooew+8lHl05/Nt0cCpV48RGEhJ83pzBm3mnwHf8lTBJH\n" +
"x6XroMXsmbnsEw==\n" +
"-----END PRIVATE KEY-----";
PrivateKey decodedPrivateKey1 = PemUtils.decodePrivateKey(privateKeyPkcs8);
// Assert it works also when the "begin/end" section is removed
String pk = PemUtils.removeBeginEnd(privateKeyPkcs8).replace("\n", "");
PrivateKey decodedPrivateKey2 = PemUtils.decodePrivateKey(pk);
Assert.assertEquals(decodedPrivateKey1, decodedPrivateKey2);
}
private void testPrivateKeyEncodeDecode(String origPrivateKeyPem) {
PrivateKey decodedPrivateKey = PemUtils.decodePrivateKey(origPrivateKeyPem);
String encodedPrivateKey = PemUtils.encodeKey(decodedPrivateKey);
assertEquals(origPrivateKeyPem, encodedPrivateKey);
}
private void testPublicKeyEncodeDecode(String origPublicKeyPem) {
PublicKey decodedPublicKey = PemUtils.decodePublicKey(origPublicKeyPem);
String encodedPublicKey = PemUtils.encodeKey(decodedPublicKey);
assertEquals(origPublicKeyPem, encodedPublicKey);
}
private void testCertificateEncodeDecode(String origCertPem) {
X509Certificate decodedCert = PemUtils.decodeCertificate(origCertPem);
String encodedCert = PemUtils.encodeCertificate(decodedCert);
assertEquals(origCertPem, encodedCert);
}
}

View file

@ -18,10 +18,12 @@
package org.keycloak.crypto.def;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.keycloak.common.util.DerUtils;
import org.keycloak.common.util.PemException;
import org.keycloak.common.crypto.PemUtilsProvider;
import java.io.StringWriter;
import java.security.PrivateKey;
/**
* Encodes Key or Certificates to PEM format string
@ -57,4 +59,18 @@ public class BCPemUtilsProvider extends PemUtilsProvider {
}
}
@Override
public PrivateKey decodePrivateKey(String pem) {
if (pem == null) {
return null;
}
try {
byte[] der = pemToDer(pem);
return DerUtils.decodePrivateKey(der);
} catch (Exception e) {
throw new PemException(e);
}
}
}

View file

@ -2,14 +2,10 @@ package org.keycloak.crypto.def.test;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.common.util.Environment;
import org.keycloak.util.PemUtilsTest;
import java.security.NoSuchAlgorithmException;
import static org.junit.Assert.assertEquals;
public class PemUtilsBCTest {
public class PemUtilsBCTest extends PemUtilsTest {
@Before
public void before() {
@ -17,18 +13,5 @@ public class PemUtilsBCTest {
Assume.assumeFalse("Java is in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode());
}
@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

@ -17,11 +17,23 @@
package org.keycloak.crypto.fips;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.keycloak.common.util.BouncyIntegration;
import org.keycloak.common.util.DerUtils;
import org.keycloak.common.util.PemException;
import org.keycloak.common.crypto.PemUtilsProvider;
import org.keycloak.common.util.PemUtils;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
/**
* Encodes Key or Certificates to PEM format string
@ -57,4 +69,52 @@ public class BCFIPSPemUtilsProvider extends PemUtilsProvider {
}
}
@Override
public PrivateKey decodePrivateKey(String pem) {
if (pem == null) {
return null;
}
try {
boolean beginEndAvailable = pem.startsWith("-----BEGIN");
Object parsedPk;
if (beginEndAvailable) { // No fallback needed as BC should know the format of the key (based on the phrase like BEGIN PRIVATE KEY, BEGIN RSA PRIVATE KEY, BEGIN EC PRIVATE KEY etc)
parsedPk = readPrivateKeyObject(pem);
} else {
try {
// Case for the PEM in traditional format
String rsaPem = PemUtils.addRsaPrivateKeyBeginEnd(pem);
parsedPk = readPrivateKeyObject(rsaPem);
} catch (IOException ioe) {
// Case for generic PKCS#8 represented keys
pem = PemUtils.addPrivateKeyBeginEnd(pem);
parsedPk = readPrivateKeyObject(pem);
}
}
PrivateKeyInfo privateKeyInfo;
if (parsedPk instanceof PEMKeyPair) {
// Usually for keys of known format (For example when PEM starts with "BEGIN RSA PRIVATE KEY")
PEMKeyPair pemKeyPair = (PEMKeyPair)parsedPk;
privateKeyInfo = pemKeyPair.getPrivateKeyInfo();
} else if (parsedPk instanceof PrivateKeyInfo) {
// Usually for PKCS#8 formatted keys of unknown type ("BEGIN PRIVATE KEY")
privateKeyInfo = (PrivateKeyInfo) parsedPk;
} else {
throw new IllegalStateException("Unknown type returned by PEMParser when parsing private key: " + parsedPk.getClass());
}
return new JcaPEMKeyConverter()
.setProvider(BouncyIntegration.PROVIDER)
.getPrivateKey(privateKeyInfo);
} catch (Exception e) {
throw new PemException(e);
}
}
private Object readPrivateKeyObject(String pemWithBeginEnd) throws IOException {
PEMParser parser = new PEMParser(new StringReader(pemWithBeginEnd));
return parser.readObject();
}
}

View file

@ -12,55 +12,6 @@ import org.keycloak.common.util.Environment;
*/
public class FIPS1402KeyPairVerifierTest extends KeyPairVerifierTest {
@Before
public void initPrivKeys() {
// The parent private key is not supported in FIPS-140-2, 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 supported in FIPS-140-2, 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==";
}
@Before
public void before() {
// Run this test just if java is in FIPS mode

View file

@ -2,34 +2,15 @@ package org.keycloak.crypto.fips.test;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.common.util.Environment;
import org.keycloak.common.util.PemUtils;
import org.keycloak.util.PemUtilsTest;
import java.security.NoSuchAlgorithmException;
import static org.junit.Assert.assertEquals;
public class PemUtilsBCFIPSTest {
public class PemUtilsBCFIPSTest extends PemUtilsTest {
@Before
public void before() {
// Run this test just if java is in FIPS mode
Assume.assumeTrue("Java is not in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode());
}
@Test
public void testGenerateThumbprintSha1() throws NoSuchAlgorithmException {
String[] test = new String[] {"abcdefg"};
String encoded = PemUtils.generateThumbprint(test, "SHA-1");
assertEquals(27, encoded.length());
}
@Test
public void testGenerateThumbprintSha256() throws NoSuchAlgorithmException {
String[] test = new String[] {"abcdefg"};
String encoded = PemUtils.generateThumbprint(test, "SHA-256");
assertEquals(43, encoded.length());
}
}

View file

@ -37,8 +37,8 @@ cd keycloak-999-SNAPSHOT/bin
./kc.sh start-dev
```
NOTE: Right now, server should start, and I am able to create admin user on `http://localhost:8080`, but I am not able to finish
login to the admin console. However the Keycloak uses bouncycastle-fips libraries and the `CryptoIntegration` uses `FIPS1402Provider`. More fixes are required to have Keycloak server working...
NOTE: Right now, server should start, and you should be able to use `http://localhost:8080` and login to admin console etc.
Keycloak will now use bouncycastle-fips libraries and the `CryptoIntegration` will use `FIPS1402Provider`.
Run the tests in the FIPS environment
-------------------------------------