apereo/cas · error · IllegalArgumentException
Proof JWT algorithm is invalid
Error message
Proof JWT algorithm is invalid
What it means
Thrown by OidcVerifiableCredentialJwtProofValidator.verifyAlgorithm when the proof JWT's alg header is null or equals 'none'. Key-bound proofs must be signed; unsigned JWTs are rejected outright to prevent trivially forged key-possession proofs.
Solutions
- Sign the proof JWT with a concrete JWSAlgorithm (e.g. RS256 or ES256) matching the holder key.
- Remove any code path that builds the proof with Algorithm.NONE.
- Ensure the JWT builder/signer is configured with a real signer before sending the credential request.
Example fix
// before JWSHeader header = new JWSHeader(JWSAlgorithm.NONE); // after JWSHeader header = new JWSHeader(JWSAlgorithm.RS256);
Defensive patterns
Strategy: validation
Validate before calling
JWSAlgorithm alg = signedJwt.getHeader().getAlgorithm();
if (alg == null || Algorithm.NONE.equals(alg)) {
throw new IllegalArgumentException("Proof must be signed");
} Type guard
boolean isSignedProof(SignedJWT jwt) { return jwt.getHeader().getAlgorithm() != null && !Algorithm.NONE.equals(jwt.getHeader().getAlgorithm()); } Prevention
- Always supply a real signer when constructing the proof JWT.
- Add a local assertion that alg is not 'none' before sending.
When it happens
Trigger: Building the proof JWT with Algorithm.NONE or leaving the algorithm unset; constructing a SignedJWT without calling sign() with a real JWSAlgorithm; misconfigured JWT library defaulting to unsecured mode.
Common situations: Developers test with unsigned JWTs to inspect claims first and forget to sign; JWT-building helper libraries emitting 'none' when no signer is supplied; disabled signing in a shared proof-builder utility.
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 nonce is invalid or missing
- Proof JWT signature validation failed
- Proof audience does not match credential issuer
- Proof JWT algorithm does not match RSA holder key
- Proof JWT algorithm does not match EC holder key
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/ef1d7027cbc52424.
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:88
verifier = new ECDSAVerifier(ecKey);
}
if (verifier == null || !signedJwt.verify(verifier)) {
throw new IllegalArgumentException("Proof JWT signature validation failed");
}
}
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))) {View on GitHub (pinned to e7288fc434)