Skip to content

Add AuthzClientCryptoProvider for authorization client cryptographic operations#8

Open
ShashankFC wants to merge 1 commit into
feature-authz-crypto-baselinefrom
feature-authz-crypto-implementation
Open

Add AuthzClientCryptoProvider for authorization client cryptographic operations#8
ShashankFC wants to merge 1 commit into
feature-authz-crypto-baselinefrom
feature-authz-crypto-implementation

Conversation

@ShashankFC

@ShashankFC ShashankFC commented Feb 13, 2026

Copy link
Copy Markdown

User description

Test 3

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced cryptographic support with ECDSA algorithm implementation for authorization client operations.
    • Improved crypto provider selection with priority ordering mechanism.
  • Tests

    • Added test coverage for ECDSA algorithm variants (ES256, ES384, ES512).
  • Chores

    • Added testing framework dependencies.

✏️ Tip: You can customize this high-level summary in your review settings.


Replicated from ai-code-review-evaluation/keycloak-coderabbit#3


CodeAnt-AI Description

Add ECDSA support and selectable crypto provider to authz-client

What Changed

  • Authz client now initializes the crypto subsystem when created, enabling client-side cryptographic operations.
  • A lightweight crypto provider is bundled and auto-registered for the authz-client; it implements ECDSA signature conversions between ASN.1/DER and the raw R||S format used by ES256/ES384/ES512.
  • Service discovery for crypto providers now prefers providers with a higher explicit order and logs which additional providers were ignored, making provider selection predictable when multiple providers are present.
  • ASN.1 encoder/decoder utilities were added to support the ECDSA conversions, and unit tests verify ES256/ES384/ES512 conversion correctness.
  • Test dependencies were added to the authz-client module and a new test validates round-trip conversion of ECDSA signatures.

Impact

✅ Authz client can handle ES256/ES384/ES512 signatures
✅ Deterministic crypto provider selection when multiple providers exist
✅ ECDSA signature conversion validated by unit tests

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

…tory

closes #33831

Signed-off-by: mposolda <mposolda@gmail.com>
@ShashankFC

Copy link
Copy Markdown
Author

@CodeAnt-AI: review

@codeant-ai

codeant-ai Bot commented Feb 13, 2026

Copy link
Copy Markdown

CodeAnt AI is running the review.

@codeant-ai codeant-ai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Feb 13, 2026
@codeant-ai

codeant-ai Bot commented Feb 13, 2026

Copy link
Copy Markdown

Sequence Diagram

Shows the new initialization path where AuthzClient ensures CryptoIntegration is initialized (via ServiceLoader), the highest-order CryptoProvider is selected (AuthzClientCryptoProvider is registered), and the client obtains the ECDSA helper for signature conversions used by the authz client tests.

sequenceDiagram
    participant TestClient
    participant AuthzClient
    participant CryptoIntegration
    participant ServiceLoader
    participant AuthzClientCryptoProvider

    TestClient->>AuthzClient: create(configuration)
    AuthzClient->>CryptoIntegration: init(classLoader)
    CryptoIntegration->>ServiceLoader: load CryptoProvider implementations
    ServiceLoader-->>CryptoIntegration: returns providers (with order)
    CryptoIntegration->>AuthzClientCryptoProvider: select highest-order provider and set as active
    AuthzClient-->>AuthzClientCryptoProvider: getEcdsaCryptoProvider() (used for ASN.1 <-> R|S conversions)
    AuthzClientCryptoProvider-->>AuthzClient: ECDSA helper returned
Loading

Generated by CodeAnt AI


