jwtk/jjwt · error · IllegalArgumentException
PrivateKeys may not be used to verify digital signatures…
Error message
PrivateKeys may not be used to verify digital signatures. PrivateKeys are used to sign, and PublicKeys are used to verify.
What it means
Thrown as IllegalArgumentException from DefaultJwtParserBuilder.verifyWith(Key) when a PrivateKey is supplied for JWS signature verification. Using a private key to verify would be both semantically wrong (private keys sign; public keys verify) and a security anti-pattern (it would expose the private key to verification logic).
Solutions
- Load and pass the corresponding PublicKey instead of the PrivateKey.
- If you only have the private key file (e.g. PEM PKCS#8), extract the public key from it once and distribute/verify with that.
- For HMAC (symmetric) tokens use a SecretKey, not a PrivateKey, and ensure the algorithm family matches the token.
- Audit key-loading code so signing and verification paths use distinct, purpose-appropriate keys.
Example fix
// before
PrivateKey priv = loadFromKeystore("signing-key");
Jwts.parser().verifyWith(priv).build().parse(jwt); // IllegalArgumentException
// after
PublicKey pub = loadFromKeystoreCert("signing-key"); // derive public key
Jwts.parser().verifyWith(pub).build().parse(jwt); Defensive patterns
Strategy: type-guard
Validate before calling
if (key instanceof java.security.PrivateKey) {
throw new IllegalArgumentException("Refusing to configure a PrivateKey for verification");
} Type guard
boolean isSafeVerificationKey(java.security.Key k) {
return k instanceof javax.crypto.SecretKey || k instanceof java.security.PublicKey;
} Try / catch
try {
builder.verifyWith(key);
} catch (IllegalArgumentException e) {
// PrivateKey supplied: switch to the matching PublicKey
} Prevention
- Keep private keys in a signing-only code path/module
- Derive and distribute PublicKeys for verification, never PrivateKeys
- Add code review checks: any verifyWith/setSigningKey call must not receive a PrivateKey
- Separate keystores for signing (private) and verification (public/trust) material
When it happens
Trigger: Calling verifyWith(privateKey) — commonly via the deprecated setSigningKey(privateKey) path — when configuring an asymmetric (RS256/ES256/etc.) parser; e.g. loading the server's own signing keystore key for verification.
Common situations: Copy-pasting signing code into verification code; a service that both signs and verifies loading the wrong keystore entry; misunderstanding asymmetric crypto and reusing the same key object on both sides.
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
- keys may not be used with ECDH-ES key agreement algorithms…
- Invalid ECDSA signature format.
- [JWA RFC 7518, Section…
- JWS verification key must be either a SecretKey (for MAC…
- JWT signature does not match locally computed signature…
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/3bcff91298cbc17f.
Report an issue: GitHub.
Appendix: source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParserBuilder.java:279
}
String msg = "JWS verification key must be either a SecretKey (for MAC algorithms) or a PublicKey " +
"(for Signature algorithms).";
throw new InvalidKeyException(msg);
}
@Override
public JwtParserBuilder verifyWith(SecretKey key) {
return verifyWith((Key) key);
}
@Override
public JwtParserBuilder verifyWith(PublicKey key) {
return verifyWith((Key) key);
}
private JwtParserBuilder verifyWith(Key key) {
if (key instanceof PrivateKey) {
throw new IllegalArgumentException(DefaultJwtParser.PRIV_KEY_VERIFY_MSG);
}
this.signatureVerificationKey = Assert.notNull(key, "signature verification key cannot be null.");
return this;
}
@Override
public JwtParserBuilder decryptWith(SecretKey key) {
return decryptWith((Key) key);
}
@Override
public JwtParserBuilder decryptWith(PrivateKey key) {
return decryptWith((Key) key);
}
private JwtParserBuilder decryptWith(final Key key) {
if (key instanceof PublicKey) {
throw new IllegalArgumentException(DefaultJwtParser.PUB_KEY_DECRYPT_MSG);View on GitHub (pinned to fb71496164)