jwtk/jjwt · error · IllegalArgumentException

PublicKeys may not be used to create digital signatures. Pri

Error message

PublicKeys may not be used to create digital signatures. PrivateKeys are used to sign, and PublicKeys are used to verify.

What it means

Digital signatures are created with PrivateKeys and verified with PublicKeys. Passing a PublicKey to signWith would be cryptographically wrong, so the builder rejects it immediately with an IllegalArgumentException.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtBuilder.java:217

            throw new UnsupportedKeyException(msg);
        }
        return alg;
    }

    @Override
    public JwtBuilder signWith(Key key) throws InvalidKeyException {
        Assert.notNull(key, "Key argument cannot be null.");
        SecureDigestAlgorithm<Key, ?> alg = forSigningKey(key); // https://github.com/jwtk/jjwt/issues/381
        return signWith(key, alg);
    }

    @Override
    public <K extends Key> JwtBuilder signWith(K key, final SecureDigestAlgorithm<? super K, ?> alg)
            throws InvalidKeyException {

        Assert.notNull(key, "Key argument cannot be null.");
        if (key instanceof PublicKey) { // it's always wrong/insecure to try to create signatures with PublicKeys:
            throw new IllegalArgumentException(PUB_KEY_SIGN_MSG);
        }
        // Implementation note:  Ordinarily Passwords should not be used to create secure digests because they usually
        // lack the length or entropy necessary for secure cryptographic operations, and are prone to misuse.
        // However, we DO NOT prevent them as arguments here (like the above PublicKey check) because
        // it is conceivable that a custom SecureDigestAlgorithm implementation would allow Password instances
        // so that it might perform its own internal key-derivation logic producing a key that is then used to create a
        // secure hash.
        //
        // Even so, a fallback safety check is that JJWT's only out-of-the-box Password implementation
        // (io.jsonwebtoken.impl.security.PasswordSpec) explicitly forbids calls to password.getEncoded() in all
        // scenarios to avoid potential misuse, so a digest algorithm implementation would explicitly need to avoid
        // this by calling toCharArray() instead.
        //
        // TLDR; the digest algorithm implementation has the final say whether a password instance is valid

        Assert.notNull(alg, "SignatureAlgorithm cannot be null.");
        String id = Assert.hasText(alg.getId(), "SignatureAlgorithm id cannot be null or empty.");
        if (Jwts.SIG.NONE.getId().equalsIgnoreCase(id)) {

View on GitHub (pinned to fb71496164)

Solutions

  1. Pass the corresponding PrivateKey to signWith for token creation.
  2. Verify key usage before signing: if (key instanceof PublicKey) fail fast in your own code and use the private counterpart.
  3. Fix keystore/dependency injection so the signing component receives the private key and only verifiers receive the public key.
  4. Catch IllegalArgumentException and surface a clear configuration error.

Example fix

// before
String jwt = Jwts.builder().signWith(publicKey, Jwts.SIG.RS256)...compact();
// after
String jwt = Jwts.builder().signWith(privateKey, Jwts.SIG.RS256)...compact();
Defensive patterns

Strategy: validation

Validate before calling

if (key instanceof PublicKey) throw new IllegalArgumentException("signWith requires a PrivateKey");

Type guard

boolean canSign(Key k) { return !(k instanceof PublicKey) && k != null; }

Try / catch

try { builder.signWith(key, alg); } catch (IllegalArgumentException e) { /* wrong key role — fail config check */ }

Prevention

When it happens

Trigger: JwtBuilder.signWith(publicKey) or signWith(publicKey, alg) — any variant where the supplied key is an instance of java.security.PublicKey.

Common situations: Config mix-ups where the verification key is wired into the token-creation code path; loading the wrong key from a keystore; swapped key parameters in helper methods.

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


AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09). Data as JSON: /api/errors/14243bea5836d12c. Report an issue: GitHub.