apereo/cas · error · IllegalArgumentException
Proof JWT signature validation failed
Error message
Proof JWT signature validation failed
What it means
Thrown by OidcVerifiableCredentialJwtProofValidator.verifySignature when the holder's proof JWT cannot be verified: no verifier could be created because the holderJwk is neither an RSAKey nor an ECKey, or SignedJWT.verify(verifier) returns false. This proves control check ensures the credential requester actually possesses the private key of the bound holder key.
Solutions
- Sign the proof JWT with the private key corresponding exactly to the holder JWK / kid bound to the credential request.
- Use an RSA or EC JWK as the holder key; other key types are not accepted by this validator.
- Verify the kid in the JWT header resolves to the intended JWK and that the JWK is well-formed.
- Re-check the signing algorithm and library settings to ensure the signature covers the exact JWT content.
Example fix
// before SignedJWT signed = new SignedJWT(header, claims); // never signed // after SignedJWT signed = new SignedJWT(header, claims); signed.sign(new RSASSASigner(rsaKeyPair.getPrivate()));
Defensive patterns
Strategy: try-catch
Validate before calling
if (!(holderJwk instanceof RSAKey) && !(holderJwk instanceof ECKey)) {
throw new IllegalArgumentException("Holder key must be RSA or EC");
} Type guard
boolean isSupportedHolderKey(JWK jwk) { return jwk instanceof RSAKey || jwk instanceof ECKey; } Try / catch
try {
validator.validate(signedJwt, holderJwk);
} catch (IllegalArgumentException e) {
log.warn("Proof signature validation failed: re-sign with the holder private key");
} Prevention
- Sign proofs with the exact private key matching the bound holder JWK.
- Only use RSA or EC holder keys.
- Unit-test the full sign/verify round trip locally before calling the issuer.
When it happens
Trigger: Proof JWT signed with a key that does not match the key (kid/JWK) presented or bound to the request; proof signed with the wrong private key; JWK of an unsupported type (e.g. oct/symmetric or OKP Ed25519 key) passed to the validator; corrupted or tampered JWT.
Common situations: Client signs the proof with a test key but references a different key in cnf/jkt; unsupported key types (symmetric, Edwards-curve) supplied as holder JWKs; kid lookup returning the wrong JWK; JWT payload modified after signing.
Related errors
- Proof nonce is invalid or missing
- Proof audience does not match credential issuer
- Proof JWT algorithm is invalid
- 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/4542188e01d95176.
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:73
protected @Nullable String verifyNonce(final SignedJWT signedJwt) throws Exception {
val claims = signedJwt.getJWTClaimsSet();
val nonce = claims.getStringClaim("nonce");
if (nonce == null || !oidcVerifiableCredentialNonceService.exists(nonce)) {
throw new IllegalArgumentException("Proof nonce %s is invalid or missing".formatted(nonce));
}
return nonce;
}
protected void verifySignature(final SignedJWT signedJwt, final JWK holderJwk) throws Exception {
JWSVerifier verifier = null;
if (holderJwk instanceof final RSAKey rsaKey) {
verifier = new RSASSAVerifier(rsaKey);
} else if (holderJwk instanceof final ECKey ecKey) {
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");View on GitHub (pinned to e7288fc434)