jwtk/jjwt · error · IllegalArgumentException

[JWA RFC 7518, Section 4.8.1.2](https://www.rfc-editor.org/r

Error message

[JWA RFC 7518, Section 4.8.1.2](https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.2) recommends password-based-encryption iterations be greater than or equal to 1000. Provided: ${iterations}

What it means

RFC 7518 Section 4.8.1.2 recommends PBES2 password-based encryption use at least 1000 iterations. Pbes2HsAkwAlgorithm.assertIterations enforces this minimum and throws IllegalArgumentException for lower values (MIN_RECOMMENDED_ITERATIONS = 1000).

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/Pbes2HsAkwAlgorithm.java:86

        // new byte array indices to 0x00, meaning the last one will be our zero delimiter:
        byte[] output = new byte[bytes.length + 1];
        System.arraycopy(bytes, 0, output, 0, bytes.length);
        return output;
    }

    private static int hashBitLength(int keyBitLength) {
        return keyBitLength * 2;
    }

    private static String idFor(int hashBitLength, KeyAlgorithm<SecretKey, SecretKey> wrapAlg) {
        Assert.notNull(wrapAlg, "wrapAlg argument cannot be null.");
        return "PBES2-HS" + hashBitLength + "+" + wrapAlg.getId();
    }

    public static int assertIterations(int iterations) {
        if (iterations < MIN_RECOMMENDED_ITERATIONS) {
            String msg = MIN_ITERATIONS_MSG_PREFIX + iterations;
            throw new IllegalArgumentException(msg);
        }
        return iterations;
    }

    public Pbes2HsAkwAlgorithm(int keyBitLength) {
        this(hashBitLength(keyBitLength), new AesWrapKeyAlgorithm(keyBitLength));
    }

    protected Pbes2HsAkwAlgorithm(int hashBitLength, KeyAlgorithm<SecretKey, SecretKey> wrapAlg) {
        super(idFor(hashBitLength, wrapAlg), "PBKDF2WithHmacSHA" + hashBitLength);
        this.wrapAlg = wrapAlg; // no need to assert non-null due to 'idFor' implementation above

        // There's some white box knowledge here: there is no need to assert the value of hashBitLength
        // because that is done implicitly in the constructor when instantiating AesWrapKeyAlgorithm. See that class's
        // implementation to see the assertion:
        this.HASH_BYTE_LENGTH = hashBitLength / Byte.SIZE;

        // If the JwtBuilder caller doesn't specify an iteration count, fall back to OWASP best-practice recommendations

View on GitHub (pinned to fb71496164)

Solutions

  1. Increase the iteration count to at least 1000 (higher, e.g. 100000+, is better for security).
  2. Wrap configuration in try-catch for IllegalArgumentException when accepting user-provided iteration counts.
  3. Set iterations via the algorithm builder's iterations() method with a validated constant.

Example fix

// before
Pbes2HsAkwAlgorithm alg = new Pbes2HsAkwAlgorithm(128).iterations(10);
// after
Pbes2HsAkwAlgorithm alg = new Pbes2HsAkwAlgorithm(128).iterations(1000);
Defensive patterns

Strategy: validation

Validate before calling

if (iterations < 1000) {
    throw new IllegalArgumentException("p2c must be >= 1000 per RFC 7518 4.8.1.2");
}

Try / catch

try {
    alg.iterations(n);
} catch (IllegalArgumentException e) {
    // below recommended minimum: raise n
}

Prevention

When it happens

Trigger: Calling iterations(n) or otherwise configuring a PBES2 JWE algorithm with a p2c/iteration count below 1000, either programmatically or via the 'p2c' header value when building.

Common situations: Copying example code with small iteration counts, tuning iterations down for speed in tests, or generating headers with p2c values from older configs.

Related errors


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