{"record":{"id":"3bcff91298cbc17f","repo":"jwtk/jjwt","slug":"privatekeys-may-not-be-used-to-verify-digital-sign-3bcff9","errorCode":null,"errorMessage":"PrivateKeys may not be used to verify digital signatures. PrivateKeys are used to sign, and PublicKeys are used to verify.","messagePattern":"PrivateKeys may not be used to verify digital signatures\\. PrivateKeys are used to sign, and PublicKeys are used to verify\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParserBuilder.java","lineNumber":279,"sourceCode":"        }\n        String msg = \"JWS verification key must be either a SecretKey (for MAC algorithms) or a PublicKey \" +\n                \"(for Signature algorithms).\";\n        throw new InvalidKeyException(msg);\n    }\n\n    @Override\n    public JwtParserBuilder verifyWith(SecretKey key) {\n        return verifyWith((Key) key);\n    }\n\n    @Override\n    public JwtParserBuilder verifyWith(PublicKey key) {\n        return verifyWith((Key) key);\n    }\n\n    private JwtParserBuilder verifyWith(Key key) {\n        if (key instanceof PrivateKey) {\n            throw new IllegalArgumentException(DefaultJwtParser.PRIV_KEY_VERIFY_MSG);\n        }\n        this.signatureVerificationKey = Assert.notNull(key, \"signature verification key cannot be null.\");\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder decryptWith(SecretKey key) {\n        return decryptWith((Key) key);\n    }\n\n    @Override\n    public JwtParserBuilder decryptWith(PrivateKey key) {\n        return decryptWith((Key) key);\n    }\n\n    private JwtParserBuilder decryptWith(final Key key) {\n        if (key instanceof PublicKey) {\n            throw new IllegalArgumentException(DefaultJwtParser.PUB_KEY_DECRYPT_MSG);","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/jwtk/jjwt/blob/fb71496164c71442d08adec4571d9616ed5e1b8d/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParserBuilder.java#L261-L297","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nPrivateKey priv = loadFromKeystore(\"signing-key\");\nJwts.parser().verifyWith(priv).build().parse(jwt); // IllegalArgumentException\n// after\nPublicKey pub = loadFromKeystoreCert(\"signing-key\"); // derive public key\nJwts.parser().verifyWith(pub).build().parse(jwt);","handlingStrategy":"type-guard","validationCode":"if (key instanceof java.security.PrivateKey) {\n    throw new IllegalArgumentException(\"Refusing to configure a PrivateKey for verification\");\n}","typeGuard":"boolean isSafeVerificationKey(java.security.Key k) {\n    return k instanceof javax.crypto.SecretKey || k instanceof java.security.PublicKey;\n}","tryCatchPattern":"try {\n    builder.verifyWith(key);\n} catch (IllegalArgumentException e) {\n    // PrivateKey supplied: switch to the matching PublicKey\n}","preventionTips":["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"],"tags":["jwt","key-management","security","asymmetric-crypto"],"backgroundTag":"invalid-argument-value","analyzedSha":"fb71496164c71442d08adec4571d9616ed5e1b8d","analyzedAt":"2026-09-09T00:33:09.982Z","contentChangedAt":"2026-09-09T00:33:09.982Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}