Upgrade xmlsec to 3.0.4 and remove KeycloakFipsSecurityProvider workaround

Closes #43263

Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
rmartinc 2025-10-08 08:45:08 +02:00 committed by Marek Posolda
parent a77c4a6ad2
commit 248d6d1feb
15 changed files with 44 additions and 92 deletions

View File

@ -91,7 +91,7 @@ public class BCCertificateUtilsProvider implements CertificateUtilsProvider {
X500Name subjectDN = new X500Name("CN=" + subject);
// Serial Number
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
SecureRandom random = new SecureRandom();
BigInteger serialNumber = BigInteger.valueOf(Math.abs(random.nextInt()));
// Validity

View File

@ -130,7 +130,7 @@ public class BCEcdhEsAlgorithmProvider implements JWEAlgorithmProvider {
private static KeyPair generateEcKeyPair(ECParameterSpec params) {
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
SecureRandom randomGen = SecureRandom.getInstance("SHA1PRNG");
SecureRandom randomGen = new SecureRandom();
keyGen.initialize(params, randomGen);
return keyGen.generateKeyPair();
} catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException e) {

View File

@ -92,7 +92,7 @@ public class BCFIPSCertificateUtilsProvider implements CertificateUtilsProvider{
X500Name subjectDN = new X500Name("CN=" + subject);
// Serial Number
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
SecureRandom random = new SecureRandom();
BigInteger serialNumber = BigInteger.valueOf(Math.abs(random.nextInt()));
// Validity

View File

@ -1,6 +1,7 @@
package org.keycloak.crypto.fips;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
@ -46,6 +47,7 @@ import javax.net.ssl.TrustManagerFactory;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.fips.FipsRSA;
import org.bouncycastle.crypto.fips.FipsSHS;
import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider;
@ -91,17 +93,20 @@ public class FIPS1402Provider implements CryptoProvider {
providers.put(CryptoConstants.ECDH_ES_A192KW, new BCFIPSEcdhEsAlgorithmProvider());
providers.put(CryptoConstants.ECDH_ES_A256KW, new BCFIPSEcdhEsAlgorithmProvider());
Security.insertProviderAt(new KeycloakFipsSecurityProvider(bcFipsProvider), 1);
if (existingBcFipsProvider == null) {
checkSecureRandom(() -> Security.insertProviderAt(this.bcFipsProvider, 2));
checkSecureRandom(() -> Security.insertProviderAt(this.bcFipsProvider, 1));
Provider bcJsseProvider = new BouncyCastleJsseProvider("fips:BCFIPS");
Security.insertProviderAt(bcJsseProvider, 3);
Security.insertProviderAt(bcJsseProvider, 2);
// force the key and trust manager factories if default values not present in BCJSSE
modifyKeyTrustManagerSecurityProperties(bcJsseProvider);
log.debugf("Inserted security providers: %s", Arrays.asList(this.bcFipsProvider.getName(),bcJsseProvider.getName()));
} else {
log.debugf("Security provider %s already loaded", existingBcFipsProvider.getName());
}
log.infof("FIPS1402Provider created: KC(%s%s, FIPS-JVM: %s)", bcFipsProvider,
CryptoServicesRegistrar.isInApprovedOnlyMode() ? " Approved Mode" : "",
isSystemFipsEnabled());
}
@ -388,4 +393,23 @@ public class FIPS1402Provider implements CryptoProvider {
throw new IllegalStateException("Provider " + bcJsseProvider.getName()
+ " does not provide KeyManagerFactory or TrustManagerFactory algorithms for TLS");
}
public static String isSystemFipsEnabled() {
Method isSystemFipsEnabled = null;
try {
Class<?> securityConfigurator = FIPS1402Provider.class.getClassLoader().loadClass("java.security.SystemConfigurator");
isSystemFipsEnabled = securityConfigurator.getDeclaredMethod("isSystemFipsEnabled");
isSystemFipsEnabled.setAccessible(true);
boolean isEnabled = (boolean) isSystemFipsEnabled.invoke(null);
return isEnabled ? "enabled" : "disabled";
} catch (Throwable ignore) {
log.debug("Could not detect if FIPS is enabled from the host", ignore);
return "unknown";
} finally {
if (isSystemFipsEnabled != null) {
isSystemFipsEnabled.setAccessible(false);
}
}
}
}

View File

@ -1,61 +0,0 @@
package org.keycloak.crypto.fips;
import static org.bouncycastle.crypto.CryptoServicesRegistrar.isInApprovedOnlyMode;
import java.lang.reflect.Method;
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(" +
bcFipsProvider.toString() +
(isInApprovedOnlyMode() ? " Approved Mode" : "") +
", FIPS-JVM: " + isSystemFipsEnabled() +
")", 1, "Keycloak pseudo provider");
this.bcFipsProvider = bcFipsProvider;
logger.infof("KeycloakFipsSecurityProvider created: %s", this.toString());
}
@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;
}
}
public static String isSystemFipsEnabled() {
Method isSystemFipsEnabled = null;
try {
Class<?> securityConfigurator = KeycloakFipsSecurityProvider.class.getClassLoader().loadClass("java.security.SystemConfigurator");
isSystemFipsEnabled = securityConfigurator.getDeclaredMethod("isSystemFipsEnabled");
isSystemFipsEnabled.setAccessible(true);
boolean isEnabled = (boolean) isSystemFipsEnabled.invoke(null);
return isEnabled ? "enabled" : "disabled";
} catch (Throwable ignore) {
logger.debug("Could not detect if FIPS is enabled from the host", ignore);
return "unknown";
} finally {
if (isSystemFipsEnabled != null) {
isSystemFipsEnabled.setAccessible(false);
}
}
}
}

