apereo/cas · error · IllegalArgumentException
Proof JWT algorithm does not match EC holder key
Error message
Proof JWT algorithm does not match EC holder key
What it means
Thrown by OidcVerifiableCredentialJwtProofValidator.verifyAlgorithm when the holder JWK is an ECKey but the proof JWT's alg header is not in the JWSAlgorithm.EC family (e.g. RS256 used with an EC holder key). The proof algorithm must match the EC holder key type.
Solutions
- Sign the proof with an EC-family algorithm (ES256/ES384/ES512) when the holder key is EC.
- Choose the signer from the holder key type dynamically rather than hardcoding the algorithm.
- Verify that the kid/JWK used for verification matches the key actually used for signing.
Example fix
// before signed.sign(new RSASSASigner(rsaKeyPair.getPrivate())); // RSA alg, EC holder key // after signed.sign(new ECDSASigner(ecKeyPair.getPrivate()));
Defensive patterns
Strategy: validation
Validate before calling
if (holderJwk instanceof ECKey && !JWSAlgorithm.Family.EC.contains(alg)) {
throw new IllegalArgumentException("Use an EC-family algorithm for EC holder keys");
} Prevention
- Derive the signing algorithm from the holder key type.
- Confirm kid resolution returns the JWK matching the actual signing key.
When it happens
Trigger: Proof JWT signed with RS256/PS256 while the bound holder JWK is an EC P-256 key; client code hardcoding RSA algorithms; kid lookup returning an EC JWK while the client signs with an RSA key.
Common situations: Proof-builder defaults to RS256 but the credential offer/key binding uses EC keys; switching libraries or snippets mid-implementation leaving algorithm and key type inconsistent.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Proof JWT algorithm does not match RSA holder key
- Proof JWT signature validation failed
- Proof JWT algorithm is invalid
- Could not determine the hash algorithm for token
- Proof nonce is invalid or missing
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/f9710b88b9868452.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oidc-vc/src/main/java/org/apereo/cas/oidc/vc/issuer/proof/OidcVerifiableCredentialJwtProofValidator.java:94
protected void verifyAudience(final SignedJWT signedJwt) throws ParseException {
val audiences = signedJwt.getJWTClaimsSet().getAudience();
val credentialIssuer = casProperties.getAuthn().getOidc().getCore().getIssuer();
if (audiences == null || !audiences.contains(credentialIssuer)) {
throw new IllegalArgumentException("Proof audience does not match credential issuer");
}
}
protected void verifyAlgorithm(final SignedJWT signedJwt, final JWK holderJwk) {
val alg = signedJwt.getHeader().getAlgorithm();
if (alg == null || Algorithm.NONE.equals(alg)) {
throw new IllegalArgumentException("Proof JWT algorithm is invalid");
}
if (holderJwk instanceof RSAKey && !JWSAlgorithm.Family.RSA.contains(alg)) {
throw new IllegalArgumentException("Proof JWT algorithm does not match RSA holder key");
}
if (holderJwk instanceof ECKey && !JWSAlgorithm.Family.EC.contains(alg)) {
throw new IllegalArgumentException("Proof JWT algorithm does not match EC holder key");
}
}
protected void verifyFreshness(final SignedJWT signedJwt) throws ParseException {
val claims = signedJwt.getJWTClaimsSet();
val issuedAt = claims.getIssueTime();
if (issuedAt == null) {
throw new IllegalArgumentException("Proof JWT is missing iat");
}
val now = Instant.now(Clock.systemUTC());
val iat = issuedAt.toInstant();
if (iat.isAfter(now.plusSeconds(SECONDS_IN_FUTURE))) {
throw new IllegalArgumentException("Proof iat is in the future");
}
if (iat.isBefore(now.minus(Duration.ofMinutes(MINUTES_IN_PAST)))) {
throw new IllegalArgumentException("Proof JWT is too old");
}
}View on GitHub (pinned to e7288fc434)