Add AuthzClientCryptoProvider to authz-client in keycloak main reposi… - #1
Add AuthzClientCryptoProvider to authz-client in keycloak main reposi…#1linxia0415 wants to merge 2 commits into
Conversation
…tory closes #33831 Signed-off-by: mposolda <mposolda@gmail.com>
📝 WalkthroughWalkthroughThis PR adds ECDSA signature format conversion support to Keycloak's authorization client by introducing a minimal ChangesCrypto Provider Framework with ECDSA Support
Sequence DiagramssequenceDiagram
participant AuthzClient as AuthzClient.create()
participant CryptoInt as CryptoIntegration
participant SPI as ServiceLoader
participant Provider as AuthzClientCryptoProvider
participant ECDSA as ECDSACryptoProvider
AuthzClient->>CryptoInt: init(classLoader)
CryptoInt->>SPI: discover CryptoProvider implementations
SPI->>Provider: load via META-INF/services
Provider->>CryptoInt: register
CryptoInt->>CryptoInt: sort by order() descending
CryptoInt->>CryptoInt: select top-ranked provider
CryptoInt-->>AuthzClient: initialization complete
Note over AuthzClient,ECDSA: AuthzClient can now use ECDSA conversion
AuthzClient->>Provider: getEcdsaCryptoProvider()
Provider->>ECDSA: concatenatedRSToASN1DER(byte[])
ECDSA-->>AuthzClient: DER-encoded signature
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning Tools execution failed with the following error: Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error) Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@authz/client/src/main/java/org/keycloak/authorization/client/util/crypto/ASN1Decoder.java`:
- Around line 49-62: The DER parser must reject indefinite and non-exact
lengths: in ASN1Decoder methods like readSequence() and readInteger(), check the
value returned by readLength() and throw an IOException if it is -1 (indefinite)
or negative; in readSequence(), track remaining bytes and after each readNext()
subtract the exact number of bytes consumed and if a child TLV reports a length
greater than the remaining bytes throw an IOException, and after the loop ensure
remaining == 0 (reject trailing bytes) instead of allowing underrun/overrun;
also add the same defensive length validation to readInteger() to prevent
NegativeArraySizeException when readLength() returns invalid values.
In
`@authz/client/src/main/java/org/keycloak/authorization/client/util/crypto/AuthzClientCryptoProvider.java`:
- Around line 125-137: The asn1derToConcatenatedRS method is silently truncating
malformed/negative ASN.1 INTEGERs via integerToBytes; update integerToBytes (and
its call sites in asn1derToConcatenatedRS) to validate that the BigInteger's
encoded form does not contain extra non-zero leading bytes or a negative sign
byte beyond the expected length and throw an IOException when the integer cannot
be represented in the target len (instead of dropping bytes); ensure both r and
s are checked (the same fix applies to the later conversion of s) and include a
clear error message indicating an oversized or negative ASN.1 INTEGER so
malformed DER is rejected rather than converted.
In
`@authz/client/src/test/java/org/keycloak/authorization/client/test/ECDSAAlgorithmTest.java`:
- Around line 39-56: The test currently reuses a single EC keyPair created in
the ECDSAAlgorithmTest constructor which mismatches curves for
ES256/ES384/ES512; change the code so test(ECDSAAlgorithm algorithm) generates a
new, curve-matched KeyPair instead of using the shared keyPair: inside test(...)
use KeyPairGenerator.getInstance("EC") and initialize it with an
ECGenParameterSpec chosen by algorithm (e.g., "secp256r1" for ES256, "secp384r1"
for ES384, "secp521r1" for ES512 / P-521) and then call genKeyPair(); use that
KeyPair for signature initSign and remove reliance on the class-level keyPair
field or stop using it in test(...).
In `@common/src/main/java/org/keycloak/common/crypto/CryptoIntegration.java`:
- Around line 56-58: The code eagerly instantiates every CryptoProvider when
building foundProviders in CryptoIntegration, causing JVM-global Security
side-effects from provider constructors; instead avoid instantiation until the
highest-order provider is chosen by either (A) using the ServiceLoader provider
metadata API (ServiceLoader.Provider) to compare order/priority without calling
get(), or (B) refactoring providers so order/priority is discoverable without
construction (e.g., a class-level annotation or static method) and then only
instantiating the winner. Alternatively (or additionally) move any JVM-wide
Security mutations out of provider constructors in classes like
DefaultCryptoProvider and FIPS1402Provider into an explicit method (e.g.,
applySecurityProvider() or initializeSecurity()) that CryptoIntegration calls
only after selecting the winning CryptoProvider.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: e9fda27e-b4ea-4460-ae5d-e0851344db47
📒 Files selected for processing (13)
.github/dependabot.ymlauthz/client/pom.xmlauthz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.javaauthz/client/src/main/java/org/keycloak/authorization/client/util/crypto/ASN1Decoder.javaauthz/client/src/main/java/org/keycloak/authorization/client/util/crypto/ASN1Encoder.javaauthz/client/src/main/java/org/keycloak/authorization/client/util/crypto/AuthzClientCryptoProvider.javaauthz/client/src/main/resources/META-INF/services/org.keycloak.common.crypto.CryptoProviderauthz/client/src/test/java/org/keycloak/authorization/client/test/ECDSAAlgorithmTest.javacommon/src/main/java/org/keycloak/common/crypto/CryptoIntegration.javacommon/src/main/java/org/keycloak/common/crypto/CryptoProvider.javacrypto/default/src/main/java/org/keycloak/crypto/def/DefaultCryptoProvider.javacrypto/elytron/src/main/java/org/keycloak/crypto/elytron/WildFlyElytronProvider.javacrypto/fips1402/src/main/java/org/keycloak/crypto/fips/FIPS1402Provider.java
💤 Files with no reviewable changes (1)
- .github/dependabot.yml
| public List<byte[]> readSequence() throws IOException { | ||
| int tag = readTag(); | ||
| int tagNo = readTagNumber(tag); | ||
| if (tagNo != ASN1Encoder.SEQUENCE) { | ||
| throw new IOException("Invalid Sequence tag " + tagNo); | ||
| } | ||
| int length = readLength(); | ||
| List<byte[]> result = new ArrayList<>(); | ||
| while (length > 0) { | ||
| byte[] bytes = readNext(); | ||
| result.add(bytes); | ||
| length = length - bytes.length; | ||
| } | ||
| return result; |
There was a problem hiding this comment.
Reject indefinite and non-exact lengths in the DER parser.
readLength() returns -1 for indefinite-length encoding, but the callers treat it like a normal length: readSequence() can return an empty sequence and readInteger() can fall into NegativeArraySizeException. readSequence() also accepts child TLVs that overrun the declared SEQUENCE length, and it never rejects trailing bytes after the sequence. In the signature path, that means malformed DER can be accepted instead of failing cleanly.
Suggested fix
public List<byte[]> readSequence() throws IOException {
int tag = readTag();
int tagNo = readTagNumber(tag);
if (tagNo != ASN1Encoder.SEQUENCE) {
throw new IOException("Invalid Sequence tag " + tagNo);
}
int length = readLength();
List<byte[]> result = new ArrayList<>();
while (length > 0) {
byte[] bytes = readNext();
result.add(bytes);
length = length - bytes.length;
}
+ if (length != 0 || is.available() != 0) {
+ throw new IOException("Invalid DER sequence length");
+ }
return result;
}
public BigInteger readInteger() throws IOException {
int tag = readTag();
int tagNo = readTagNumber(tag);
if (tagNo != ASN1Encoder.INTEGER) {
throw new IOException("Invalid Integer tag " + tagNo);
}
int length = readLength();
byte[] bytes = read(length);
return new BigInteger(bytes);
}
int readLength() throws IOException {
int length = read();
if (length < 0) {
throw new EOFException("EOF found when length expected");
}
if (length == 0x80) {
- return -1; // indefinite-length encoding
+ throw new IOException("Indefinite-length encoding is not supported");
}Also applies to: 65-73, 127-135
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@authz/client/src/main/java/org/keycloak/authorization/client/util/crypto/ASN1Decoder.java`
around lines 49 - 62, The DER parser must reject indefinite and non-exact
lengths: in ASN1Decoder methods like readSequence() and readInteger(), check the
value returned by readLength() and throw an IOException if it is -1 (indefinite)
or negative; in readSequence(), track remaining bytes and after each readNext()
subtract the exact number of bytes consumed and if a child TLV reports a length
greater than the remaining bytes throw an IOException, and after the loop ensure
remaining == 0 (reject trailing bytes) instead of allowing underrun/overrun;
also add the same defensive length validation to readInteger() to prevent
NegativeArraySizeException when readLength() returns invalid values.
| public byte[] asn1derToConcatenatedRS(byte[] derEncodedSignatureValue, int signLength) throws IOException { | ||
| int len = signLength / 2; | ||
|
|
||
| List<byte[]> seq = ASN1Decoder.create(derEncodedSignatureValue).readSequence(); | ||
| if (seq.size() != 2) { | ||
| throw new IOException("Invalid sequence with size different to 2"); | ||
| } | ||
|
|
||
| BigInteger rBigInteger = ASN1Decoder.create(seq.get(0)).readInteger(); | ||
| BigInteger sBigInteger = ASN1Decoder.create(seq.get(1)).readInteger(); | ||
|
|
||
| byte[] r = integerToBytes(rBigInteger, len); | ||
| byte[] s = integerToBytes(sBigInteger, len); |
There was a problem hiding this comment.
Do not silently truncate invalid DER INTEGERs.
integerToBytes() currently drops any extra leading bytes, even when the ASN.1 INTEGER is negative or genuinely larger than the curve size. That converts malformed DER into a different r/s value instead of rejecting it.
Suggested fix
+import java.util.Arrays;
...
- private byte[] integerToBytes(BigInteger s, int qLength) {
- byte[] bytes = s.toByteArray();
- if (qLength < bytes.length) {
- byte[] tmp = new byte[qLength];
- System.arraycopy(bytes, bytes.length - tmp.length, tmp, 0, tmp.length);
- return tmp;
- } else if (qLength > bytes.length) {
+ private byte[] integerToBytes(BigInteger s, int qLength) throws IOException {
+ if (s.signum() < 0) {
+ throw new IOException("ECDSA signature values must be positive");
+ }
+
+ byte[] bytes = s.toByteArray();
+ if (bytes.length == qLength + 1 && bytes[0] == 0) {
+ bytes = Arrays.copyOfRange(bytes, 1, bytes.length);
+ } else if (bytes.length > qLength) {
+ throw new IOException("ECDSA signature value is too large");
+ }
+
+ if (qLength > bytes.length) {
byte[] tmp = new byte[qLength];
System.arraycopy(bytes, 0, tmp, tmp.length - bytes.length, bytes.length);
return tmp;
}
return bytes;Also applies to: 151-160
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@authz/client/src/main/java/org/keycloak/authorization/client/util/crypto/AuthzClientCryptoProvider.java`
around lines 125 - 137, The asn1derToConcatenatedRS method is silently
truncating malformed/negative ASN.1 INTEGERs via integerToBytes; update
integerToBytes (and its call sites in asn1derToConcatenatedRS) to validate that
the BigInteger's encoded form does not contain extra non-zero leading bytes or a
negative sign byte beyond the expected length and throw an IOException when the
integer cannot be represented in the target len (instead of dropping bytes);
ensure both r and s are checked (the same fix applies to the later conversion of
s) and include a clear error message indicating an oversized or negative ASN.1
INTEGER so malformed DER is rejected rather than converted.
| private final KeyPair keyPair; | ||
|
|
||
| public ECDSAAlgorithmTest() throws Exception { | ||
| keyPair = KeyPairGenerator.getInstance("EC").genKeyPair(); | ||
| } | ||
|
|
||
|
|
||
| private void test(ECDSAAlgorithm algorithm) throws Exception { | ||
| AuthzClientCryptoProvider prov = new AuthzClientCryptoProvider(); | ||
| byte[] data = "Something to sign".getBytes(StandardCharsets.UTF_8); | ||
| Signature signature = Signature.getInstance(JavaAlgorithm.getJavaAlgorithm(algorithm.name())); | ||
| signature.initSign(keyPair.getPrivate()); | ||
| signature.update(data); | ||
| byte[] sign = signature.sign(); | ||
| byte[] rsConcat = prov.getEcdsaCryptoProvider().asn1derToConcatenatedRS(sign, algorithm.getSignatureLength()); | ||
| byte[] asn1Des = prov.getEcdsaCryptoProvider().concatenatedRSToASN1DER(rsConcat, algorithm.getSignatureLength()); | ||
| byte[] rsConcat2 = prov.getEcdsaCryptoProvider().asn1derToConcatenatedRS(asn1Des, algorithm.getSignatureLength()); | ||
| Assert.assertArrayEquals(rsConcat, rsConcat2); |
There was a problem hiding this comment.
Generate a curve-matched keypair per algorithm.
These tests reuse one EC keypair for ES256, ES384, and ES512. That means at least two cases run with a mismatched curve size, so they mainly validate padding/truncation in the conversion code rather than real ES384/ES512 signatures.
Suggested fix
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
+import java.security.spec.ECGenParameterSpec;
...
- private final KeyPair keyPair;
-
- public ECDSAAlgorithmTest() throws Exception {
- keyPair = KeyPairGenerator.getInstance("EC").genKeyPair();
- }
-
-
private void test(ECDSAAlgorithm algorithm) throws Exception {
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
+ switch (algorithm) {
+ case ES256:
+ keyPairGenerator.initialize(new ECGenParameterSpec("secp256r1"));
+ break;
+ case ES384:
+ keyPairGenerator.initialize(new ECGenParameterSpec("secp384r1"));
+ break;
+ case ES512:
+ keyPairGenerator.initialize(new ECGenParameterSpec("secp521r1"));
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported algorithm " + algorithm);
+ }
+
+ KeyPair keyPair = keyPairGenerator.generateKeyPair();
AuthzClientCryptoProvider prov = new AuthzClientCryptoProvider();
byte[] data = "Something to sign".getBytes(StandardCharsets.UTF_8);
Signature signature = Signature.getInstance(JavaAlgorithm.getJavaAlgorithm(algorithm.name()));
signature.initSign(keyPair.getPrivate());🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@authz/client/src/test/java/org/keycloak/authorization/client/test/ECDSAAlgorithmTest.java`
around lines 39 - 56, The test currently reuses a single EC keyPair created in
the ECDSAAlgorithmTest constructor which mismatches curves for
ES256/ES384/ES512; change the code so test(ECDSAAlgorithm algorithm) generates a
new, curve-matched KeyPair instead of using the shared keyPair: inside test(...)
use KeyPairGenerator.getInstance("EC") and initialize it with an
ECGenParameterSpec chosen by algorithm (e.g., "secp256r1" for ES256, "secp384r1"
for ES384, "secp521r1" for ES512 / P-521) and then call genKeyPair(); use that
KeyPair for signature initSign and remove reliance on the class-level keyPair
field or stop using it in test(...).
| List<CryptoProvider> foundProviders = StreamSupport.stream(ServiceLoader.load(CryptoProvider.class, classLoader).spliterator(), false) | ||
| .sorted(Comparator.comparingInt(CryptoProvider::order).reversed()) | ||
| .collect(Collectors.toList()); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate repository files for inspection
ls -la
# Show CryptoIntegration around the referenced lines
echo "=== CryptoIntegration.java (lines 1-120) ==="
sed -n '1,140p' common/src/main/java/org/keycloak/common/crypto/CryptoIntegration.java | nl -ba | sed -n '1,140p'
# Grep for detectProvider / provider discovery in CryptoIntegration
echo "=== Search detectProvider in CryptoIntegration.java ==="
rg -n "detectProvider|ServiceLoader\\.load\\(CryptoProvider|CryptoProvider::order|foundProviders|spliterator\\(" common/src/main/java/org/keycloak/common/crypto/CryptoIntegration.java
# Read CryptoProvider interface to see how order is defined
echo "=== Find CryptoProvider interface ==="
fd -a "CryptoProvider.*" common crypto -t f 2>/dev/null | head -n 50
# If CryptoProvider is in another module, search globally for interface/class definition
rg -n "interface CryptoProvider|class CryptoProvider" -S . --hidden --glob '!**/target/**' | head -n 50
# Read the found CryptoProvider definition
echo "=== Show CryptoProvider definition ==="
# pick first match line and extract file and around it
first=$(rg -n "interface CryptoProvider|class CryptoProvider" -S . --hidden --glob '!**/target/**' | head -n 1 | cut -d: -f1)
echo "First match file: $first"
sed -n '1,200p' "$first" | nl -ba | sed -n '1,200p'
# Read DefaultCryptoProvider constructor
echo "=== DefaultCryptoProvider.java constructors ==="
default_file="crypto/default/src/main/java/org/keycloak/crypto/def/DefaultCryptoProvider.java"
if [ -f "$default_file" ]; then
rg -n "DefaultCryptoProvider\\(" "$default_file"
sed -n '1,200p' "$default_file" | nl -ba | sed -n '1,200p'
else
echo "Missing: $default_file"
fi
# Read FIPS1402Provider constructor
echo "=== FIPS1402Provider.java constructors ==="
fips_file="crypto/fips1402/src/main/java/org/keycloak/crypto/fips/FIPS1402Provider.java"
if [ -f "$fips_file" ]; then
rg -n "FIPS1402Provider\\(" "$fips_file"
sed -n '1,240p' "$fips_file" | nl -ba | sed -n '1,240p'
else
echo "Missing: $fips_file"
fiRepository: test-code-tool/keycloak__keycloak__CodeRabbit__PR33832__20260407
Length of output: 2050
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ls -la
echo "=== CryptoIntegration.java (full file up to 220 lines) ==="
sed -n '1,220p' common/src/main/java/org/keycloak/common/crypto/CryptoIntegration.java | nl -ba | sed -n '1,220p'
echo "=== Search for provider discovery usage ==="
rg -n "detectProvider|ServiceLoader\\.load\\(CryptoProvider|CryptoProvider::order|foundProviders|collect\\(Collectors\\.toList\\(\\)\\)|spliterator\\(" common/src/main/java/org/keycloak/common/crypto/CryptoIntegration.java
echo "=== Find CryptoProvider definition ==="
rg -n "interface CryptoProvider|class CryptoProvider" -S . --hidden --glob '!**/target/**' | head -n 20
# Determine file of first match
first=$(rg -n "interface CryptoProvider|class CryptoProvider" -S . --hidden --glob '!**/target/**' | head -n 1 | cut -d: -f1)
echo "First match file: $first"
sed -n '1,220p' "$first" | nl -ba | sed -n '1,220p'
default_file="crypto/default/src/main/java/org/keycloak/crypto/def/DefaultCryptoProvider.java"
if [ -f "$default_file" ]; then
echo "=== DefaultCryptoProvider.java around constructor ==="
rg -n "DefaultCryptoProvider\\(" "$default_file"
sed -n '1,240p' "$default_file" | nl -ba | sed -n '1,240p'
else
echo "Missing: $default_file"
fi
fips_file="crypto/fips1402/src/main/java/org/keycloak/crypto/fips/FIPS1402Provider.java"
if [ -f "$fips_file" ]; then
echo "=== FIPS1402Provider.java around constructor ==="
rg -n "FIPS1402Provider\\(" "$fips_file"
sed -n '1,320p' "$fips_file" | nl -ba | sed -n '1,320p'
else
echo "Missing: $fips_file"
fiRepository: test-code-tool/keycloak__keycloak__CodeRabbit__PR33832__20260407
Length of output: 2050
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ls -la
echo "=== CryptoIntegration.java (lines 1-220) ==="
sed -n '1,220p' common/src/main/java/org/keycloak/common/crypto/CryptoIntegration.java | nl -ba | sed -n '1,220p'
echo "=== Search for provider discovery usage ==="
rg -n "detectProvider|ServiceLoader\\.load\\(CryptoProvider|CryptoProvider::order|foundProviders|spliterator\\(" common/src/main/java/org/keycloak/common/crypto/CryptoIntegration.java
echo "=== Find CryptoProvider definition ==="
rg -n "interface CryptoProvider|class CryptoProvider" -S . --hidden --glob '!**/target/**' | head -n 20
first=$(rg -n "interface CryptoProvider|class CryptoProvider" -S . --hidden --glob '!**/target/**' | head -n 1 | cut -d: -f1)
echo "First match file: $first"
sed -n '1,220p' "$first" | nl -ba | sed -n '1,220p'
default_file="crypto/default/src/main/java/org/keycloak/crypto/def/DefaultCryptoProvider.java"
if [ -f "$default_file" ]; then
echo "=== DefaultCryptoProvider.java (lines 1-240) ==="
sed -n '1,240p' "$default_file" | nl -ba | sed -n '1,240p'
else
echo "Missing: $default_file"
fi
fips_file="crypto/fips1402/src/main/java/org/keycloak/crypto/fips/FIPS1402Provider.java"
if [ -f "$fips_file" ]; then
echo "=== FIPS1402Provider.java (lines 1-320) ==="
sed -n '1,320p' "$fips_file" | nl -ba | sed -n '1,320p'
else
echo "Missing: $fips_file"
fiRepository: test-code-tool/keycloak__keycloak__CodeRabbit__PR33832__20260407
Length of output: 2050
🌐 Web query:
DefaultCryptoProvider.java Security.addProvider constructor org.keycloak crypto def
💡 Result:
In Keycloak, the org.keycloak.crypto.def.DefaultCryptoProvider class is responsible for providing cryptographic implementations, including the BouncyCastle provider [1]. Within its constructor, the provider attempts to optimize performance and initialization by checking if an instance of the BouncyCastleProvider is already registered in the Java security environment [1]. Specifically, the DefaultCryptoProvider constructor performs the following check: 1. It attempts to retrieve the existing BouncyCastle provider using Security.getProvider(CryptoConstants.BC_PROVIDER_ID) [1]. 2. If an existing provider is found, it uses that instance to avoid redundant initialization, which is an expensive operation [1]. 3. If no existing provider is found, it instantiates a new BouncyCastleProvider [1]. This mechanism is designed to respect providers explicitly configured in the java.security file [1]. If you are working in an environment where you need to ensure the BouncyCastle provider is available for Keycloak to use, it is common practice to manually register it using Security.addProvider(new BouncyCastleProvider) before initializing Keycloak's cryptographic integrations [2][3]. This is particularly relevant in environments like unit tests or custom application integrations where the provider might not be registered automatically [2][3]. If you encounter errors related to the unavailability of cryptographic algorithms (such as ECDSA), ensuring the provider is correctly registered in the Java security environment is the standard resolution [3][4].
Citations:
- 1: https://github.com/keycloak/keycloak/blob/ce1331f550d2c1e00e17562f86e8cd583f841632/crypto/default/src/main/java/org/keycloak/crypto/def/DefaultCryptoProvider.java
- 2: https://blog.nybeyond.com/learning/keycloak-jwe-unit-test-setup/
- 3: https://stackoverflow.com/questions/70750555/bug-in-keycloak-package-ecdsa-keyfactory-not-available
- 4: https://forum.keycloak.org/t/spring-boot-and-openjdk-ecdsa-keyfactory-not-available/1849
🌐 Web query:
FIPS1402Provider.java Security.insertProviderAt Security.setProperty constructor org.keycloak crypto fips1402
💡 Result:
The FIPS1402Provider.java class is a core component within Keycloak's cryptographic architecture, specifically located in the org.keycloak.crypto.fips package [1]. It acts as a wrapper or integration point for BouncyCastle FIPS (BCFIPS) libraries, which Keycloak requires for FIPS 140-2 compliance [2][3]. Regarding the specific methods and mechanisms you mentioned: Security.insertProviderAt: This method is used within the Keycloak ecosystem to dynamically register security providers [4]. In the context of Keycloak's FIPS implementation, the system ensures the BCFIPS provider is correctly positioned within the Java Security provider list—often as the primary provider—to enforce FIPS-compliant cryptographic operations [5][6]. Security.setProperty: This method is used programmatically to override or configure default Java security properties when the environment requires it [7]. For example, developers and the Keycloak team have explored or implemented the use of Security.setProperty to ensure that specific algorithms (such as those for KeyManagerFactory or TrustManagerFactory) align with FIPS requirements, avoiding failures in scenarios where default JVM settings might point to non-FIPS-compliant providers or algorithms [5][7]. Constructor and Usage: The FIPS1402Provider class is instantiated to configure and initialize the necessary cryptographic environment [1][2]. It handles the abstraction of cryptographic providers, allowing Keycloak to switch between standard and FIPS-compliant modes based on the environment and provided configurations [8]. In practice, when Keycloak runs in FIPS mode, it does not ship the BouncyCastle FIPS libraries by default [2][3]. Users must provide these libraries, and Keycloak's internal mechanisms, centered around classes like FIPS1402Provider, detect and utilize them to ensure that all cryptographic tasks strictly adhere to approved algorithms and modes [2][3]. Workarounds, such as the KeycloakFipsSecurityProvider, are sometimes used to redirect non-FIPS-compliant calls (like those from legacy libraries like Apache Santuario) to BCFIPS-approved implementations [9].
Citations:
- 1: https://github.com/keycloak/keycloak/blob/main/crypto/fips1402/src/main/java/org/keycloak/crypto/fips/FIPS1402Provider.java
- 2: https://www.keycloak.org/server/fips
- 3: https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/26.4/html/server_configuration_guide/fips-
- 4: https://docs.oracle.com/en/java/java-components/jipher/10/jipher-user-guide/jipher-fips-140-compliance.html
- 5: Doublecheck if we need to override properties in java.security keycloak/keycloak#17110
- 6: Test Java 25 with FIPS enabled keycloak/keycloak#47581
- 7: Doublecheck if we need to override properties in java.security keycloak/keycloak#16702
- 8: https://www.keycloak.org/docs-api/latest/javadocs/org/keycloak/common/crypto/CryptoProvider.html
- 9: Skip adding xmlsec security provider. Adding KeycloakFipsSecurityProvider to workaround 'Security.getInstance("SHA1PRNG")' keycloak/keycloak#12786
🌐 Web query:
CryptoIntegration.java ServiceLoader CryptoProvider order reversed collect(Collectors.toList()) org.keycloak.common.crypto
💡 Result:
In Keycloak's org.keycloak.common.crypto.CryptoIntegration class, the ServiceLoader mechanism is used to discover available CryptoProvider implementations at runtime [1][2]. Because the order of providers returned by Java's ServiceLoader is not guaranteed [3], Keycloak implements a sorting mechanism to ensure deterministic selection [2]. Specifically, discovered CryptoProvider implementations are collected into a list, sorted by their order method in descending order, and then the provider with the highest order value is selected [4][2]. The code typically uses Java Streams and collectors to process these providers [5][1]. The implementation in CryptoIntegration.java ensures that the provider with the highest numerical priority—defined by its order method—takes precedence, while other providers found on the classpath are ignored and logged [5][2]. This pattern addresses the lack of built-in priority support in the standard ServiceLoader API [3][2].
Citations:
- 1: https://apidia.net/mvn/org.keycloak/keycloak/24.0.0/org.keycloak.common.crypto.CryptoIntegration.html
- 2: Add AuthzClientCryptoProvider for authorization client cryptographic operations AI-Code-Review-Evals/coderabbit-keycloak#3
- 3: https://bugs.openjdk.org/browse/JDK-7191104
- 4: https://www.keycloak.org/docs-api/latest/javadocs/org/keycloak/common/crypto/CryptoProvider.html
- 5: https://github.com/keycloak/keycloak-client/blob/3c74dac9/client-common-synced/src/main/java/org/keycloak/common/crypto/CryptoIntegration.java
🌐 Web query:
Java ServiceLoader provider instantiation happens when iterating/next() collect all providers instantiates all provider classes
💡 Result:
In Java, ServiceLoader provider instantiation is performed lazily, meaning it occurs on demand rather than all at once [1][2]. When you use the iterator method, it returns an iterator that performs the following steps: 1. It first yields any providers that have already been loaded and cached from previous iterations [1][3]. 2. It then lazily locates and instantiates any remaining providers one by one, as needed [1][4]. 3. Each newly instantiated provider is added to the internal provider cache [1][5]. Because this process is lazy, the iterator's hasNext and next methods perform the actual work of locating and instantiating the service provider classes [2][5]. Consequently, these methods can throw a ServiceConfigurationError if a provider cannot be found, instantiated, or is not assignable to the service type [1][2]. If you wish to identify or filter service providers without instantiating them, you should use the stream method instead, which allows for inspecting providers without triggering immediate instantiation [1][6].
Citations:
- 1: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/ServiceLoader.html
- 2: https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html
- 3: http://download.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html
- 4: https://docs.oracle.com/javase/10/docs/api/java/util/ServiceLoader.html
- 5: https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/ServiceLoader.html
- 6: https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/ServiceLoader.html
🌐 Web query:
site:github.com/keycloak/keycloak FIPS1402Provider Security.insertProviderAt
💡 Result:
The term FIPS1402Provider refers to a specific class within the Keycloak project (org.keycloak.crypto.fips.FIPS1402Provider) designed to facilitate compliance with FIPS 140-2 standards by integrating the Bouncy Castle FIPS (BCFIPS) security provider into the Java environment [1][2]. The method Security.insertProviderAt is a standard Java Security API used to programmatically register a security provider at a specific position in the list of available providers [3]. In the context of Keycloak's FIPS integration, developers and the framework use this (or similar mechanisms) to ensure that the FIPS-compliant Bouncy Castle provider is prioritized or correctly positioned within the Java Security provider list [3][4]. Key observations regarding its usage in Keycloak: 1. Provider Prioritization: Ensuring BCFIPS is correctly registered is critical for FIPS-enabled systems to ensure that cryptographic operations are performed by the compliant provider rather than default, non-compliant Java providers [4]. 2. Implementation Requirements: Integrating FIPS often requires adding not only the primary BCFIPS provider but also related providers (like BCJSSE for TLS) and managing potential conflicts with default providers [2][5]. 3. Workarounds: In some cases, specific "dummy" or wrapper providers (like KeycloakFipsSecurityProvider) are introduced to bridge compatibility gaps between external libraries (like Apache Santuario) and the strict requirements of a FIPS-compliant environment [6]. If you are encountering this in Keycloak source code or configuration, it is part of the infrastructure that intercepts and routes cryptographic service requests to Bouncy Castle FIPS implementations [1][7]. To use Security.insertProviderAt in custom Java code for this purpose, you would typically instantiate the provider and call: Security.insertProviderAt(new org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider, 1); Note that direct programmatic manipulation of security providers should be handled carefully to avoid breaking system-wide security, and in modern environments, this is often managed via the java.security configuration file rather than hard-coded registration [2][5].
Citations:
- 1: https://github.com/keycloak/keycloak/blob/main/crypto/fips1402/src/main/java/org/keycloak/crypto/fips/FIPS1402Provider.java
- 2: Initial integration tests with BCFIPS distribution keycloak/keycloak#14895
- 3: Refactor BouncyIntegration keycloak/keycloak#12244
- 4: Make sure BouncyCastle is the first provider to prevent issues on FIPS enabled systems keycloak/keycloak#9965
- 5: Doublecheck if we need to override properties in java.security keycloak/keycloak#17110
- 6: Skip adding xmlsec security provider. Adding KeycloakFipsSecurityProvider to workaround 'Security.getInstance("SHA1PRNG")' keycloak/keycloak#12786
- 7: Fixing UserFederationLdapConnectionTest,LDAPUserLoginTest to work with FIPS keycloak/keycloak#15299
🌐 Web query:
site:github.com/keycloak/keycloak FIPS1402Provider Security.setProperty
💡 Result:
FIPS1402Provider Security.setProperty (Java) In Keycloak’s FIPS integration, the class org.keycloak.crypto.fips.FIPS1402Provider is the place where FIPS-specific security provider setup and (in some setups) java.security property overrides are discussed. The strongest concrete, source-backed signal from the retrieved materials is that Keycloak’s FIPS setup relies on overriding the TLS KeyManagerFactory algorithm property ssl.KeyManagerFactory.algorithm (via java.security properties in their docs/tests; and they consider setting it programmatically via Security.setProperty inside FIPS1402Provider). Source-backed details 1) ssl.KeyManagerFactory.algorithm is explicitly called out as the non-default property that drives failures if not set - A Keycloak issue discussion notes that ssl.KeyManagerFactory.algorithm is “the only security property, which is not default value” in their OpenJDK 17 test context, and that commenting it causes multiple test failures, while keystore.type and ssl.TrustManagerFactory.algorithm remain at PKCS12 by default in both FIPS and non-FIPS environments [1]. - The same thread asks whether FIPS1402Provider should set this property programmatically using Security.setProperty("ssl.KeyManagerFactory.algorithm", "PKIX") and asks about side effects [1]. 2) FIPS1402Provider forces KeyManagerFactory/TrustManagerFactory implementations when inserting BCJSSE - A related Keycloak PR description states that “FIPS1402Provider now forces implementations for KeyManagerFactory and TrustManagerFactory if the default one is missing when inserting the BCJSSE provider” [2]. This is consistent with the need for specific algorithm/provider behaviors in TLS setup. 3) Keycloak’s FIPS mode can require additional java.security property entries beyond provider insertion - A Keycloak issue about “FIPS BouncyCastle Errors and Config” references adding a java.security property fips.provider.7=XMLDSig as an additional requirement in their environment [3]. What I can and cannot answer precisely from the evidence retrieved - I can confirm (from the cited issue text) the exact property key that Keycloak discusses setting (ssl.KeyManagerFactory.algorithm) and the candidate value (PKIX) that they considered setting via Security.setProperty in FIPS1402Provider [1]. - I cannot, in this run, quote the exact method call(s) within FIPS1402Provider.java itself (the specific Security.setProperty(...) lines and values), because the search results returned PR/issue discussions rather than the verbatim code lines from the class. Practical guidance If you’re trying to reproduce Keycloak’s behavior, the cited discussions indicate that you should ensure ssl.KeyManagerFactory.algorithm is set to PKIX in your FIPS mode security configuration (either via java.security property files used at startup, or—if you are modifying code—by setting it early in FIPS1402Provider initialization as discussed in [1]). If you want, tell me which project/runtime you mean (Keycloak version and where you saw Security.setProperty), and I can narrow the answer to the exact property/value(s) by locating the verbatim Security.setProperty calls in the corresponding FIPS1402Provider.java revision.
Citations:
- 1: Doublecheck if we need to override properties in java.security keycloak/keycloak#16702
- 2: Doublecheck if we need to override properties in java.security keycloak/keycloak#17110
- 3: FIPS BouncyCastle Errors and Config keycloak/keycloak#17003
🌐 Web query:
site:github.com/keycloak/keycloak DefaultCryptoProvider Security.addProvider
💡 Result:
DefaultCryptoProvider is not a standard Java class; it is a specific implementation component within the Keycloak identity and access management project [1][2]. It implements the Keycloak-specific org.keycloak.common.crypto.CryptoProvider interface [1]. Keycloak uses this class to manage cryptographic operations, such as algorithm provider initialization (e.g., for AES, RSA, and ECDSA) [1]. Its primary purpose is to encapsulate and manage the Bouncy Castle provider (BouncyCastleProvider) while allowing for flexibility—such as supporting FIPS-compliant environments by swapping out the underlying provider implementation [1][3][4]. In Keycloak's architecture, developers generally do not manually call Security.addProvider with DefaultCryptoProvider. Instead: 1. Keycloak's CryptoIntegration class automatically initializes the appropriate CryptoProvider based on the environment configuration [4][5]. 2. DefaultCryptoProvider itself internally checks if a Bouncy Castle provider is already registered in the Java security environment (using Security.getProvider) and uses it if available to avoid redundant or expensive re-initialization [1]. 3. For custom applications needing Bouncy Castle, standard Java practice is to either add it to the java.security file (via $JAVA_HOME/jre/lib/security/java.security) or use Security.addProvider(new BouncyCastleProvider) [6]. If you are encountering issues related to DefaultCryptoProvider (often seen in Keycloak version upgrades), it is typically a configuration or dependency conflict where the application fails to instantiate the provider due to missing libraries or classpath issues [2][7]. Keycloak's internal architecture now manages these providers to facilitate switching between standard Bouncy Castle and FIPS-compliant versions, and manual overrides are discouraged [4][8].
Citations:
- 1: https://github.com/keycloak/keycloak/blob/ce1331f550d2c1e00e17562f86e8cd583f841632/crypto/default/src/main/java/org/keycloak/crypto/def/DefaultCryptoProvider.java
- 2: After upgrade from 20.0.3 to 22.0.1 Keycloak java client fail on DefaultCryptoProvider keycloak/keycloak#22173
- 3: Introduce module "crypto/default" and move DefaultCryptoProvider into that keycloak/keycloak#12625
- 4: Introduction of CryptoProvider and initial support for build keycloak quarkus distribution with FIPS keycloak/keycloak#12522
- 5: Simplify usage of Keycloak API due to CryptoIntegration keycloak/keycloak#27916
- 6: KEYCLOAK-12624 ECDSA KeyFactory not available keycloak/keycloak#6643
- 7: EAP Adapter tests failing in Test Pipelines with ClassCastException in BCProvider keycloak/keycloak#14210
- 8: Refactor BouncyIntegration keycloak/keycloak#12244
🌐 Web query:
site:github.com/keycloak/keycloak "class FIPS1402Provider" "insertProviderAt("
💡 Result:
The class FIPS1402Provider is part of the Keycloak identity and access management system [1][2]. It is used to integrate the Bouncy Castle FIPS (BCFIPS) library into the Keycloak server, ensuring that cryptographic operations comply with FIPS 140-2 standards [3][4]. The method call insertProviderAt(this.bcFipsProvider, 1) is used within this class to programmatically register the Bouncy Castle FIPS security provider with the Java Security API [1]. By specifying the position as 1, Keycloak ensures that the BCFIPS provider is placed at the top of the list of configured security providers [5][6]. This precedence is critical to guarantee that the application uses the FIPS-compliant cryptographic implementations provided by Bouncy Castle instead of the default, potentially non-compliant, providers available in the underlying Java environment [6][7]. Keycloak uses this class to manage FIPS-compliant security providers, including both the BCFIPS provider for general cryptography and the BCJSSE provider for TLS, particularly when the server is operating in FIPS-enabled environments [3][8].
Citations:
- 1: https://github.com/keycloak/keycloak/blob/main/crypto/fips1402/src/main/java/org/keycloak/crypto/fips/FIPS1402Provider.java
- 2: https://github.com/keycloak/keycloak/blob/main/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FipsDistTest.java
- 3: Initial integration tests with BCFIPS distribution keycloak/keycloak#14895
- 4: Fixing UserFederationLdapConnectionTest,LDAPUserLoginTest to work with FIPS keycloak/keycloak#15299
- 5: Refactor BouncyIntegration keycloak/keycloak#12244
- 6: Make sure BouncyCastle is the first provider to prevent issues on FIPS enabled systems keycloak/keycloak#9965
- 7: Make Keycloak FIPS working with OpenJDK 17 on FIPS enabled RHEL keycloak/keycloak#16082
- 8: Doublecheck if we need to override properties in java.security keycloak/keycloak#17110
🌐 Web query:
site:github.com/keycloak/keycloak "FIPS1402Provider" "setProperty("
💡 Result:
The term FIPS1402Provider refers to a class within the Keycloak identity and access management system, specifically located in the keycloak-crypto-fips1402 module [1]. This class is responsible for managing FIPS 140-2 compliance and security provider configuration within the Keycloak runtime [1][2]. The method call FIPS1402Provider.setProperty(String key, String value) is used to programmatically configure Java security properties at runtime [3]. In the context of Keycloak's FIPS integration, this is often utilized to override or set specific security settings—such as algorithms for KeyManagerFactory or TrustManagerFactory—that are necessary for the Bouncy Castle FIPS (BCFIPS) security providers to function correctly alongside Java's default security environment [4][3]. For example, the code may invoke Security.setProperty (or its own wrapper) to ensure that algorithms like SunX509 or PKIX are explicitly set to satisfy the requirements of the FIPS-validated environment, especially when standard Java defaults might be incompatible or insufficient [4][3]. This programmatic approach allows Keycloak to dynamically adjust security parameters during its initialization without requiring users to manually modify the system-wide java.security configuration file in every environment [4][2].
Citations:
- 1: https://github.com/keycloak/keycloak/blob/main/crypto/fips1402/src/main/java/org/keycloak/crypto/fips/FIPS1402Provider.java
- 2: Initial integration tests with BCFIPS distribution keycloak/keycloak#14895
- 3: Doublecheck if we need to override properties in java.security keycloak/keycloak#16702
- 4: Doublecheck if we need to override properties in java.security keycloak/keycloak#17110
🌐 Web query:
site:github.com/keycloak/keycloak/ "DefaultCryptoProvider" "addProvider("
💡 Result:
The term DefaultCryptoProvider refers to a core component within the Keycloak identity and access management system, specifically responsible for providing cryptographic services [1]. It implements the CryptoProvider interface [1] and serves as the default implementation used by Keycloak to handle tasks such as encryption, signing, and certificate utilities [1]. Keycloak's architecture utilizes a CryptoIntegration class to manage and initialize these providers [2]. The DefaultCryptoProvider is part of the crypto/default module [3], which provides support for standard cryptographic operations by leveraging the BouncyCastle library [1]. Regarding your query about "addProvider(", this method is not typically a direct member of DefaultCryptoProvider. Instead, DefaultCryptoProvider internally manages a map of algorithm providers and interacts with the Java Security environment [1]. Specifically, its constructor checks the existing Java security providers to see if BouncyCastle is already registered (using Security.getProvider) [1]. If it is not, or as part of its initialization, it ensures that the necessary cryptographic providers are available for Keycloak's operations [1]. In modern Keycloak versions, the initialization of these providers is handled automatically via the CryptoIntegration system [2]. Users generally do not need to manually add or register providers; instead, Keycloak detects and sets up the appropriate implementation (default or FIPS-compliant) based on the environment [3][4]. If you encounter errors related to DefaultCryptoProvider, they are often related to classpath issues, missing dependencies, or improper initialization order of the CryptoIntegration system [5][6][2].
Citations:
- 1: https://github.com/keycloak/keycloak/blob/ce1331f550d2c1e00e17562f86e8cd583f841632/crypto/default/src/main/java/org/keycloak/crypto/def/DefaultCryptoProvider.java
- 2: Simplify usage of Keycloak API due to CryptoIntegration keycloak/keycloak#27916
- 3: Introduce module "crypto/default" and move DefaultCryptoProvider into that keycloak/keycloak#12625
- 4: Introduction of CryptoProvider and initial support for build keycloak quarkus distribution with FIPS keycloak/keycloak#12522
- 5: After upgrade from 20.0.3 to 22.0.1 Keycloak java client fail on DefaultCryptoProvider keycloak/keycloak#22173
- 6: EAP Adapter tests failing in Test Pipelines with ClassCastException in BCProvider keycloak/keycloak#14210
Avoid instantiating all CryptoProviders before selecting by order()
In common/src/main/java/org/keycloak/common/crypto/CryptoIntegration.java:56-58, collecting the sorted ServiceLoader results (collect(Collectors.toList())) forces instantiation of every discovered CryptoProvider before the highest-order one is chosen. Those constructors perform JVM-global security changes (e.g., DefaultCryptoProvider registers BouncyCastle via Security.addProvider(...), and FIPS1402Provider inserts the FIPS provider via Security.insertProviderAt(...) and adjusts security properties via Security.setProperty(...)), so even “ignored” providers can still mutate global crypto state.
Move JVM-global Security mutations out of provider constructors and execute them only after the winning provider is selected (or make provider priority discoverable without instantiation).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@common/src/main/java/org/keycloak/common/crypto/CryptoIntegration.java`
around lines 56 - 58, The code eagerly instantiates every CryptoProvider when
building foundProviders in CryptoIntegration, causing JVM-global Security
side-effects from provider constructors; instead avoid instantiation until the
highest-order provider is chosen by either (A) using the ServiceLoader provider
metadata API (ServiceLoader.Provider) to compare order/priority without calling
get(), or (B) refactoring providers so order/priority is discoverable without
construction (e.g., a class-level annotation or static method) and then only
instantiating the winner. Alternatively (or additionally) move any JVM-wide
Security mutations out of provider constructors in classes like
DefaultCryptoProvider and FIPS1402Provider into an explicit method (e.g.,
applySecurityProvider() or initializeSecurity()) that CryptoIntegration calls
only after selecting the winning CryptoProvider.
…tory
closes #33831
This is the Keycloak main part for solving the keycloak/keycloak#32962 and adding the related changes in the authz-client on the Keycloak side, which can then be later synced to the keycloak-client repository.
In the end, I am using approach (2) from keycloak/keycloak#32962 with adding the method
order()toCryptoProvider, which allows to have multiple instances ofCryptoProvideron classpath (Keycloak testsuite still uses theDefaultCryptoProvideras before this PR).Summary by CodeRabbit
New Features
Tests
Chores