Skip adding xmlsec security provider. Adding KeycloakFipsSecurityProvider to workaround 'Security.getInstance("SHA1PRNG")' (#12786)

Closes #12425 #12853
This commit is contained in:
Marek Posolda 2022-07-26 16:40:36 +02:00 committed by GitHub
parent bbda7526dd
commit 4e4fc16617
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 279 additions and 387 deletions

View file

@ -1,8 +1,9 @@
package org.keycloak.common.crypto; package org.keycloak.common.crypto;
import java.security.Provider;
import java.security.Security;
import java.util.List; import java.util.List;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
@ -25,6 +26,10 @@ public class CryptoIntegration {
if (cryptoProvider == null) { if (cryptoProvider == null) {
cryptoProvider = detectProvider(classLoader); cryptoProvider = detectProvider(classLoader);
logger.debugv("BouncyCastle provider: {0}", BouncyIntegration.PROVIDER); logger.debugv("BouncyCastle provider: {0}", BouncyIntegration.PROVIDER);
if (logger.isTraceEnabled()) {
logger.tracef(dumpJavaSecurityProviders());
}
} }
} }
} }
@ -49,9 +54,17 @@ public class CryptoIntegration {
throw new IllegalStateException("Multiple crypto providers loaded with the classLoader: " + classLoader + throw new IllegalStateException("Multiple crypto providers loaded with the classLoader: " + classLoader +
". Make sure only one cryptoProvider available on the classpath. Available providers: " +foundProviders); ". Make sure only one cryptoProvider available on the classpath. Available providers: " +foundProviders);
} else { } else {
logger.infof("Detected security provider: %s", foundProviders.get(0).getClass().getName()); logger.infof("Detected crypto provider: %s", foundProviders.get(0).getClass().getName());
return foundProviders.get(0); return foundProviders.get(0);
} }
} }
public static String dumpJavaSecurityProviders() {
StringBuilder builder = new StringBuilder("Java security providers: [ \n");
for (Provider p : Security.getProviders()) {
builder.append(" " + p.toString() + " - " + p.getClass() + ", \n");
}
return builder.append("]").toString();
}
} }

View file

@ -1,8 +1,5 @@
package org.keycloak.common.crypto; package org.keycloak.common.crypto;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.spec.ECParameterSpec; import java.security.spec.ECParameterSpec;
/** /**
@ -12,11 +9,6 @@ import java.security.spec.ECParameterSpec;
*/ */
public interface CryptoProvider { public interface CryptoProvider {
/**
* @return secureRandom implementation based on the available security algorithms according to environment (FIPS non-fips)
*/
SecureRandom getSecureRandom() throws NoSuchAlgorithmException, NoSuchProviderException;
/** /**
* Get some algorithm provider implementation. Returned implementation can be dependent according to if we have * Get some algorithm provider implementation. Returned implementation can be dependent according to if we have
* non-fips bouncycastle or fips bouncycastle on the classpath. * non-fips bouncycastle or fips bouncycastle on the classpath.

View file

@ -17,6 +17,9 @@
package org.keycloak.common.util; package org.keycloak.common.util;
import java.security.Provider;
import java.security.Security;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
@ -35,4 +38,16 @@ public class Environment {
} }
} }
/**
* Tries to detect if Java platform is in the FIPS mode
* @return true if java is FIPS mode
*/
public static boolean isJavaInFipsMode() {
for (Provider provider : Security.getProviders()) {
if (provider.getName().equals("BCFIPS")) continue; // Ignore BCFIPS provider for the detection as we may register it programatically
if (provider.getName().toUpperCase().contains("FIPS")) return true;
}
return false;
}
} }

View file

@ -138,7 +138,7 @@ public abstract class JWKTest {
@Test @Test
public void publicEs256() throws Exception { public void publicEs256() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", BouncyIntegration.PROVIDER); KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", BouncyIntegration.PROVIDER);
SecureRandom randomGen = CryptoIntegration.getProvider().getSecureRandom(); SecureRandom randomGen = new SecureRandom();
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1"); ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");
keyGen.initialize(ecSpec, randomGen); keyGen.initialize(ecSpec, randomGen);
KeyPair keyPair = keyGen.generateKeyPair(); KeyPair keyPair = keyGen.generateKeyPair();

View file

