jwtk/jjwt · error · MalformedKeyException

Invalid Secret JWK value ' '. Secret JWKs may only be used…

Error message

Invalid Secret JWK ${AbstractJwk.ALG} value '${alg.getId()}'. Secret JWKs may only be used with symmetric (secret) key algorithms.

What it means

Thrown as MalformedKeyException when a SecretJwk declares an 'alg' that is not a symmetric algorithm (MAC, secret-key encryption, or AEAD). Secret JWKs represent symmetric key material and can only be associated with symmetric algorithms.

Solutions

  1. Set 'alg' to a symmetric algorithm (HS256/HS384/HS512, AxxxKW key-wrap, or GCM AEAD algorithms).
  2. If the key is asymmetric, build the correct JWK type (EC or RSA JWK) instead of a SecretJwk.
  3. Validate the alg value against Jwts.SIG / Jwts.ENC registry before building the JWK.

Example fix

// before
values.put("kty", "oct");
values.put("alg", "RS256"); // asymmetric alg on secret JWK
// after
values.put("kty", "oct");
values.put("alg", "HS256");
Defensive patterns

Strategy: validation

Validate before calling

Object alg = Jwts.SIG.get().forId(algId);
if (!(alg instanceof MacAlgorithm || alg instanceof SecretKeyAlgorithm || alg instanceof AeadAlgorithm)) throw new IllegalStateException("Secret JWK needs a symmetric alg");

Type guard

boolean symmetricAlg(Object a) { return a instanceof MacAlgorithm || a instanceof SecretKeyAlgorithm || a instanceof AeadAlgorithm; }

Try / catch

try { /* build SecretJwk */ } catch (MalformedKeyException e) { throw new IllegalArgumentException("Use HS*/A*KW/GCM algs for oct (secret) JWKs", e); }

Prevention

When it happens

Trigger: Building a SecretJwk from values with 'alg' set to an asymmetric algorithm such as RS256, ES256, or RSA-OAEP instead of HS256/A128KW/A256GCM etc.

Common situations: Copy-pasting JWK templates across key types; misconfiguring algorithm headers after switching key material; typos or mixing up JWK 'kty' (oct) with 'alg' families.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

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

            // Implementors note:  Don't print out any information about the `bytes` value itself - size,
            // content, etc., as it is considered secret material:
            String msg = "Secret JWK " + AbstractJwk.ALG + " value is '" + alg.getId() +
                    "', but the " + DefaultSecretJwk.K + " length is smaller than the " + alg.getId() +
                    " minimum length of " + Bytes.bitsMsg(requiredBitLen) +
                    " required by " +
                    "[JWA RFC 7518, Section 3.2](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.2), " +
                    "2nd paragraph: 'A key of the same size as the hash output or larger MUST be used with this " +
                    "algorithm.'";
            throw new WeakKeyException(msg);
        }
    }

    private static void assertSymmetric(Identifiable alg) {
        if (alg instanceof MacAlgorithm || alg instanceof SecretKeyAlgorithm || alg instanceof AeadAlgorithm)
            return; // valid
        String msg = "Invalid Secret JWK " + AbstractJwk.ALG + " value '" + alg.getId() + "'. Secret JWKs " +
                "may only be used with symmetric (secret) key algorithms.";
        throw new MalformedKeyException(msg);
    }

    @Override
    protected SecretJwk createJwkFromValues(JwkContext<SecretKey> ctx) {
        ParameterReadable reader = new RequiredParameterReader(ctx);
        final byte[] bytes = reader.get(DefaultSecretJwk.K);
        SecretKey key;

        String algId = ctx.getAlgorithm();
        if (!Strings.hasText(algId)) { // optional per https://www.rfc-editor.org/rfc/rfc7517.html#section-4.4

            // Here we try to infer the best type of key to create based on siguse and/or key length.
            //
            // AES requires 128, 192, or 256 bits, so anything larger than 256 cannot be AES, so we'll need to assume
            // HMAC.
            //
            // Also, 256 bits works for either HMAC or AES, so we just have to choose one as there is no other
            // RFC-based criteria for determining.  Historically, we've chosen AES due to the larger number of

View on GitHub (pinned to fb71496164)