jwtk/jjwt · error · io.jsonwebtoken.security.InvalidKeyException
The provided Elliptic Curve ${keyType} key size (aka order b
Error message
The provided Elliptic Curve ${keyType} key size (aka order bit length) is ${size}, but the '${id}' algorithm requires EC Keys with ${orderBitLength} per [RFC 7518, Section 3.4](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4). What it means
JJWT's EC signature algorithms (ES256/ES384/ES512) require an EC key whose order bit length exactly matches the algorithm's expected size (256, 384, or 512 bits). During key validation the actual key size was compared against the required orderBitLength and did not match, so an InvalidKeyException is thrown referencing RFC 7518 Section 3.4. This guards against signing/verifying with a key too weak or too strong for the chosen algorithm.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/EcSignatureAlgorithm.java:159
.random(Randoms.secureRandom());
}
@Override
protected void validateKey(Key key, boolean signing) {
super.validateKey(key, signing);
if (!KEY_ALG_NAMES.contains(KeysBridge.findAlgorithm(key))) {
throw new InvalidKeyException("Unrecognized EC key algorithm name.");
}
int size = KeysBridge.findBitLength(key);
if (size < 0) return; // likely PKCS11 or HSM key, can't get the data we need
int sigFieldByteLength = Bytes.length(size);
int concatByteLength = sigFieldByteLength * 2;
if (concatByteLength != this.signatureByteLength) {
String msg = "The provided Elliptic Curve " + keyType(signing) +
" key size (aka order bit length) is " + Bytes.bitsMsg(size) + ", but the '" +
getId() + "' algorithm requires EC Keys with " + Bytes.bitsMsg(this.orderBitLength) +
" per [RFC 7518, Section 3.4](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4).";
throw new InvalidKeyException(msg);
}
}
@Override
protected byte[] doDigest(final SecureRequest<InputStream, PrivateKey> request) {
return jca(request).withSignature(new CheckedFunction<Signature, byte[]>() {
@Override
public byte[] apply(Signature sig) throws Exception {
sig.initSign(KeysBridge.root(request));
byte[] signature = sign(sig, request.getPayload());
return transcodeDERToConcat(signature, signatureByteLength);
}
});
}
boolean isValidRAndS(PublicKey key, byte[] concatSignature) {
if (key instanceof ECKey) { //Some PKCS11 providers and HSMs won't expose the ECKey interface, so we have to check first
ECKey ecKey = (ECKey) key;View on GitHub (pinned to fb71496164)
Solutions
- Regenerate or obtain an EC key on the curve matching the algorithm: P-256 for ES256, P-384 for ES384, P-521 for ES512
- If the key is intentional, change the algorithm to the one matching its size (e.g. ES384 for a 384-bit key)
- Print key size before use: KeyPairGenerator.getInstance("EC").initialize(256) — or check ((ECPublicKey)key).getParams().getOrder().bitLength()
Example fix
// before
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC"); // provider default curve, may not be 256-bit
KeyPair kp = kpg.generateKeyPair();
String jwt = Jwts.builder().signWith(kp.getPrivate(), SignatureAlgorithm.ES256).compact();
// after
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(new ECGenParameterSpec("secp256r1")); // P-256 == ES256 requirement
KeyPair kp = kpg.generateKeyPair();
String jwt = Jwts.builder().signWith(kp.getPrivate(), SignatureAlgorithm.ES256).compact(); Defensive patterns
Strategy: validation
Validate before calling
boolean ecKeyMatches(PrivateKey key, String alg) {
ECParameterSpec p = ((ECPrivateKey) key).getParams();
int bits = p.getOrder().bitLength();
int required = alg.equals("ES256") ? 256 : alg.equals("ES384") ? 384 : 521;
return bits == required;
} Type guard
boolean isP256Key(PrivateKey k) {
return k instanceof ECPrivateKey
&& ((ECPrivateKey) k).getParams().getOrder().bitLength() == 256;
} Try / catch
try {
String jwt = Jwts.builder().signWith(ecKey, SignatureAlgorithm.ES256).compact();
} catch (InvalidKeyException e) {
throw new IllegalStateException("EC key curve does not match ES256 (need 256-bit order)", e);
} Prevention
- Initialize KeyPairGenerator explicitly with ECGenParameterSpec (secp256r1/secp384r1/secp521r1)
- Check ((ECKey) key).getParams().getOrder().bitLength() at startup, before signing anything
- Keep one curve-to-algorithm mapping table in config and derive the algorithm from the key, not vice versa
When it happens
Trigger: Calling Jwts.builder().signWith(key, SignatureAlgorithm.ES256) (or parse-time verifyWith) with an EC PrivateKey/PublicKey whose curve is not exactly P-256/384/521 — e.g. a P-224, secp256k1, or P-521 key paired with ES256.
Common situations: Generating keys with OpenSSL default curves (secp256k1) instead of NIST P-curves; reusing one P-384 key across services configured for ES256; migrating from HS256 to ES256 while keeping old key files; picking a curve by habit ('stronger is better') without matching the algorithm.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- PrivateKeys may not be used to verify digital signatures. Pr
- JWS verification key must be either a SecretKey (for MAC alg
- Provided signature is ${actual} but ${id} signatures must be
- Unable to verify Elliptic Curve signature using provided ECP
- Invalid ECDSA signature format
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/a3a486ceede67e12.
Report an issue: GitHub.