{"record":{"id":"022788b207e803fe","repo":"jwtk/jjwt","slug":"invalid-ecdsa-signature-format-022788","errorCode":null,"errorMessage":"Invalid ECDSA signature format.","messagePattern":"Invalid ECDSA signature format\\.","errorType":"exception","errorClass":"io.jsonwebtoken.security.SignatureException","httpStatus":null,"severity":"error","filePath":"impl/src/main/java/io/jsonwebtoken/impl/security/EcSignatureAlgorithm.java","lineNumber":308,"sourceCode":"        System.arraycopy(derSignature, (offset + 2 + rLength) - i, concatSignature, rawLen - i, i);\n        System.arraycopy(derSignature, (offset + 2 + rLength + 2 + sLength) - j, concatSignature, 2 * rawLen - j, j);\n\n        return concatSignature;\n    }\n\n    /**\n     * Transcodes the ECDSA JWS signature into ASN.1/DER format for use by the JCA verifier.\n     *\n     * @param jwsSignature The JWS signature, consisting of the concatenated R and S values. Must not be {@code null}.\n     * @return The ASN.1/DER encoded signature.\n     * @throws JwtException If the ECDSA JWS signature format is invalid.\n     */\n    public static byte[] transcodeConcatToDER(byte[] jwsSignature) throws JwtException {\n        try {\n            return concatToDER(jwsSignature);\n        } catch (Exception e) { // CVE-2022-21449 guard\n            String msg = \"Invalid ECDSA signature format.\";\n            throw new SignatureException(msg, e);\n        }\n    }\n\n    /**\n     * Converts the specified concat-encoded signature to a DER-encoded signature.\n     *\n     * @param jwsSignature concat-encoded signature\n     * @return correpsonding DER-encoded signature\n     * @throws ArrayIndexOutOfBoundsException if the signature cannot be converted\n     * @author Martin Treurnicht via <a href=\"https://github.com/jwtk/jjwt/commit/61510dfca58dd40b4b32c708935126785dcff48c\">61510dfca58dd40b4b32c708935126785dcff48c</a>\n     */\n    private static byte[] concatToDER(byte[] jwsSignature) throws ArrayIndexOutOfBoundsException {\n\n        int rawLen = jwsSignature.length / 2;\n\n        int i = rawLen;\n\n        while ((i > 0) && (jwsSignature[rawLen - i] == 0)) {","sourceCodeStart":290,"sourceCodeEnd":326,"githubUrl":"https://github.com/jwtk/jjwt/blob/fb71496164c71442d08adec4571d9616ed5e1b8d/impl/src/main/java/io/jsonwebtoken/impl/security/EcSignatureAlgorithm.java#L290-L326","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before: assuming any token parses\nClaims c = Jwts.parser().verifyWith(pubKey).build()\n    .parseSignedClaims(token).getPayload();\n// after: reject malformed signatures explicitly\ntry {\n    Claims c = Jwts.parser().verifyWith(pubKey).build()\n        .parseSignedClaims(token).getPayload();\n} catch (SignatureException e) {\n    throw new UntrustedTokenException(\"malformed ECDSA signature\", e);\n}","handlingStrategy":"try-catch","validationCode":"// check concat signature length matches curve before verify\nint half = 32; // ES256\nbyte[] sig = Base64.getUrlDecoder().decode(sigPart);\nif (sig.length != 2 * half) throw new IllegalArgumentException(\"bad ECDSA signature length\");","typeGuard":"boolean isValidConcatSignature(byte[] sig, int halfLen) {\n    return sig != null && sig.length == 2 * halfLen && (sig[0] != 0 || sig[halfLen] != 0);\n}","tryCatchPattern":"try {\n    return Jwts.parser().verifyWith(ecPub).build().parseSignedClaims(token).getPayload();\n} catch (SignatureException e) {\n    audit.warn(\"Rejected token with malformed ECDSA signature\");\n    throw new UntrustedTokenException(e);\n}","preventionTips":["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"],"tags":["jwt","ecdsa","signature","verification","security"],"backgroundTag":"invalid-argument-format","analyzedSha":"fb71496164c71442d08adec4571d9616ed5e1b8d","analyzedAt":"2026-09-09T00:33:09.982Z","contentChangedAt":"2026-09-09T00:33:09.982Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}