View File

@ -38,16 +38,13 @@ public class FIPS1402SecureRandomTest {
SecureRandom sc1 = new SecureRandom();
logger.infof(dumpSecureRandom("new SecureRandom()", sc1));
Assert.assertEquals("DEFAULT", sc1.getAlgorithm());
Assert.assertEquals("BCFIPS", sc1.getProvider().getName());
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());
}

View File

@ -122,7 +122,7 @@ When starting the server, you can check that the startup log contains `KC` provi
[source]
----
KeycloakFipsSecurityProvider created: KC(BCFIPS version 2.0102 Approved Mode, FIPS-JVM: enabled) version 1.0
FIPS1402Provider created: KC(BCFIPS version 2.0102 Approved Mode, FIPS-JVM: enabled)
----
=== Cryptography restrictions in strict mode

View File

@ -126,7 +126,7 @@
<woodstox.version>6.0.3</woodstox.version>
<wildfly.common.quarkus.aligned.version>1.5.4.Final-format-001</wildfly.common.quarkus.aligned.version>
<wildfly.common.wildfly.aligned.version>1.6.0.Final</wildfly.common.wildfly.aligned.version>
<xmlsec.version>2.2.6</xmlsec.version>
<xmlsec.version>3.0.4</xmlsec.version>
<nashorn.version>15.4</nashorn.version>
<ua-parser.version>1.6.1</ua-parser.version>
<org.yaml.snakeyaml.version>2.0</org.yaml.snakeyaml.version>

View File

