Skip to content

Conversation

@akshayutture-augment
Copy link
Contributor

@akshayutture-augment
Copy link
Contributor Author

@claude review this PR

1 similar comment
@akshayutture-augment
Copy link
Contributor Author

@claude review this PR

@claude
Copy link

claude bot commented Nov 20, 2025

Claude finished @akshayutture-augment's task —— View job


PR Review Complete

I've completed a thorough review of the PR "Add AuthzClientCryptoProvider for authorization client cryptographic operations". This implementation adds cryptographic provider support to the authz-client module, particularly for ECDSA signature handling. Below is my detailed feedback.


Summary

This PR introduces a minimal crypto provider implementation for the authorization client, focusing on ECDSA signature format conversion between concatenated R+S format and ASN.1 DER encoding. The implementation includes custom ASN.1 encoder/decoder utilities and integrates with Keycloak's existing crypto provider infrastructure.

Overall Assessment: The implementation is functional but has several areas that need attention regarding security, code quality, and completeness.


Critical Issues

1. Security: Integer to Bytes Conversion Logic Flaw

File: authz/client/src/main/java/org/keycloak/authorization/client/util/crypto/AuthzClientCryptoProvider.java:151-163

The integerToBytes method has a potential security vulnerability:

if (qLength < bytes.length) {
    byte[] tmp = new byte[qLength];
    System.arraycopy(bytes, bytes.length - tmp.length, tmp, 0, tmp.length);
    return tmp;
}

Issue: When truncating, this takes the last qLength bytes. For BigInteger's two's complement representation, this could truncate significant bits from the beginning, corrupting the signature component.

Impact: This could lead to invalid signatures that fail verification, or worse, truncated values that change the cryptographic meaning.

Recommendation: Add validation to ensure bytes are only being trimmed of leading zero padding bytes, not significant data:

if (qLength < bytes.length) {
    // Only allow truncation if we're removing leading zeros (sign extension)
    for (int i = 0; i < bytes.length - qLength; i++) {
        if (bytes[i] != 0) {
            throw new IOException("Cannot truncate significant bytes from BigInteger");
        }
    }
    byte[] tmp = new byte[qLength];
    System.arraycopy(bytes, bytes.length - tmp.length, tmp, 0, tmp.length);
    return tmp;
}

2. Memory Inefficiency: Redundant ASN1Encoder Instances

File: authz/client/src/main/java/org/keycloak/authorization/client/util/crypto/AuthzClientCryptoProvider.java:114-121

ASN1Encoder.create().write(rBigInteger);  // Created and discarded
ASN1Encoder.create().write(sBigInteger);  // Created and discarded

return ASN1Encoder.create()  // Only this one is actually used
    .writeDerSeq(...)
    .toByteArray();

Issue: Two encoder instances are created and immediately discarded without their results being used.

Recommendation: Remove the unused lines 114-115.

3. Provider Priority May Cause Conflicts

File: authz/client/src/main/java/org/keycloak/authorization/client/util/crypto/AuthzClientCryptoProvider.java:70-72

@Override
public int order() {
    return 100;
}

Issue: The AuthzClientCryptoProvider has order 100, which is lower than DefaultCryptoProvider, WildFlyElytronProvider, and FIPS1402Provider (all at 200). However, the ServiceLoader mechanism selects providers by highest order (line 57 of CryptoIntegration.java: sorted(Comparator.comparingInt(CryptoProvider::order).reversed())).

