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
- Inspect the cause chain (e.getCause()) to find the actual JCA failure
- Verify the ECPublicKey corresponds to the signing PrivateKey (same keypair; check rotation history)
- Re-encode the public key from its X.509 bytes with KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(bytes)) to rule out malformed key objects
- 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
- Always log/inspect getCause() — the JCA message explains the real failure
- Pin a known-good security provider (e.g. BouncyCastle) to avoid SunEC quirks across JDK versions
- Rebuild public keys from canonical X.509 bytes instead of passing hand-constructed ECPoint objects
- Test verification against a token signed with the same keypair after any key rotation
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
- JWS verification key must be either a SecretKey (for MAC alg
- The provided Elliptic Curve ${keyType} key size (aka order b
- Provided signature is ${actual} but ${id} signatures must be
- Invalid ECDSA signature format
- Invalid ECDSA signature format.
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/4f8100fef699cc97.
Report an issue: GitHub.