@ -21,7 +21,7 @@ import java.nio.file.Path;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.keycloak.crypto.fips.KeycloakFipsSecurityProvider;
import org.keycloak.crypto.fips.FIPS1402Provider;
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.RawDistOnly;
@ -44,7 +44,7 @@ public class FipsDistTest {
cliResult.assertStarted();
// Not shown as FIPS is not a preview anymore
cliResult.assertMessageWasShownExactlyNumberOfTimes("Preview features enabled: fips:v1", 0);
cliResult.assertMessage("KeycloakFipsSecurityProvider created: KC(" + BCFIPS_VERSION + ", FIPS-JVM: " + KeycloakFipsSecurityProvider.isSystemFipsEnabled() + ") version 1.0");
cliResult.assertMessage("FIPS1402Provider created: KC(" + BCFIPS_VERSION + ", FIPS-JVM: " + FIPS1402Provider.isSystemFipsEnabled() + ")");
});
}
@ -56,7 +56,7 @@ public class FipsDistTest {
CLIResult cliResult = dist.run("start", "--fips-mode=strict");
cliResult.assertMessage("password must be at least 112 bits");
cliResult.assertMessage("KeycloakFipsSecurityProvider created: KC(" + BCFIPS_VERSION + " Approved Mode, FIPS-JVM: " + KeycloakFipsSecurityProvider.isSystemFipsEnabled() + ") version 1.0");
cliResult.assertMessage("FIPS1402Provider created: KC(" + BCFIPS_VERSION + " Approved Mode, FIPS-JVM: " + FIPS1402Provider.isSystemFipsEnabled() + ")");
dist.setEnvVar("KC_BOOTSTRAP_ADMIN_PASSWORD", "adminadminadmin");
cliResult = dist.run("start", "--fips-mode=strict");

View File

@ -52,7 +52,7 @@ public abstract class AbstractEcKeyProviderFactory<T extends KeyProvider> implem
public static KeyPair generateEcKeyPair(String keySpecName) {
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
SecureRandom randomGen = SecureRandom.getInstance("SHA1PRNG");
SecureRandom randomGen = new SecureRandom();
ECGenParameterSpec ecSpec = new ECGenParameterSpec(keySpecName);
keyGen.initialize(ecSpec, randomGen);
return keyGen.generateKeyPair();

View File

@ -10,7 +10,6 @@ import org.keycloak.utils.StringUtil;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Collections;
@ -101,7 +100,7 @@ public class DefaultSamlArtifactResolver implements ArtifactResolver {
*/
public String createArtifact(String entityId) throws ArtifactResolverProcessingException {
try {
SecureRandom handleGenerator = SecureRandom.getInstance("SHA1PRNG");
SecureRandom handleGenerator = new SecureRandom();
byte[] trimmedIndex = new byte[2];
byte[] source = ArtifactBindingUtils.computeArtifactBindingIdentifier(entityId);
@ -118,8 +117,6 @@ public class DefaultSamlArtifactResolver implements ArtifactResolver {
byte[] artifact = bos.toByteArray();
return Base64.getEncoder().encodeToString(artifact);
} catch (NoSuchAlgorithmException e) {
throw new ArtifactResolverProcessingException("JVM does not support required cryptography algorithms: SHA-1/SHA1PRNG.", e);
} catch (IOException e) {
throw new ArtifactResolverProcessingException(e);
}

View File

@ -85,18 +85,13 @@ public class SamlEncryptionTest {
@BeforeClass
public static void beforeClass() {
Cipher cipher = null;
SecureRandom random = null;
try {
// Apache santuario 2.2.3 needs to have SHA1PRNG (fixed in 3.0.2)
// see: https://issues.apache.org/jira/browse/SANTUARIO-589
random = SecureRandom.getInstance("SHA1PRNG");
// FIPS mode removes needed ciphers like "RSA/ECB/OAEPPadding"
cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
} catch (NoSuchAlgorithmException|NoSuchPaddingException e) {
// ignore
}
Assume.assumeNotNull("OAEPPadding not supported", cipher);
Assume.assumeNotNull("SHA1PRNG required for Apache santuario xmlsec", random);
}
private void testEncryption(KeyPair pair, String alg, int keySize, String keyWrapAlg, String keyWrapHashMethod, String keyWrapMgf) throws Exception {

View File

@ -190,7 +190,7 @@ public class TestingOIDCEndpointsApplicationResource {
private KeyPair generateEcdsaKey(String ecDomainParamName) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
SecureRandom randomGen = SecureRandom.getInstance("SHA1PRNG");
SecureRandom randomGen = new SecureRandom();
ECGenParameterSpec ecSpec = new ECGenParameterSpec(ecDomainParamName);
keyGen.initialize(ecSpec, randomGen);
KeyPair keyPair = keyGen.generateKeyPair();

View File

@ -469,17 +469,17 @@ public class BasicSamlTest extends AbstractSamlTest {
@Test
public void testEncryptionRsaOaep11Default() throws Exception {
testEncryption(XMLCipher.AES_256_GCM, XMLCipher.RSA_OAEP_11, XMLCipher.SHA1, EncryptionConstants.MGF1_SHA1, XMLCipher.AES_256_GCM, XMLCipher.RSA_OAEP_11, "", "");
testEncryption(XMLCipher.AES_256_GCM, XMLCipher.RSA_OAEP_11, XMLCipher.SHA1, EncryptionConstants.MGF1_SHA1, XMLCipher.AES_256_GCM, XMLCipher.RSA_OAEP_11, "", EncryptionConstants.MGF1_SHA1);
}
@Test
public void testEncryptionRsaOaep() throws Exception {
testEncryption(XMLCipher.AES_256_GCM, XMLCipher.RSA_OAEP, XMLCipher.SHA256, "");
testEncryption(XMLCipher.AES_256_GCM, XMLCipher.RSA_OAEP, XMLCipher.SHA256, "", XMLCipher.AES_256_GCM, XMLCipher.RSA_OAEP, XMLCipher.SHA256, EncryptionConstants.MGF1_SHA1);
}
@Test
public void testEncryptionRsaOaepLegacy() throws Exception {
testEncryption(XMLCipher.AES_128, XMLCipher.RSA_OAEP, XMLCipher.SHA1, "", XMLCipher.AES_128, XMLCipher.RSA_OAEP, "", "");
testEncryption(XMLCipher.AES_128, XMLCipher.RSA_OAEP, XMLCipher.SHA1, "", XMLCipher.AES_128, XMLCipher.RSA_OAEP, "", EncryptionConstants.MGF1_SHA1);
}
@Test

View File

@ -468,7 +468,7 @@ public final class ClientPoliciesUtil {
public static KeyPair generateEcdsaKey(String ecDomainParamName) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
SecureRandom randomGen = SecureRandom.getInstance("SHA1PRNG");
SecureRandom randomGen = new SecureRandom();
ECGenParameterSpec ecSpec = new ECGenParameterSpec(ecDomainParamName);
keyGen.initialize(ecSpec, randomGen);
KeyPair keyPair = keyGen.generateKeyPair();