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:
parent
6ad71557de
commit
19daf2b375
10 changed files with 291 additions and 107 deletions
|
@ -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);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
136
core/src/test/java/org/keycloak/util/PemUtilsTest.java
Normal file
136
core/src/test/java/org/keycloak/util/PemUtilsTest.java
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
-------------------------------------
|
||||
|
|
Loading…
Reference in a new issue