Impact: This provider will never be selected when other providers are available. If this is intentional (authz-client should only use this when it's the only provider), the documentation should clarify this.

Recommendation: Either:

  • Increase order to 250+ if it should take precedence for authz-client
  • Add clear documentation explaining why order is 100 and when this provider is expected to be used

Major Issues

4. Incomplete Implementation with Silent Failures

File: authz/client/src/main/java/org/keycloak/authorization/client/util/crypto/AuthzClientCryptoProvider.java

19 out of 23 interface methods throw UnsupportedOperationException. While this might be intentional for a minimal implementation:

Issues:

  • No documentation explains which operations are supported
  • Calling code may fail at runtime with cryptic errors
  • The getBouncyCastleProvider() method (lines 61-67) returns the default keystore provider, which is misleading - it's not actually BouncyCastle

Recommendations:

  1. Add comprehensive JavaDoc explaining this is a minimal provider for ECDSA signature format conversion only
  2. Consider throwing more descriptive exceptions with guidance:
    throw new UnsupportedOperationException(
        "AuthzClientCryptoProvider only supports ECDSA signature operations. " +
        "This operation requires a full crypto provider."
    );
  3. Fix getBouncyCastleProvider() name or behavior - it's confusing

5. ASN.1 Decoder: Incomplete Sequence Length Validation

File: authz/client/src/main/java/org/keycloak/authorization/client/util/crypto/ASN1Decoder.java:49-63

while (length > 0) {
    byte[] bytes = readNext();
    result.add(bytes);
    length = length - bytes.length;
}

Issue: This loop assumes readNext() will consume exactly length bytes, but there's no validation that:

  • The total consumed matches the declared length
  • The length doesn't go negative (though this is unlikely)

Recommendation: Add validation after the loop:

if (length != 0) {
    throw new IOException("Sequence length mismatch");
}

6. Missing Null Safety Checks

File: authz/client/src/main/java/org/keycloak/authorization/client/util/crypto/AuthzClientCryptoProvider.java:103-144

The concatenatedRSToASN1DER and asn1derToConcatenatedRS methods don't validate input parameters:

  • signature could be null
  • derEncodedSignatureValue could be null
  • signLength could be invalid (negative, odd number, etc.)

Recommendation: Add parameter validation at method entry.


Moderate Issues

7. Test Coverage Insufficient

File: authz/client/src/test/java/org/keycloak/authorization/client/test/ECDSAAlgorithmTest.java

The test validates round-trip conversion but doesn't test:

  • Edge cases (minimum/maximum values for r and s)
  • Invalid input handling
  • Null safety
  • Invalid ASN.1 DER format handling
  • Signature values with leading zeros
  • Different key sizes beyond the defaults

Recommendation: Add negative test cases and edge case validation.

8. ASN.1 Implementation: Limited Scope

Files: ASN1Encoder.java and ASN1Decoder.java

The implementations only support INTEGER and SEQUENCE types. While this may be sufficient for ECDSA signatures:

Issues:

  • No documentation indicating this is intentionally minimal
  • Tag constants are defined but not all are used
  • Could be confusing if someone tries to extend this for other ASN.1 structures

Recommendation: Add clear documentation stating this is specifically for ECDSA signature encoding/decoding and should not be used as a general-purpose ASN.1 library.

9. Package Visibility Inconsistency

Files: ASN1Encoder.java and ASN1Decoder.java

Both classes are package-private (no explicit modifier), which is appropriate, but:

  • ASN1Encoder has public static constants (INTEGER, SEQUENCE, CONSTRUCTED) that can't be accessed outside the package
  • Some methods are public when they don't need to be

Recommendation: Make all internal methods package-private or private as appropriate.


Minor Issues / Code Quality

10. Misleading Method Name in ASN1Decoder

File: authz/client/src/main/java/org/keycloak/authorization/client/util/crypto/ASN1Decoder.java:195-201

int read() {
    int tmp = is.read();
    if (tmp >= 0) {
        count++;
    }
    return tmp;
}

Issue: This method has side effects (updates count) which is not obvious from the name.

Recommendation: Rename to readAndCount() or add JavaDoc explaining the side effect.

11. Magic Number: 0x1f

File: authz/client/src/main/java/org/keycloak/authorization/client/util/crypto/ASN1Decoder.java:94-99

int tagNo = tag & 0x1f;
if (tagNo == 0x1f) {

Recommendation: Define as named constant with explanation:

private static final int TAG_NUMBER_MASK = 0x1f;  // ASN.1 tag number is in lower 5 bits
private static final int LONG_FORM_TAG = 0x1f;    // Indicates multi-byte tag

12. Inconsistent Exception Handling

File: authz/client/src/main/java/org/keycloak/authorization/client/util/crypto/AuthzClientCryptoProvider.java:61-67

@Override
public Provider getBouncyCastleProvider() {
    try {
        return KeyStore.getInstance(KeyStore.getDefaultType()).getProvider();
    } catch (KeyStoreException e) {
        throw new IllegalStateException(e);
    }
}

Issue: Wraps checked exception in IllegalStateException, but most other methods declare they throw checked exceptions.

Recommendation: Consider whether this should return null, throw the KeyStoreException, or be documented as to why IllegalStateException is appropriate here.

13. Missing Copyright Consistency

File: authz/client/src/main/resources/META-INF/services/org.keycloak.common.crypto.CryptoProvider

The service file has a copyright header (good!), but service files typically don't include them. This is minor but inconsistent with Java conventions.


Integration Concerns

14. CryptoIntegration Initialization in AuthzClient

File: authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java:95

CryptoIntegration.init(AuthzClient.class.getClassLoader());

Question: What happens if CryptoIntegration is already initialized with a different provider? The init method checks if cryptoProvider is null (line 30 of CryptoIntegration.java), so subsequent calls are no-ops.

Potential Issue: If Keycloak server components initialize CryptoIntegration first with a different provider, the authz-client will use that provider instead of AuthzClientCryptoProvider.

Recommendation: Document the expected initialization order and behavior, or consider whether authz-client needs guaranteed isolation.

15. No FIPS Compliance Verification

Since this PR modifies FIPS1402Provider and the authz-client will be used in FIPS environments:

Question: Has the ASN.1 encoder/decoder implementation been validated for FIPS compliance? Custom cryptographic code in FIPS environments typically needs certification.

Recommendation: Add documentation or tests confirming FIPS compliance, or document if FIPS environments should use a different code path.


Documentation Needs

16. Missing High-Level Documentation

There's no README, design doc, or package-info.java explaining:

  • Why a separate crypto provider was needed for authz-client
  • What problem this solves
  • When this provider is used vs. others
  • Design decisions and trade-offs

Recommendation: Add package-level documentation or update authz-client documentation to explain this implementation.


Positive Aspects

Good: Proper copyright headers on all files
Good: Service provider interface correctly configured
Good: Clean separation of concerns (encoder/decoder as separate classes)
Good: ASN.1 implementation follows X.690 spec comments
Good: Integration point in AuthzClient.create() is appropriate
Good: Test coverage for happy path scenarios
Good: No external dependencies introduced (pure Java implementation)


Recommendations Summary

Must Fix (Security/Correctness):

  1. Fix integer-to-bytes conversion logic (potential signature corruption)
  2. Remove redundant ASN1Encoder instantiations
  3. Add input validation for null checks and parameter bounds
  4. Fix or clarify provider order priority

Should Fix (Code Quality):
5. Add comprehensive documentation explaining limited implementation scope
6. Improve error messages for UnsupportedOperationException cases
7. Add validation in ASN.1 decoder sequence length handling
8. Expand test coverage with negative cases

Consider:
9. Document FIPS compliance status
10. Clarify initialization behavior with other providers
11. Add high-level design documentation


Conclusion

The implementation provides necessary ECDSA signature format conversion functionality but needs attention to security issues (particularly the integer truncation logic), documentation, and error handling. The core approach is sound, but the execution needs refinement before production use.

Recommendation: Request changes to address the critical security issue before merging.


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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants