jwtk/jjwt · error · io.jsonwebtoken.security.InvalidKeyException
Invalid ${id} encoded ${PublicKey|PrivateKey} length. Should
Error message
Invalid ${id} encoded ${PublicKey|PrivateKey} length. Should be ${expected}, found ${actual}. What it means
Raw Edwards curve keys have a fixed encoded length per curve (e.g. 32 bytes for Ed25519/X25519, 57 for Ed448/X448). assertLength is called from toPublicKey/toPrivateKey and rejects raw x or d byte arrays whose length does not match the curve's expected encodedKeyByteLength, throwing an InvalidKeyException.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/EdwardsCurve.java:253
}
}
Assert.eq(keyLen, this.encodedKeyByteLength, "Invalid key length.");
byte[] result = Arrays.copyOfRange(encoded, i, i + keyLen);
keyLen = Bytes.length(result);
Assert.eq(keyLen, this.encodedKeyByteLength, "Invalid key length.");
return result;
} finally {
Bytes.clear(encoded);
}
}
private void assertLength(byte[] raw, boolean isPublic) {
int len = Bytes.length(raw);
if (len != this.encodedKeyByteLength) {
String msg = "Invalid " + getId() + " encoded " + (isPublic ? "PublicKey" : "PrivateKey") +
" length. Should be " + Bytes.bytesMsg(this.encodedKeyByteLength) + ", found " +
Bytes.bytesMsg(len) + ".";
throw new InvalidKeyException(msg);
}
}
public PublicKey toPublicKey(byte[] x, Provider provider) {
assertLength(x, true);
final byte[] encoded = Bytes.concat(this.PUBLIC_KEY_ASN1_PREFIX, x);
final X509EncodedKeySpec spec = new X509EncodedKeySpec(encoded);
JcaTemplate template = new JcaTemplate(getJcaName(), provider);
return template.generatePublic(spec);
}
KeySpec privateKeySpec(byte[] d, boolean standard) {
byte[] prefix = standard ? this.PRIVATE_KEY_ASN1_PREFIX : this.PRIVATE_KEY_JDK11_PREFIX;
byte[] encoded = Bytes.concat(prefix, d);
return new PKCS8EncodedKeySpec(encoded);
}
public PrivateKey toPrivateKey(final byte[] d, Provider provider) {View on GitHub (pinned to fb71496164)
Solutions
- Check the byte array length and trim/pad to the curve's canonical size (32 bytes for 255519-family, 57 for 448-family).
- Decode the JWK value with a proper base64url decoder without padding (e.g. io.jsonwebtoken.lang.Base64Url or java.util.Base64.getUrlDecoder).
- Confirm you are using the curve instance matching the key's crv (EdwardsCurve.Ed25519 for 32-byte keys, not Ed448).
- If you have a 64-byte seed+pub concatenation, use only the first 32 bytes for the private key.
Example fix
// before
byte[] d = Base64.getDecoder().decode(jwk.get("d")); // wrong decoder, wrong length
PrivateKey pk = EdwardsCurve.Ed25519.toPrivateKey(d, null);
// after
byte[] d = Base64.getUrlDecoder().decode((String) jwk.get("d"));
if (d.length != 32) throw new IllegalArgumentException("Ed25519 private key must be 32 bytes");
PrivateKey pk = EdwardsCurve.Ed25519.toPrivateKey(d, null); Defensive patterns
Strategy: validation
Validate before calling
if (d.length != 32) { // Ed25519/X25519
throw new IllegalArgumentException("Expected 32-byte raw key, got " + d.length);
} Type guard
boolean hasCanonicalLength(byte[] raw, int expected) {
return raw != null && raw.length == expected; // 32 for 25519-family, 57 for 448-family
} Try / catch
try {
PrivateKey pk = EdwardsCurve.Ed25519.toPrivateKey(d, null);
} catch (InvalidKeyException e) {
// length mismatch: fix decoding / curve selection
} Prevention
- Use base64url decoding (no padding) for JWK x/d values
- Know your curve's canonical key length (32 vs 57 bytes)
- Never pass seed||public (64-byte) blobs as private keys
When it happens
Trigger: Calling EdwardsCurve.toPublicKey(byte[] x)/toPrivateKey(byte[] d) (directly or via Jwk parsing of an OKP JWK's x/d) with a byte array that is shorter or longer than the curve's canonical size — e.g. 64-byte signature instead of a 32-byte key, hex/base64 decoded incorrectly, or an Ed25519 key parsed as Ed448.
Common situations: Decoding the JWK x value with the wrong base64 variant (standard vs base64url) or with whitespace; concatenating seed+public key (64 bytes) and passing it as the private key; assuming all curves use 32-byte keys.
Related errors
- Invalid ${id} ASN.1 encoding: ${t.getMessage()}
- PrivateKeys may not be used to verify digital signatures. Pr
- PublicKeys may not be used to decrypt data. PublicKeys are u
- Invalid Base64Url <name>: <value>
- JWS verification key must be either a SecretKey (for MAC alg
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/f839c82c56eb6f42.
Report an issue: GitHub.