jwtk/jjwt · error · InvalidKeyException

Unable to encode SecretKey to JWK

Error message

Unable to encode SecretKey to JWK: ${t.getMessage()}

What it means

Thrown as InvalidKeyException when a SecretKey cannot be encoded to bytes (KeysBridge.getEncoded) and Base64URL-encoded into the JWK 'k' parameter while building a SecretJwk. The original throwable's message is included as the cause.

Solutions

  1. Use an exportable SecretKey (e.g. generated with Keys.secretKeyFor or SecretKeySpec) when building a JWK.
  2. If the key lives in an HSM/keystore, do not build a JWK from it; reference it by key ID instead of embedding key material.
  3. Check the cause for the exact provider failure and ensure the key is not destroyed or cleared before JWK creation.

Example fix

// before
SecretKey key = keystore.getKey(alias, null); // non-exportable HSM key
Jwk jwk = Jwts.builder().keys().build(key);
// after
SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256); // exportable
Jwk jwk = Jwts.builder().keys().build(key);
Defensive patterns

Strategy: validation

Validate before calling

byte[] encoded = secretKey.getEncoded();
if (encoded == null || encoded.length == 0) throw new IllegalStateException("SecretKey is not exportable");

Type guard

boolean isExportableSecretKey(SecretKey k) { return k.getEncoded() != null && k.getEncoded().length > 0; }

Try / catch

try { /* build SecretJwk */ } catch (InvalidKeyException e) { throw new IllegalStateException("Key not exportable; use an in-memory SecretKey", e); }

Prevention

When it happens

Trigger: Building a JWK from a SecretKey whose getEncoded() returns null or fails, e.g. hardware/PKCS11-backed or destroyable keys, or keys from providers that refuse export when calling JwkBuilder/JWK creation APIs.

Common situations: Using keys stored in an HSM/keystore that prohibit key export; keys already destroyed (DestroyFailedException paths); platform-specific providers (Android Keystore) returning null encodings.

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/513acdf464243212. Report an issue: GitHub.

Appendix: source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/SecretJwkFactory.java:58

 */
class SecretJwkFactory extends AbstractFamilyJwkFactory<SecretKey, SecretJwk> {

    SecretJwkFactory() {
        super(DefaultSecretJwk.TYPE_VALUE, SecretKey.class, DefaultSecretJwk.PARAMS);
    }

    @Override
    protected SecretJwk createJwkFromKey(JwkContext<SecretKey> ctx) {
        SecretKey key = Assert.notNull(ctx.getKey(), "JwkContext key cannot be null.");
        String k;
        byte[] encoded = null;
        try {
            encoded = KeysBridge.getEncoded(key);
            k = Encoders.BASE64URL.encode(encoded);
            Assert.hasText(k, "k value cannot be null or empty.");
        } catch (Throwable t) {
            String msg = "Unable to encode SecretKey to JWK: " + t.getMessage();
            throw new InvalidKeyException(msg, t);
        } finally {
            Bytes.clear(encoded);
        }

        MacAlgorithm mac = DefaultMacAlgorithm.findByKey(key);
        if (mac != null) {
            ctx.put(AbstractJwk.ALG.getId(), mac.getId());
        }

        ctx.put(DefaultSecretJwk.K.getId(), k);

        return createJwkFromValues(ctx);
    }

    private static void assertKeyBitLength(byte[] bytes, MacAlgorithm alg) {
        long bitLen = Bytes.bitLength(bytes);
        long requiredBitLen = alg.getKeyBitLength();
        if (bitLen < requiredBitLen) {

View on GitHub (pinned to fb71496164)