@Override
public int order() {
return 100;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: This lightweight crypto provider is registered as a CryptoProvider service and returns a high priority (order() = 100), but most of its methods throw UnsupportedOperationException; in an environment where both this client JAR and the full server crypto provider are on the classpath, CryptoIntegration.detectProvider() will sort by order() and may incorrectly select this incomplete provider, leading to runtime UnsupportedOperationExceptions in server-side cryptographic operations that expect a full implementation. [logic error]

Severity Level: Critical 🚨
- ❌ CryptoIntegration may select incomplete AuthzClientCryptoProvider implementation.
- ❌ Server cryptographic operations fail with UnsupportedOperationException at runtime.
- ⚠️ Affects apps including authz-client jar on server classpath.
Suggested change
return 100;
// Use a very low priority so this lightweight client provider is only chosen
// when no other CryptoProvider implementations are available on the classpath.
return Integer.MIN_VALUE;
Steps of Reproduction ✅
1. Package an application (for example, a Keycloak-based server or tool) whose classloader
includes both the `authz/client` module and at least one server crypto module implementing
`CryptoProvider` (e.g. `org.keycloak.crypto.def.DefaultCryptoProvider` in
`crypto/default/src/main/java/org/keycloak/crypto/def/DefaultCryptoProvider.java`)
together with their `META-INF/services/org.keycloak.common.crypto.CryptoProvider` entries;
note that
`authz/client/src/main/resources/META-INF/services/org.keycloak.common.crypto.CryptoProvider`
already registers
`org.keycloak.authorization.client.util.crypto.AuthzClientCryptoProvider` via
ServiceLoader (seen in Grep output).

2. At application startup, call `CryptoIntegration.init(classLoader)` as intended by
`common/src/main/java/org/keycloak/common/crypto/CryptoIntegration.java:29-44`, passing
the application classloader that sees both providers; inside `init`,
`detectProvider(classLoader)` is invoked (`CryptoIntegration.java:55`).

3. In `detectProvider`, ServiceLoader discovers all `CryptoProvider` implementations on
the classpath and sorts them by
`Comparator.comparingInt(CryptoProvider::order).reversed()`
(`CryptoIntegration.java:55-58`); `AuthzClientCryptoProvider.order()` returns `100` as
defined in
`authz/client/src/main/java/org/keycloak/authorization/client/util/crypto/AuthzClientCryptoProvider.java:69-72`,
so if other providers use a lower `order()` value this lightweight provider becomes the
first in `foundProviders` and is set as the global provider
(`CryptoIntegration.java:63-71`).

4. Later, when server-side code calls `CryptoIntegration.getProvider()`
(`CryptoIntegration.java:46-51`) and then uses methods such as `getPemUtils()`,
`getCertificateUtils()`, `getSignature(...)`, or `wrapFactoryForTruststore(...)`, it now
receives the `AuthzClientCryptoProvider` instance whose implementations for these methods
simply throw `UnsupportedOperationException` (`AuthzClientCryptoProvider.java:75-97`,
183-220, 222-225), causing runtime failures in cryptographic operations that expect a full
provider implementation; this failure mode is a direct consequence of the high `order()`
value on an intentionally incomplete client-side provider.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** authz/client/src/main/java/org/keycloak/authorization/client/util/crypto/AuthzClientCryptoProvider.java
**Line:** 71:71
**Comment:**
	*Logic Error: This lightweight crypto provider is registered as a `CryptoProvider` service and returns a high priority (`order()` = 100), but most of its methods throw `UnsupportedOperationException`; in an environment where both this client JAR and the full server crypto provider are on the classpath, `CryptoIntegration.detectProvider()` will sort by `order()` and may incorrectly select this incomplete provider, leading to runtime `UnsupportedOperationException`s in server-side cryptographic operations that expect a full implementation.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

*
* The higher number has preference over the lower number
*/
int order();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Adding a new abstract method to a public interface is a binary-incompatible change: any existing third-party CryptoProvider implementations compiled against the previous version will not have an order() method, so when CryptoIntegration.detectProvider calls CryptoProvider::order they will trigger an AbstractMethodError at runtime; making order() a default method with a sensible default value preserves compatibility while still allowing newer providers to override it for prioritization. [type error]

Severity Level: Critical 🚨
- ❌ Keycloak fails startup when custom CryptoProvider SPI present.
- ⚠️ CryptoIntegration auto-detection unusable with third-party crypto providers.
Suggested change
int order();
default int order() {
return 0;
}
Steps of Reproduction ✅
1. Implement a custom crypto provider in an external module as `public class
CustomCryptoProvider implements org.keycloak.common.crypto.CryptoProvider` compiled
against a Keycloak version where `CryptoProvider` (file
`common/src/main/java/org/keycloak/common/crypto/CryptoProvider.java`) does not yet
declare `int order()`.

2. Register this implementation for ServiceLoader by adding
`org.keycloak.common.crypto.CryptoProvider` with the FQCN of `CustomCryptoProvider` to
`META-INF/services` in that external JAR and put the JAR on the Keycloak server classpath.

3. Upgrade Keycloak to the PR version where `CryptoProvider` declares the new abstract
method `int order()` (line 44 in `CryptoProvider.java`) and start the server, which calls
`CryptoIntegration.init(KeycloakApplication.class.getClassLoader())` from
`services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java:89`.

4. During `CryptoIntegration.init`, `detectProvider` in
`common/src/main/java/org/keycloak/common/crypto/CryptoIntegration.java:55-58` executes
`ServiceLoader.load(CryptoProvider.class, classLoader)` and sorts the loaded instances
with `Comparator.comparingInt(CryptoProvider::order).reversed()`, which invokes `order()`
on `CustomCryptoProvider` that has no such method, causing a
`java.lang.AbstractMethodError` and preventing server startup.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** common/src/main/java/org/keycloak/common/crypto/CryptoProvider.java
**Line:** 44:44
**Comment:**
	*Type Error: Adding a new abstract method to a public interface is a binary-incompatible change: any existing third-party `CryptoProvider` implementations compiled against the previous version will not have an `order()` method, so when `CryptoIntegration.detectProvider` calls `CryptoProvider::order` they will trigger an `AbstractMethodError` at runtime; making `order()` a default method with a sensible default value preserves compatibility while still allowing newer providers to override it for prioritization.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

@codeant-ai

codeant-ai Bot commented Feb 13, 2026

Copy link
Copy Markdown

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Breaking change
    A new abstract method order() was added to the public CryptoProvider interface. This is a source/binary incompatible change that will break any existing implementations that were not updated to implement this method.

  • Compatibility risk
    A new getAesGcmCipher() method was added to the interface (non-default). Like order(), this can break consumers who implement CryptoProvider but haven't been updated.

  • Provider selection ambiguity
    Providers are now sorted by order() and the highest is chosen. If multiple providers share the same top order value the selection may be ambiguous — consider explicit tie-handling or logging. The debug logging of ignored providers builds a trailing-comma string via manual concatenation.

  • Provider Order Collision
    This PR adds an order() implementation returning a hard-coded value (200). Multiple providers (Default and FIPS) now return the same order which may cause priority collisions when selecting crypto providers. Verify this priority aligns with the intended provider selection strategy and that there are no unintended ties.

  • Provider selection
    getBouncyCastleProvider() returns the Provider from a KeyStore created with KeyStore.getDefaultType(). This may not be the BouncyCastle provider and is surprising given the method name. Validate the intended provider selection and consider returning the actual BouncyCastle provider (or renaming the method) so callers get the expected provider behavior.

@codeant-ai

codeant-ai Bot commented Feb 13, 2026

Copy link
Copy Markdown

CodeAnt AI finished running the review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants