jwtk/jjwt · error · io.jsonwebtoken.JwtException

Invalid ECDSA signature format

Error message

Invalid ECDSA signature format

What it means

JJWT's transcodeDERToConcat converts an ASN.1/DER ECDSA signature into the RFC 7518 R||S concatenation. The very first structural check requires the DER blob to be at least 8 bytes and to start with the SEQUENCE tag byte 0x30. A signature failing this basic shape check causes a JwtException('Invalid ECDSA signature format').

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/EcSignatureAlgorithm.java:253

                }
            }
        });
    }

    /**
     * Transcodes the JCA ASN.1/DER-encoded signature into the concatenated
     * R + S format expected by ECDSA JWS.
     *
     * @param derSignature The ASN1./DER-encoded. Must not be {@code null}.
     * @param outputLength The expected length of the ECDSA JWS signature.
     * @return The ECDSA JWS encoded signature.
     * @throws JwtException If the ASN.1/DER signature format is invalid.
     * @author Martin Treurnicht via <a href="https://github.com/jwtk/jjwt/commit/61510dfca58dd40b4b32c708935126785dcff48c">61510dfca58dd40b4b32c708935126785dcff48c</a>
     */
    public static byte[] transcodeDERToConcat(final byte[] derSignature, int outputLength) throws JwtException {

        if (derSignature.length < 8 || derSignature[0] != 48) {
            throw new JwtException("Invalid ECDSA signature format");
        }

        int offset;
        if (derSignature[1] > 0) {
            offset = 2;
        } else if (derSignature[1] == (byte) 0x81) {
            offset = 3;
        } else {
            throw new JwtException("Invalid ECDSA signature format");
        }

        byte rLength = derSignature[offset + 1];

        int i = rLength;
        while ((i > 0) && (derSignature[(offset + 2 + rLength) - i] == 0)) {
            i--;
        }

View on GitHub (pinned to fb71496164)

Solutions

  1. Confirm the input to transcodeDERToConcat is genuinely DER (starts with 0x30); raw JWS concat signatures must NOT be passed here
  2. Check the signature's decoded length against the expected 64/96/132 bytes before transcoding — short arrays indicate truncation upstream
  3. Regenerate the token from the trusted signer if the bytes are corrupted

Example fix

// before
byte[] raw = Base64.getUrlDecoder().decode(jwtSignatureSegment); // already R||S concat
byte[] der = EcSignatureAlgorithm.transcodeDERToConcat(raw, 64); // throws: not DER
// after
byte[] raw = Base64.getUrlDecoder().decode(jwtSignatureSegment);
if (raw.length != 64) throw new IllegalArgumentException("bad ES256 signature length");
// raw is already concat format; no transcoding needed
Defensive patterns

Strategy: validation

Validate before calling

boolean looksLikeDer(byte[] sig) {
  return sig != null && sig.length >= 8 && sig[0] == 0x30;
}

Try / catch

try {
  byte[] concat = EcSignatureAlgorithm.transcodeDERToConcat(derSig, 64);
} catch (JwtException e) {
  throw new IllegalArgumentException("Input is not a DER ECDSA signature (expected 0x30-leading ASN.1)", e);
}

Prevention

When it happens

Trigger: Calling apply()/verify with a signature byte array shorter than 8 bytes or not beginning with 0x30 — i.e. not DER at all — for example a raw concat signature fed into a DER-parsing path, or random garbage/truncated bytes.

Common situations: Base64url-decoding the JWT signature segment and re-checking it manually with transcodeDERToConcat when it is already concat format; truncated storage of signature bytes; verifying tokens signed by non-JWS schemes.

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


AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09). Data as JSON: /api/errors/972ee71febd704bd. Report an issue: GitHub.