@ -1,11 +1,8 @@
package org.keycloak.crypto.def; package org.keycloak.crypto.def;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.ECParameterSpec; import java.security.spec.ECParameterSpec;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier; import java.util.concurrent.ConcurrentHashMap;
import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
@ -21,21 +18,16 @@ import org.keycloak.common.crypto.PemUtilsProvider;
*/ */
public class DefaultCryptoProvider implements CryptoProvider { public class DefaultCryptoProvider implements CryptoProvider {
private Map<String, Supplier<?>> providers = new HashMap<>(); private Map<String, Object> providers = new ConcurrentHashMap<>();
public DefaultCryptoProvider() { public DefaultCryptoProvider() {
providers.put(CryptoProviderTypes.BC_SECURITY_PROVIDER, BouncyCastleProvider::new); providers.put(CryptoProviderTypes.BC_SECURITY_PROVIDER, new BouncyCastleProvider());
providers.put(CryptoProviderTypes.AES_KEY_WRAP_ALGORITHM_PROVIDER, AesKeyWrapAlgorithmProvider::new); providers.put(CryptoProviderTypes.AES_KEY_WRAP_ALGORITHM_PROVIDER, new AesKeyWrapAlgorithmProvider());
}
@Override
public SecureRandom getSecureRandom() throws NoSuchAlgorithmException {
return SecureRandom.getInstance("SHA1PRNG");
} }
@Override @Override
public <T> T getAlgorithmProvider(Class<T> clazz, String algorithm) { public <T> T getAlgorithmProvider(Class<T> clazz, String algorithm) {
Object o = providers.get(algorithm).get(); Object o = providers.get(algorithm);
if (o == null) { if (o == null) {
throw new IllegalArgumentException("Not found provider of algorithm: " + algorithm); throw new IllegalArgumentException("Not found provider of algorithm: " + algorithm);
} }

View file

@ -1,5 +1,8 @@
package org.keycloak.crypto.def.test; package org.keycloak.crypto.def.test;
import org.junit.Assume;
import org.junit.Before;
import org.keycloak.common.util.Environment;
import org.keycloak.jose.JWETest; import org.keycloak.jose.JWETest;
/** /**
@ -8,4 +11,10 @@ import org.keycloak.jose.JWETest;
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class DefaultCryptoJWETest extends JWETest { public class DefaultCryptoJWETest extends JWETest {
@Before
public void before() {
// Run this test just if java is not in FIPS mode
Assume.assumeFalse("Java is in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode());
}
} }

View file

@ -1,5 +1,8 @@
package org.keycloak.crypto.def.test; package org.keycloak.crypto.def.test;
import org.junit.Assume;
import org.junit.Before;
import org.keycloak.common.util.Environment;
import org.keycloak.jose.jwk.JWKTest; import org.keycloak.jose.jwk.JWKTest;
/** /**
@ -8,4 +11,10 @@ import org.keycloak.jose.jwk.JWKTest;
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class DefaultCryptoJWKTest extends JWKTest { public class DefaultCryptoJWKTest extends JWKTest {
@Before
public void before() {
// Run this test just if java is not in FIPS mode
Assume.assumeFalse("Java is in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode());
}
} }

View file

@ -1,6 +1,9 @@
package org.keycloak.crypto.def.test; package org.keycloak.crypto.def.test;
import org.junit.Assume;
import org.junit.Before;
import org.keycloak.KeyPairVerifierTest; import org.keycloak.KeyPairVerifierTest;
import org.keycloak.common.util.Environment;
/** /**
* Test with default security provider and non-fips bouncycastle * Test with default security provider and non-fips bouncycastle
@ -8,4 +11,10 @@ import org.keycloak.KeyPairVerifierTest;
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class DefaultCryptoKeyPairVerifierTest extends KeyPairVerifierTest { public class DefaultCryptoKeyPairVerifierTest extends KeyPairVerifierTest {
@Before
public void before() {
// Run this test just if java is not in FIPS mode
Assume.assumeFalse("Java is in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode());
}
} }

View file

@ -0,0 +1,42 @@
package org.keycloak.crypto.def.test;
import java.security.SecureRandom;
import org.jboss.logging.Logger;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.rule.CryptoInitRule;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class DefaultSecureRandomTest {
@ClassRule
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
protected static final Logger logger = Logger.getLogger(DefaultSecureRandomTest.class);
@Test
public void testSecureRandom() throws Exception {
logger.info(CryptoIntegration.dumpJavaSecurityProviders());
SecureRandom sc1 = new SecureRandom();
logger.infof(dumpSecureRandom("new SecureRandom()", sc1));
SecureRandom sc3 = SecureRandom.getInstance("SHA1PRNG");
logger.infof(dumpSecureRandom("SecureRandom.getInstance(\"SHA1PRNG\")", sc3));
Assert.assertEquals("SHA1PRNG", sc3.getAlgorithm());
}
private String dumpSecureRandom(String prefix, SecureRandom secureRandom) {
StringBuilder builder = new StringBuilder(prefix + ": algorithm: " + secureRandom.getAlgorithm() + ", provider: " + secureRandom.getProvider() + ", random numbers: ");
for (int i=0; i < 5; i++) {
builder.append(secureRandom.nextInt(1000) + ", ");
}
return builder.toString();
}
}

View file

@ -1,17 +1,14 @@
package org.keycloak.crypto.fips; package org.keycloak.crypto.fips;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.spec.ECField; import java.security.spec.ECField;
import java.security.spec.ECFieldF2m; import java.security.spec.ECFieldF2m;
import java.security.spec.ECFieldFp; import java.security.spec.ECFieldFp;
import java.security.spec.ECParameterSpec; import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint; import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve; import java.security.spec.EllipticCurve;
import java.util.HashMap; import java.security.Security;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier; import java.util.concurrent.ConcurrentHashMap;
import org.bouncycastle.asn1.x9.ECNamedCurveTable; import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.asn1.x9.X9ECParameters;
@ -30,21 +27,19 @@ import org.keycloak.common.crypto.PemUtilsProvider;
*/ */
public class FIPS1402Provider implements CryptoProvider { public class FIPS1402Provider implements CryptoProvider {
private Map<String, Supplier<?>> providers = new HashMap<>(); private final Map<String, Object> providers = new ConcurrentHashMap<>();
public FIPS1402Provider() { public FIPS1402Provider() {
providers.put(CryptoProviderTypes.BC_SECURITY_PROVIDER, BouncyCastleFipsProvider::new); BouncyCastleFipsProvider bcFipsProvider = new BouncyCastleFipsProvider();
providers.put(CryptoProviderTypes.AES_KEY_WRAP_ALGORITHM_PROVIDER, FIPSAesKeyWrapAlgorithmProvider::new); providers.put(CryptoProviderTypes.BC_SECURITY_PROVIDER, bcFipsProvider);
} providers.put(CryptoProviderTypes.AES_KEY_WRAP_ALGORITHM_PROVIDER, new FIPSAesKeyWrapAlgorithmProvider());
@Override Security.insertProviderAt(new KeycloakFipsSecurityProvider(bcFipsProvider), 1);
public SecureRandom getSecureRandom() throws NoSuchAlgorithmException, NoSuchProviderException {
return SecureRandom.getInstance("DEFAULT","BCFIPS");
} }
@Override @Override
public <T> T getAlgorithmProvider(Class<T> clazz, String algorithm) { public <T> T getAlgorithmProvider(Class<T> clazz, String algorithm) {
Object o = providers.get(algorithm).get(); Object o = providers.get(algorithm);
if (o == null) { if (o == null) {
throw new IllegalArgumentException("Not found provider of algorithm: " + algorithm); throw new IllegalArgumentException("Not found provider of algorithm: " + algorithm);
} }

View file

@ -0,0 +1,35 @@
package org.keycloak.crypto.fips;
import java.security.Provider;
import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider;
import org.jboss.logging.Logger;
/**
* Security provider to workaround usage of potentially unsecured algorithms by 3rd party dependencies.
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class KeycloakFipsSecurityProvider extends Provider {
protected static final Logger logger = Logger.getLogger(KeycloakFipsSecurityProvider.class);
private final BouncyCastleFipsProvider bcFipsProvider;
public KeycloakFipsSecurityProvider(BouncyCastleFipsProvider bcFipsProvider) {
super("KC", 1, "Keycloak pseudo provider");
this.bcFipsProvider = bcFipsProvider;
}
@Override
public synchronized final Service getService(String type, String algorithm) {
// Using 'SecureRandom.getInstance("SHA1PRNG")' will delegate to BCFIPS DEFAULT provider instead of returning SecureRandom based on potentially unsecure SHA1PRNG
if ("SHA1PRNG".equals(algorithm) && "SecureRandom".equals(type)) {
logger.debug("Returning DEFAULT algorithm of BCFIPS provider instead of SHA1PRNG");
return this.bcFipsProvider.getService("SecureRandom", "DEFAULT");
} else {
return null;
}
}
}

View file

@ -1,5 +1,8 @@
package org.keycloak.crypto.fips.test; package org.keycloak.crypto.fips.test;
import org.junit.Assume;
import org.junit.Before;
import org.keycloak.common.util.Environment;
import org.keycloak.jose.JWETest; import org.keycloak.jose.JWETest;
/** /**
@ -8,4 +11,10 @@ import org.keycloak.jose.JWETest;
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class FIPS1402JWETest extends JWETest { public class FIPS1402JWETest extends JWETest {
@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());
}
} }

View file

@ -1,7 +1,10 @@
package org.keycloak.crypto.fips.test; package org.keycloak.crypto.fips.test;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.keycloak.common.util.Environment;
import org.keycloak.jose.jwk.JWKTest; import org.keycloak.jose.jwk.JWKTest;
/** /**
@ -11,10 +14,15 @@ import org.keycloak.jose.jwk.JWKTest;
*/ */
public class FIPS1402JWKTest extends JWKTest { public class FIPS1402JWKTest extends JWKTest {
@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());
}
@Ignore("Test not supported by BC FIPS") @Ignore("Test not supported by BC FIPS")
@Test @Test
public void publicEs256() throws Exception { public void publicEs256() throws Exception {
// Do nothing // Do nothing
} }
} }

View file

@ -1,7 +1,9 @@
package org.keycloak.crypto.fips.test; package org.keycloak.crypto.fips.test;
import org.junit.Before; import org.junit.Before;
import org.junit.Assume;
import org.keycloak.KeyPairVerifierTest; import org.keycloak.KeyPairVerifierTest;
import org.keycloak.common.util.Environment;
/** /**
* Test with fips1402 security provider and bouncycastle-fips * Test with fips1402 security provider and bouncycastle-fips
@ -15,47 +17,51 @@ public class FIPS1402KeyPairVerifierTest extends KeyPairVerifierTest {
// The parent private key is not supported in FIPS-140-2, using a PKCS#8 formatted key // The parent private key is not supported in FIPS-140-2, using a PKCS#8 formatted key
privateKey1 = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKtWsK5O0CtuBpnM" + privateKey1 = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKtWsK5O0CtuBpnM" +
"vWG+HTG0vmZzujQ2o9WdheQu+BzCILcGMsbDW0YQaglpcO5JpGWWhubnckGGPHfd" + "vWG+HTG0vmZzujQ2o9WdheQu+BzCILcGMsbDW0YQaglpcO5JpGWWhubnckGGPHfd" +
"Q2/7nP9QwbiTK0FbGF41UqcvoaCqU1psxoV88s8IXyQCAqeyLv00yj6foqdJjxh5" + "Q2/7nP9QwbiTK0FbGF41UqcvoaCqU1psxoV88s8IXyQCAqeyLv00yj6foqdJjxh5" +
"SZ5z+na+M7Y2OxIBVxYRAxWEnfUvAgMBAAECgYB+Y7yBWHIHF2qXGYi6CVvPxtyN" + "SZ5z+na+M7Y2OxIBVxYRAxWEnfUvAgMBAAECgYB+Y7yBWHIHF2qXGYi6CVvPxtyN" +
"BuFcktHYShLyeBNeY3VujYv3QzSZQpJ1zuoXXQuARMHOovyNiVAhu357pMfx9wSk" + "BuFcktHYShLyeBNeY3VujYv3QzSZQpJ1zuoXXQuARMHOovyNiVAhu357pMfx9wSk" +
"oKNSXKrQx/+9Vt9lI1pXJxjXedPOjbuI/JZAcrk0u4nOfXG/HGtR5cjoDZYWkYQE" + "oKNSXKrQx/+9Vt9lI1pXJxjXedPOjbuI/JZAcrk0u4nOfXG/HGtR5cjoDZYWkYQE" +
"tsePCnHlZAb0D7axwQJBAO92f00Tvkc9NU/EGqwR3bPXRMqSX0JnG7XRBvLeJBCZ" + "tsePCnHlZAb0D7axwQJBAO92f00Tvkc9NU/EGqwR3bPXRMqSX0JnG7XRBvLeJBCZ" +
"YsQn0s2bLdpy8qsTeAyJg1ZvrEc8qIio5HVqzsvbhpMCQQC3K9A6UK+vmQCNWqsQ" + "YsQn0s2bLdpy8qsTeAyJg1ZvrEc8qIio5HVqzsvbhpMCQQC3K9A6UK+vmQCNWqsQ" +
"pdqWPRPN7CPB67FzSmyS8CtMjY6jTvSHrkamggotz2N/5QDr1xG2q7A/3dpkq1bT" + "pdqWPRPN7CPB67FzSmyS8CtMjY6jTvSHrkamggotz2N/5QDr1xG2q7A/3dpkq1bT" +
"pTx1AkAXZjjiSz+Yrn57IOqKTeSgIjTypoLwdirbBWXsbZCQnqxsBogu1y8P3ZOg" + "pTx1AkAXZjjiSz+Yrn57IOqKTeSgIjTypoLwdirbBWXsbZCQnqxsBogu1y8P3ZOg" +
"6/IbJ4TR+W+YNnExiW9pmdpDSVxJAkEAplTq6YmLf/F4RuQmox94tyUPbtcYQWg9" + "6/IbJ4TR+W+YNnExiW9pmdpDSVxJAkEAplTq6YmLf/F4RuQmox94tyUPbtcYQWg9" +
"42uZ3HSrXQDOng18kBj5nwpHJAJHYEQb6g2K0E5n5hcX0oKkfdx2YQJAcSKAmFiD" + "42uZ3HSrXQDOng18kBj5nwpHJAJHYEQb6g2K0E5n5hcX0oKkfdx2YQJAcSKAmFiD" +
"7KQ6+vVqJlQwVPvYdTSOeZB7YVV6S4b4slS3ZObsa0yNMWgal/QnCtW5k3f185gC" + "7KQ6+vVqJlQwVPvYdTSOeZB7YVV6S4b4slS3ZObsa0yNMWgal/QnCtW5k3f185gC" +
"Wj6dOLGB5btfxg=="; "Wj6dOLGB5btfxg==";
// The parent private key is not supported in FIPS-140-2, using a PKCS#8 formatted key // The parent private key is not supported in FIPS-140-2, using a PKCS#8 formatted key
privateKey2048 = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDhXcyk6e4qx0Ft" + privateKey2048 = "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" +
"jLsXjc2CPf/lwNFqsVl7dlPNmg=="; "jLsXjc2CPf/lwNFqsVl7dlPNmg==";
} }
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());
}
} }

View file

@ -0,0 +1,57 @@
package org.keycloak.crypto.fips.test;
import java.security.SecureRandom;
import org.jboss.logging.Logger;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.util.Environment;
import org.keycloak.rule.CryptoInitRule;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class FIPS1402SecureRandomTest {
@ClassRule
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
@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());
}
protected static final Logger logger = Logger.getLogger(FIPS1402SecureRandomTest.class);
@Test
public void testSecureRandom() throws Exception {
logger.info(CryptoIntegration.dumpJavaSecurityProviders());
SecureRandom sc1 = new SecureRandom();
logger.infof(dumpSecureRandom("new SecureRandom()", sc1));
SecureRandom sc2 = SecureRandom.getInstance("DEFAULT", "BCFIPS");
logger.infof(dumpSecureRandom("SecureRandom.getInstance(\"DEFAULT\", \"BCFIPS\")", sc2));
Assert.assertEquals("DEFAULT", sc2.getAlgorithm());
Assert.assertEquals("BCFIPS", sc2.getProvider().getName());
SecureRandom sc3 = SecureRandom.getInstance("SHA1PRNG");
logger.infof(dumpSecureRandom("SecureRandom.getInstance(\"SHA1PRNG\")", sc3));
Assert.assertEquals("SHA1PRNG", sc3.getAlgorithm());
Assert.assertEquals("BCFIPS", sc3.getProvider().getName());
}
private String dumpSecureRandom(String prefix, SecureRandom secureRandom) {
StringBuilder builder = new StringBuilder(prefix + ": algorithm: " + secureRandom.getAlgorithm() + ", provider: " + secureRandom.getProvider() + ", random numbers: ");
for (int i=0; i < 5; i++) {
builder.append(secureRandom.nextInt(1000) + ", ");
}
return builder.toString();
}
}

View file

@ -1,127 +0,0 @@
/*
* 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.saml.processing.core.util;
import org.keycloak.saml.common.PicketLinkLogger;
import org.keycloak.saml.common.PicketLinkLoggerFactory;
import org.keycloak.saml.common.util.SecurityActions;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.Provider;
import java.security.Security;
/**
* Utility dealing with the Santuario (XMLSec) providers registration for PicketLink
*
* @author alessio.soldano@jboss.com
* @since 07-May-2012
*/
public class ProvidersUtil {
private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
/**
* No-op call such that the default system properties are set
*/
public static synchronized void ensure() {
AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
// register Apache Santuario 1.5.x XMLDSig version
addXMLDSigRI();
// register BC provider if available (to have additional encryption algorithms, etc.)
// addJceProvider("BC", "org.bouncycastle.jce.provider.BouncyCastleProvider");
return true;
}
});
}
private static void addXMLDSigRI() {
try {
Class<?> clazz = SecurityActions
.loadClass(XMLSignatureUtil.class, "org.apache.jcp.xml.dsig.internal.dom.XMLDSigRI");
if (clazz == null)
throw logger.classNotLoadedError("org.apache.jcp.xml.dsig.internal.dom.XMLDSigRI");
addJceProvider("ApacheXMLDSig", (Provider) clazz.newInstance());
} catch (Throwable t) {
// ignore - may be a NoClassDefFound if XMLDSigRI isn't avail
return;
}
}
/**
* Add a new JCE security provider to use for PicketLink.
*
* @param name The name string of the provider (this may not be the real name of the provider)
* @param provider A subclass of <code>java.security.Provider</code>
*
* @return Returns the actual name of the provider that was loaded
*/
private static String addJceProvider(String name, Provider provider) {
Provider currentProvider = Security.getProvider(name);
if (currentProvider == null) {
try {
//
// Install the provider after the SUN provider (see WSS-99)
// Otherwise fall back to the old behaviour of inserting
// the provider in position 2. For AIX, install it after
// the IBMJCE provider.
//
int ret = 0;
Provider[] provs = Security.getProviders();
for (int i = 0; i < provs.length; i++) {
if ("SUN".equals(provs[i].getName()) || "IBMJCE".equals(provs[i].getName())) {
ret = Security.insertProviderAt(provider, i + 2);
break;
}
}
if (ret == 0) {
ret = Security.insertProviderAt(provider, 2);
}
if (logger.isDebugEnabled()) {
logger.debug("The provider " + provider.getName() + " - " + provider.getVersion() + " was added at position: "
+ ret);
}
return provider.getName();
} catch (Throwable t) {
if (logger.isDebugEnabled()) {
logger.jceProviderCouldNotBeLoaded(name, t);
}
return null;
}
}
return currentProvider.getName();
}
private static String addJceProvider(String name, String className) {
Provider currentProvider = Security.getProvider(name);
if (currentProvider == null) {
try {
// Class<? extends Provider> clazz = Loader.loadClass(className, false, Provider.class);
Class<? extends Provider> clazz = Class.forName(className).asSubclass(Provider.class);
Provider provider = clazz.newInstance();
return addJceProvider(name, provider);
} catch (Throwable t) {
if (logger.isDebugEnabled()) {
logger.jceProviderCouldNotBeLoaded(name, t);
}
return null;
}
}
return currentProvider.getName();
}
}

View file

@ -24,7 +24,6 @@ import org.apache.xml.security.utils.EncryptionConstants;
import org.keycloak.saml.common.PicketLinkLogger; import org.keycloak.saml.common.PicketLinkLogger;
import org.keycloak.saml.common.PicketLinkLoggerFactory; import org.keycloak.saml.common.PicketLinkLoggerFactory;
import org.keycloak.saml.common.exceptions.ConfigurationException;
import org.keycloak.saml.common.exceptions.ProcessingException; import org.keycloak.saml.common.exceptions.ProcessingException;
import org.keycloak.saml.common.util.DocumentUtil; import org.keycloak.saml.common.util.DocumentUtil;
import org.keycloak.saml.common.util.StringUtil; import org.keycloak.saml.common.util.StringUtil;
@ -86,7 +85,7 @@ public class XMLEncryptionUtil {
* *
* @throws org.keycloak.saml.common.exceptions.ProcessingException * @throws org.keycloak.saml.common.exceptions.ProcessingException
*/ */
public static EncryptedKey encryptKey(Document document, SecretKey keyToBeEncrypted, PublicKey keyUsedToEncryptSecretKey, private static EncryptedKey encryptKey(Document document, SecretKey keyToBeEncrypted, PublicKey keyUsedToEncryptSecretKey,
int keySize) throws ProcessingException { int keySize) throws ProcessingException {
XMLCipher keyCipher; XMLCipher keyCipher;
String pubKeyAlg = keyUsedToEncryptSecretKey.getAlgorithm(); String pubKeyAlg = keyUsedToEncryptSecretKey.getAlgorithm();
@ -197,176 +196,6 @@ public class XMLEncryptionUtil {
} }
} }
/**
* <p>
* Encrypts an element in a XML document using the specified public key, secret key, and key size. This method
* doesn't wrap
* the encrypted element in a new element. Instead, it replaces the element with its encrypted version.
* </p>
* <p>
* For example, calling this method to encrypt the <tt><b>inner</b></tt> element in the following XML document
*
* <pre>
* &lt;root&gt;
* &lt;outer&gt;
* &lt;inner&gt;
* ...
* &lt;/inner&gt;
* &lt;/outer&gt;
* &lt;/root&gt;
* </pre>
*
* would result in a document similar to
*
* <pre>
* &lt;root&gt;
* &lt;outer&gt;
* &lt;xenc:EncryptedData xmlns:xenc="..."&gt;
* ...
* &lt;/xenc:EncryptedData&gt;
* &lt;/outer&gt;
* &lt;/root&gt;
* </pre>
*
* </p>
*
* @param document the {@code Document} that contains the element to be encrypted.
* @param element the {@code Element} to be encrypted.
* @param publicKey the {@code PublicKey} that must be used to encrypt the secret key.
* @param secretKey the {@code SecretKey} used to encrypt the specified element.
* @param keySize the size (in bits) of the secret key.
*
* @throws ProcessingException if an error occurs while encrypting the element with the specified params.
*/
public static void encryptElement(Document document, Element element, PublicKey publicKey, SecretKey secretKey, int keySize)
throws ProcessingException {
if (element == null)
throw logger.nullArgumentError("element");
if (document == null)
throw logger.nullArgumentError("document");
XMLCipher cipher = null;
EncryptedKey encryptedKey = encryptKey(document, secretKey, publicKey, keySize);
String encryptionAlgorithm = getXMLEncryptionURL(secretKey.getAlgorithm(), keySize);
// Encrypt the Document
try {
cipher = XMLCipher.getInstance(encryptionAlgorithm);
cipher.init(XMLCipher.ENCRYPT_MODE, secretKey);
} catch (XMLEncryptionException e1) {
throw logger.processingError(e1);
}
Document encryptedDoc;
try {
encryptedDoc = cipher.doFinal(document, element);
} catch (Exception e) {
throw logger.processingError(e);
}
// The EncryptedKey element is added
Element encryptedKeyElement = cipher.martial(document, encryptedKey);
// Outer ds:KeyInfo Element to hold the EncryptionKey
Element sigElement = encryptedDoc.createElementNS(XMLSignature.XMLNS, DS_KEY_INFO);
sigElement.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:ds", XMLSignature.XMLNS);
sigElement.appendChild(encryptedKeyElement);
// Insert the Encrypted key before the CipherData element
NodeList nodeList = encryptedDoc.getElementsByTagNameNS(EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_CIPHERDATA);
if (nodeList == null || nodeList.getLength() == 0)
throw logger.domMissingElementError("xenc:CipherData");
Element cipherDataElement = (Element) nodeList.item(0);
Node cipherParent = cipherDataElement.getParentNode();
cipherParent.insertBefore(sigElement, cipherDataElement);
}
/**
* Encrypt the root document element inside a Document. <b>NOTE:</b> The document root element will be replaced by
* the
* wrapping element.
*
* @param document Document that contains an element to encrypt
* @param publicKey The Public Key used to encrypt the secret encryption key
* @param secretKey The secret encryption key
* @param keySize Length of key
* @param wrappingElementQName QName of the element to be used to wrap around the cipher data.
* @param addEncryptedKeyInKeyInfo Should the encrypted key be inside a KeyInfo or added as a peer of Cipher Data
*
* @return An element that has the wrappingElementQName
*
* @throws ProcessingException
* @throws org.keycloak.saml.common.exceptions.ConfigurationException
*/
public static Element encryptElementInDocument(Document document, PublicKey publicKey, SecretKey secretKey, int keySize,
QName wrappingElementQName, boolean addEncryptedKeyInKeyInfo) throws ProcessingException, ConfigurationException {
String wrappingElementPrefix = wrappingElementQName.getPrefix();
if (wrappingElementPrefix == null || "".equals(wrappingElementPrefix))
throw logger.wrongTypeError("Wrapping element prefix invalid");
XMLCipher cipher = null;
EncryptedKey encryptedKey = encryptKey(document, secretKey, publicKey, keySize);
String encryptionAlgorithm = getXMLEncryptionURL(secretKey.getAlgorithm(), keySize);
// Encrypt the Document
try {
cipher = XMLCipher.getInstance(encryptionAlgorithm);
cipher.init(XMLCipher.ENCRYPT_MODE, secretKey);
} catch (XMLEncryptionException e1) {
throw logger.configurationError(e1);
}
Document encryptedDoc;
try {
encryptedDoc = cipher.doFinal(document, document.getDocumentElement());
} catch (Exception e) {
throw logger.processingError(e);
}
// The EncryptedKey element is added
Element encryptedKeyElement = cipher.martial(document, encryptedKey);
final String wrappingElementName;
if (StringUtil.isNullOrEmpty(wrappingElementPrefix)) {
wrappingElementName = wrappingElementQName.getLocalPart();
} else {
wrappingElementName = wrappingElementPrefix + ":" + wrappingElementQName.getLocalPart();
}
// Create the wrapping element and set its attribute NS
Element wrappingElement = encryptedDoc.createElementNS(wrappingElementQName.getNamespaceURI(), wrappingElementName);
if (! StringUtil.isNullOrEmpty(wrappingElementPrefix)) {
wrappingElement.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + wrappingElementPrefix, wrappingElementQName.getNamespaceURI());
}
Element encryptedDocRootElement = encryptedDoc.getDocumentElement();
// Bring in the encrypted wrapping element to wrap the root node
encryptedDoc.replaceChild(wrappingElement, encryptedDocRootElement);
wrappingElement.appendChild(encryptedDocRootElement);
if (addEncryptedKeyInKeyInfo) {
// Outer ds:KeyInfo Element to hold the EncryptionKey
Element sigElement = encryptedDoc.createElementNS(XMLSignature.XMLNS, DS_KEY_INFO);
sigElement.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:ds", XMLSignature.XMLNS);
sigElement.appendChild(encryptedKeyElement);
// Insert the Encrypted key before the CipherData element
NodeList nodeList = encryptedDocRootElement.getElementsByTagNameNS(EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_CIPHERDATA);
if (nodeList == null || nodeList.getLength() == 0)
throw logger.domMissingElementError("xenc:CipherData");
Element cipherDataElement = (Element) nodeList.item(0);
encryptedDocRootElement.insertBefore(sigElement, cipherDataElement);
} else {
// Add the encrypted key as a child of the wrapping element
wrappingElement.appendChild(encryptedKeyElement);
}
return encryptedDoc.getDocumentElement();
}
/** /**
* Decrypt an encrypted element inside a document * Decrypt an encrypted element inside a document
* *

View file

@ -107,7 +107,6 @@ public class XMLSignatureUtil {
// Set some system properties and Santuario providers. Run this block before any other class initialization. // Set some system properties and Santuario providers. Run this block before any other class initialization.
static { static {
ProvidersUtil.ensure();
SystemPropertiesUtil.ensure(); SystemPropertiesUtil.ensure();
String keyInfoProp = SecurityActions.getSystemProperty("picketlink.xmlsig.includeKeyInfo", null); String keyInfoProp = SecurityActions.getSystemProperty("picketlink.xmlsig.includeKeyInfo", null);
if (StringUtil.isNotNull(keyInfoProp)) { if (StringUtil.isNotNull(keyInfoProp)) {