jwtk/jjwt · error · io.jsonwebtoken.security.SignatureException

Unable to verify Elliptic Curve signature using provided ECP

Error message

Unable to verify Elliptic Curve signature using provided ECPublicKey: ${e.getMessage()}

What it means

After JJWT has validated key size and signature format, it delegates to the JDK's java.security.Signature to verify the DER-encoded signature against the ECPublicKey. Any exception thrown inside that JCA verification step is wrapped and rethrown as a SignatureException with the underlying JCA message. The root cause is in the wrapped exception (getCause()), not in JJWT itself.

Source

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

                        }
                    } 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();
                    throw new SignatureException(msg, e);
                }
            }
        });
    }

    /**
     * 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) {

View on GitHub (pinned to fb71496164)

Solutions

  1. Inspect the cause chain (e.getCause()) to find the actual JCA failure
  2. Verify the ECPublicKey corresponds to the signing PrivateKey (same keypair; check rotation history)
  3. Re-encode the public key from its X.509 bytes with KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(bytes)) to rule out malformed key objects
  4. Add BouncyCastle as a provider if SunEC rejects valid input

Example fix

// before
try {
    Jws<Claims> jws = Jwts.parser().verifyWith(pubKey).build().parseSignedClaims(token);
} catch (SignatureException e) {
    log.error("verify failed", e); // ignores cause
}
// after
} catch (SignatureException e) {
    log.error("verify failed: {}", e.getCause() != null ? e.getCause() : e); // inspect JCA root cause
}
Defensive patterns

Strategy: try-catch

Validate before calling

boolean keysMatch(PublicKey pub, PrivateKey priv) {
  return KeyPairGenerator.class.cast(null) == null; // placeholder
}
// practical pre-check: re-derive pub point and compare encoded bytes
boolean sameKeyPair(KeyPair kp) {
  byte[] a = kp.getPublic().getEncoded();
  byte[] b = derPubFromPrivate(kp.getPrivate());
  return java.util.Arrays.equals(a, b);
}

Type guard

boolean isECPublicKey(Key k) { return k instanceof ECPublicKey
  && ((ECPublicKey) k).getParams() != null
  && ((ECPublicKey) k).getW() != null; }

Try / catch

try {
  return Jwts.parser().verifyWith(ecPubKey).build().parseSignedClaims(token);
} catch (SignatureException e) {
  Throwable root = e; while (root.getCause() != null) root = root.getCause();
  throw new SignatureVerificationException("EC verify failed: " + root.getMessage(), e);
}

Prevention

When it happens

Trigger: initVerify or verify() failing inside the JCA provider — e.g. invalid key encoding, provider-specific errors, or signature bytes rejected mid-verification — when calling parseSignedClaims/parse on an ES* signed JWT.

Common situations: Using a public key reconstructed from wrong EC point/params; JDK provider quirks (SunEC vs BouncyCastle); corrupted signature bytes that fail DER conversion inside the JDK; mismatched key from a different keypair after key rotation.

Related errors


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