jwtk/jjwt · error · io.jsonwebtoken.security.SignatureException
Invalid ECDSA signature format.
Error message
Invalid ECDSA signature format.
What it means
Thrown by EcSignatureAlgorithm.transcodeConcatToDER when converting a JWS concat-encoded (R||S) ECDSA signature to DER for the JCA verifier fails for any reason. It wraps every exception from concatToDER (including ArrayIndexOutOfBoundsException) into a SignatureException, a deliberate CVE-2022-21449 (psychic signature) guard so that malformed signature bytes can never bypass verification. It means the JWT's signature bytes were not a plausible fixed-length R||S pair.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/EcSignatureAlgorithm.java:308
System.arraycopy(derSignature, (offset + 2 + rLength) - i, concatSignature, rawLen - i, i);
System.arraycopy(derSignature, (offset + 2 + rLength + 2 + sLength) - j, concatSignature, 2 * rawLen - j, j);
return concatSignature;
}
/**
* Transcodes the ECDSA JWS signature into ASN.1/DER format for use by the JCA verifier.
*
* @param jwsSignature The JWS signature, consisting of the concatenated R and S values. Must not be {@code null}.
* @return The ASN.1/DER encoded signature.
* @throws JwtException If the ECDSA JWS signature format is invalid.
*/
public static byte[] transcodeConcatToDER(byte[] jwsSignature) throws JwtException {
try {
return concatToDER(jwsSignature);
} catch (Exception e) { // CVE-2022-21449 guard
String msg = "Invalid ECDSA signature format.";
throw new SignatureException(msg, e);
}
}
/**
* Converts the specified concat-encoded signature to a DER-encoded signature.
*
* @param jwsSignature concat-encoded signature
* @return correpsonding DER-encoded signature
* @throws ArrayIndexOutOfBoundsException if the signature cannot be converted
* @author Martin Treurnicht via <a href="https://github.com/jwtk/jjwt/commit/61510dfca58dd40b4b32c708935126785dcff48c">61510dfca58dd40b4b32c708935126785dcff48c</a>
*/
private static byte[] concatToDER(byte[] jwsSignature) throws ArrayIndexOutOfBoundsException {
int rawLen = jwsSignature.length / 2;
int i = rawLen;
while ((i > 0) && (jwsSignature[rawLen - i] == 0)) {View on GitHub (pinned to fb71496164)
Solutions
- Check the token is complete and unmodified: base64url-decode the signature part and confirm its length is exactly 2x the curve size (64 bytes for ES256)
- Regenerate the token with Jwts.builder().signWith(ecKey, Jwts.SIG.ES256...) — do not hand-assemble signatures
- Verify the key and alg match: an EC public key with ES256, not RS256/HS256 tokens verified with the wrong parser config
- Catch SignatureException (a JwtException subclass) around parsing and treat the token as untrusted/reject it
Example fix
// before: assuming any token parses
Claims c = Jwts.parser().verifyWith(pubKey).build()
.parseSignedClaims(token).getPayload();
// after: reject malformed signatures explicitly
try {
Claims c = Jwts.parser().verifyWith(pubKey).build()
.parseSignedClaims(token).getPayload();
} catch (SignatureException e) {
throw new UntrustedTokenException("malformed ECDSA signature", e);
} Defensive patterns
Strategy: try-catch
Validate before calling
// check concat signature length matches curve before verify
int half = 32; // ES256
byte[] sig = Base64.getUrlDecoder().decode(sigPart);
if (sig.length != 2 * half) throw new IllegalArgumentException("bad ECDSA signature length"); Type guard
boolean isValidConcatSignature(byte[] sig, int halfLen) {
return sig != null && sig.length == 2 * halfLen && (sig[0] != 0 || sig[halfLen] != 0);
} Try / catch
try {
return Jwts.parser().verifyWith(ecPub).build().parseSignedClaims(token).getPayload();
} catch (SignatureException e) {
audit.warn("Rejected token with malformed ECDSA signature");
throw new UntrustedTokenException(e);
} Prevention
- Treat this exception as evidence of a corrupted or forged token and reject the request
- Never construct signature bytes manually; use Jwts.builder().signWith
- Ensure no middleware rewrites or truncates tokens in transit
- Fuzz-test your token verification endpoint; this guard exists to stop CVE-2022-21449-style bypasses
When it happens
Trigger: Verifying an ES256/ES384/ES512 JWS whose decoded signature is null/empty, has odd or zero length, is shorter than 2*rawLen bytes (so R or S extraction indexes out of bounds), or is otherwise malformed — typically via Jwts.parser().verifyWith(ecPublicKey).parseSignedClaims(token).
Common situations: Truncated or corrupted JWTs in transit; attackers sending junk signature bytes (the guard exists precisely for this); tokens minted by code that base64-encodes the wrong byte range; tests feeding arbitrary byte arrays to the verify path; wrong algorithm configured so a non-EC signature reaches the EC verifier.
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
- The 'none' algorithm cannot be used to create signatures.
- The 'none' algorithm cannot be used to verify signatures.
- Unsupported signature algorithm '${alg}': ${e.getMessage()}
- Cannot verify JWS signature: unable to locate signature veri
- PrivateKeys may not be used to verify digital signatures. Pr
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/022788b207e803fe.
Report an issue: GitHub.