jwtk/jjwt · error · io.jsonwebtoken.security.SignatureException
Provided signature is ${actual} but ${id} signatures must be
Error message
Provided signature is ${actual} but ${id} signatures must be exactly ${expected} per [RFC 7518, Section 3.4 (validation)](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4). What it means
Per RFC 7518 Section 3.4, a JWS ECDSA signature must be the raw R||S concatenation of exactly 2*fieldSize bytes (64 for ES256, 96 for ES384, 132 for ES512). During verification the provided signature had a different byte length, so a SignatureException is thrown before parsing R and S. This typically means the signature was not produced in the JWS (concatenated) format.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/EcSignatureAlgorithm.java:215
// mandated per https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4 :
if (signatureByteLength != concatSignature.length) {
/*
* If the expected size is not valid for JOSE, fall back to ASN.1 DER signature IFF the application
* is configured to do so. This fallback is for backwards compatibility ONLY (to support tokens
* generated by early versions of jjwt) and backwards compatibility will be removed in a future
* version of this library. This fallback is only enabled if the system property is set to 'true' due to
* the risk of CVE-2022-21449 attacks on early JVM versions 15, 17 and 18.
*/
// TODO: remove for 1.0 (DER-encoding support is not in the JWT RFCs)
if (concatSignature[0] == 0x30 &&
"true".equalsIgnoreCase(System.getProperty(DER_ENCODING_SYS_PROPERTY_NAME))) {
derSignature = concatSignature;
} else {
String msg = "Provided signature is " + Bytes.bytesMsg(concatSignature.length) + " but " +
getId() + " signatures must be exactly " + Bytes.bytesMsg(signatureByteLength) +
" per [RFC 7518, Section 3.4 (validation)]" +
"(https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4).";
throw new SignatureException(msg);
}
} else {
//guard for JVM security bug CVE-2022-21449:
if (!isValidRAndS(key, concatSignature)) {
return false;
}
// Convert from concat to DER encoding since
// 1) SHAXXXWithECDSAInP1363Format algorithms are only available on >= JDK 9 and
// 2) the SignatureAlgorithm enum JCA alg names are all SHAXXXwithECDSA (which expects DER formatting)
derSignature = transcodeConcatToDER(concatSignature);
}
sig.initVerify(key);
return verify(sig, request.getPayload(), derSignature);
} catch (Exception e) {
String msg = "Unable to verify Elliptic Curve signature using provided ECPublicKey: " + e.getMessage();View on GitHub (pinned to fb71496164)
Solutions
- Ensure the signature is the RFC 7518 raw R||S concat format, exactly 64/96/132 bytes depending on algorithm
- If the source emits DER, transcode with EllipticCurveProvider.transcodeDerToConcat (or JJWT'stranscodeDERToConcat path) before verifying
- Re-verify the base64url signature segment for truncation/corruption (check decoded length with Base64.getUrlDecoder())
Example fix
// before byte[] sig = derSignatureFromOtherLib; // ASN.1 DER, wrong length parser.verifyWith(pubKey).parseSignedClaims(jwt); // fails // after byte[] concat = io.jsonwebtoken.impl.security.EcSignatureAlgorithm.transcodeDERToConcat(derSignature, 64); // or better: only verify signatures produced in JWS concat format by the signing side
Defensive patterns
Strategy: validation
Validate before calling
boolean isJwsConcatSignature(byte[] sig, int fieldSize) {
return sig != null && sig.length == fieldSize * 2; // 64 / 96 / 132
} Try / catch
try {
Jws<Claims> jws = Jwts.parser().verifyWith(pubKey).build().parseSignedClaims(token);
} catch (SignatureException e) {
if (e.getMessage().contains("must be exactly")) {
throw new IllegalArgumentException("Signature not in RFC 7518 concat format", e);
}
throw e;
} Prevention
- Never feed java.security.Signature.sign() output directly into a JWS context — it is DER, not concat
- Validate decoded signature length (64/96/132) before storing or transmitting tokens
- When interoperating with OpenSSL/other libs, transcode DER to concat on the boundary
When it happens
Trigger: Passing a DER/ASN.1-encoded ECDSA signature (as output by java.security.Signature.sign() or OpenSSL by default) directly into the JWT, or passing a truncated/corrupted signature string to a parser's setSigningKey/verifyWith flow.
Common situations: Verifying a JWT signature produced by another library (node-jose, OpenSSL dgst) that emits DER format; hand-splitting base64url JWT parts and passing a damaged signature; storing signatures in a DB column that truncates bytes.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Invalid ECDSA signature format
- The provided Elliptic Curve ${keyType} key size (aka order b
- Unable to verify Elliptic Curve signature using provided ECP
- Invalid ECDSA signature format.
- Unexpected unsecured Claims JWT.
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/7da74cc2c3515fd9.
Report an issue: GitHub.