jwtk/jjwt · error · io.jsonwebtoken.security.SecurityException

${Class} callback execution failed: ${t.getMessage()}

Error message

${Class} callback execution failed: ${t.getMessage()}

What it means

JcaTemplate wraps JCE operations (Cipher, KeyFactory, SecretKeyFactory, KeyGenerator) in callback functions. Any Throwable thrown inside a callback other than SecurityException is wrapped into a SecurityException with this message naming the JCE class, preserving the cause. It signals that the underlying JCE call failed — e.g. bad algorithm parameters, unsupported provider, or invalid key material.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/JcaTemplate.java:123

    private <T, R> R execute(Class<T> clazz, CheckedFunction<T, R> callback, Provider provider) throws Exception {
        InstanceFactory<?> factory = REGISTRY.get(clazz);
        Assert.notNull(factory, "Unsupported JCA instance class.");

        Object object = factory.get(this.jcaName, provider);
        T instance = Assert.isInstanceOf(clazz, object, "Factory instance does not match expected type.");

        return callback.apply(instance);
    }

    private <T> T execute(Class<?> clazz, CheckedSupplier<T> fn) throws SecurityException {
        try {
            return fn.get();
        } catch (SecurityException se) {
            throw se; //propagate
        } catch (Throwable t) {
            String msg = clazz.getSimpleName() + " callback execution failed: " + t.getMessage();
            throw new SecurityException(msg, t);
        }
    }

    private <T, R> R execute(final Class<T> clazz, final CheckedFunction<T, R> fn) throws SecurityException {
        return execute(clazz, new CheckedSupplier<R>() {
            @Override
            public R get() throws Exception {
                return execute(clazz, fn, JcaTemplate.this.provider);
            }
        });
    }

    protected <T, R> R fallback(final Class<T> clazz, final CheckedFunction<T, R> callback) throws SecurityException {
        return execute(clazz, new CheckedSupplier<R>() {
            @Override
            public R get() throws Exception {
                try {
                    return execute(clazz, callback, JcaTemplate.this.provider);

View on GitHub (pinned to fb71496164)

Solutions

  1. Inspect the exception's cause (`e.getCause()`) — it holds the original JCE error (NoSuchAlgorithmException, InvalidKeySpecException, AEADBadTagException, etc.).
  2. Install/verify the required JCE provider (e.g. BouncyCastle) and register it before the operation.
  3. Confirm the algorithm name and key type match what the callback requests.
  4. Upgrade the JDK if the required algorithm is only available in newer versions.

Example fix

// before: no BC provider registered
JweParser p = Jwts.parser().decryptWith(key).build();
// after
Security.addProvider(new BouncyCastleProvider());
JweParser p = Jwts.parser().decryptWith(key).build();
Defensive patterns

Strategy: try-catch

Try / catch

try {
  byte[] out = jcaTemplate.withCipher(cipherCallback);
} catch (SecurityException e) {
  Throwable jceCause = e.getCause(); // real JCE failure (NoSuchAlgorithmException, etc.)
  logger.error("JCE operation failed", jceCause);
}

Prevention

When it happens

Trigger: Any jjwt crypto operation whose underlying JCE call fails: Cipher.doFinal/init throwing (invalid key spec, bad block sizes, AEADBadTagException), KeyFactory.generatePublic/Private with malformed key specs, SecretKeyFactory failures, or a required algorithm/provider missing so NoSuchAlgorithmException occurs.

Common situations: Missing JCE provider for an algorithm (e.g. no provider for a specific EC curve or AES-KW); key bytes that don't match the expected KeySpec; JDK restrictions or a typo'd algorithm; wrapping a JCE exception whose cause is the real problem.

Related errors


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