jwtk/jjwt · error · IllegalArgumentException
PrivateKeys may not be used to verify digital signatures. Pr
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).
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)
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
- JWT signature does not match locally computed signature. JWT
- The parsed JWT indicates it was signed with the '${algId}' s
- Unsecured JWSs (those with an alg header value of 'none') ar
- JWS verification key must be either a SecretKey (for MAC alg
- PublicKeys may not be used to decrypt data. PublicKeys are u
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/3bcff91298cbc17f.
Report an issue: GitHub.