keycloak/keycloak · critical · VerificationException
Invalid jws signature
Error message
Invalid jws signature
What it means
Thrown by JwsToken.verifySignature() (line 93) when verifier.verify(encodedSignatureInput, signature) returns false: the signature does not validate against the signing input using the verifier's public key. The token was tampered with, signed by a different key, or corrupted. This is a security-critical rejection.
Source
Thrown at core/src/main/java/org/keycloak/sdjwt/JwsToken.java:93
}
return jws;
}
public void verifySignature(SignatureVerifierContext verifier) throws VerificationException {
Objects.requireNonNull(verifier, "verifier must not be null");
String headerAlgorithm = jwsHeader == null || jwsHeader.getAlgorithm() == null
? null
: jwsHeader.getRawAlgorithm();
String verifierAlgorithm = verifier.getAlgorithm();
if (headerAlgorithm == null || verifierAlgorithm == null || !headerAlgorithm.equals(verifierAlgorithm)) {
throw new VerificationException(String.format(
"JWS header algorithm '%s' does not match verifier algorithm '%s'",
headerAlgorithm, verifierAlgorithm));
}
try {
if (!verifier.verify(jwsInput.getEncodedSignatureInput().getBytes(StandardCharsets.UTF_8),
jwsInput.getSignature())) {
throw new VerificationException("Invalid jws signature");
}
} catch (Exception e) {
throw new VerificationException(e);
}
}
public Optional<String> getSdHashAlgorithm() {
return Optional.ofNullable(payload.get(OID4VCConstants.CLAIM_NAME_SD_HASH_ALGORITHM))
.map(JsonNode::textValue);
}
public String getJws() {
return jws;
}
public void setJws(String jws) {
this.jws = jws;
}View on GitHub (pinned to 66c7e15a37)
Solutions
- Confirm the verifier uses the issuer's current public key (refresh the JWKS).
- Check the JWS string is not truncated or altered by whitespace/encoding.
- For key binding, ensure the holder key matches the cnf.jwk in the SD-JWT VC.
- Verify the signing input is the UTF-8 bytes of the encoded signature input (header.payload).
Defensive patterns
Strategy: try-catch
Validate before calling
// No meaningful pre-check: signature validity is the verifier's job. Ensure the right key first.
public static SignatureVerifierContext loadCurrentIssuerVerifier(JwkParsingSource source) {
// refresh JWKS so the key is current, then build the verifier
return source.refreshAndBuild();
} Try / catch
// Signature failure is security-critical; never retry with relaxed checks.
try {
token.verifySignature(verifier);
} catch (VerificationException e) {
// treat as invalid credential — do not fall back to a different key to 'make it pass'
throw e;
} Prevention
- Keep the issuer/holder public key current; refresh JWKS on rotation.
- Do not truncate or alter the JWS string (watch copy-paste and encoding).
- For key binding, verify the holder key matches cnf.jwk exactly.
- Never fall back to alternate keys on signature failure — reject the token.
When it happens
Trigger: The token's signature segment was modified; the verifier holds a stale or wrong issuer key; a token signed by one issuer is verified against another's key; the JWS string is truncated; for key binding, the holder signed with a key different from cnf.jwk.
Common situations: Wrong issuer JWKS configured; key rotation without JWKS refresh; man-in-the-middle tampering; copy-paste truncation of the JWS; holder key binding JWT signed by a different holder key than the cnf.jwk in the credential.
Related errors
- JWS header algorithm '%s' does not match verifier algorithm
- Invalid token signature
- Unexpected or insecure hash algorithm: " + hashAlg
- Invalid signature on document
- Error validating signature
AI-assisted analysis of keycloak/keycloak@66c7e15a37 (2026-08-14).
Data as JSON: /api/errors/9e098e5ba58a5485.
Report an issue: GitHub.