jwtk/jjwt · error · MalformedJwtException

JWEs do not support key management alg header value 'none' p

Error message

JWEs do not support key management alg header value 'none' per https://www.rfc-editor.org/rfc/rfc7518.html#section-4.1

What it means

jjwt rejects JWE compact strings whose key management 'alg' header is 'none'. Per RFC 7518 Section 4.1, JWE key management algorithms never include 'none' — unsecured (algorithm-none) tokens are only valid as JWS, never as encrypted JWEs. The parser detects a TokenizedJwe whose alg equals 'none' and throws MalformedJwtException.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java:409

        // https://tools.ietf.org/html/rfc7515#section-10.7 , second-to-last bullet point, note the use of 'always':
        //
        //   *  Require that the "alg" Header Parameter be carried in the JWS
        //      Protected Header.  (This is always the case when using the JWS
        //      Compact Serialization and is the approach taken by CMS [RFC6211].)
        //
        final String alg = Strings.clean(header.getAlgorithm());
        if (!Strings.hasText(alg)) {
            String msg = tokenized instanceof TokenizedJwe ? MISSING_JWE_ALG_MSG : MISSING_JWS_ALG_MSG;
            throw new MalformedJwtException(msg);
        }
        final boolean unsecured = Jwts.SIG.NONE.getId().equalsIgnoreCase(alg);

        final CharSequence base64UrlDigest = tokenized.getDigest();
        final boolean hasDigest = Strings.hasText(base64UrlDigest);
        if (unsecured) {
            if (tokenized instanceof TokenizedJwe) {
                throw new MalformedJwtException(JWE_NONE_MSG);
            }
            // Unsecured JWTs are disabled by default per the RFC:
            if (!this.unsecured) {
                String msg = UNSECURED_DISABLED_MSG_PREFIX + header;
                throw new UnsupportedJwtException(msg);
            }
            if (hasDigest) {
                throw new MalformedJwtException(JWS_NONE_SIG_MISMATCH_MSG);
            }
            if (header.containsKey(DefaultProtectedHeader.CRIT.getId())) {
                String msg = String.format(CRIT_UNSECURED_MSG, header);
                throw new MalformedJwtException(msg);
            }
        } else if (!hasDigest) { // something other than 'none'.  Must have a digest component:
            String fmt = tokenized instanceof TokenizedJwe ? MISSING_JWE_DIGEST_MSG_FMT : MISSING_JWS_DIGEST_MSG_FMT;
            String msg = String.format(fmt, alg);
            throw new MalformedJwtException(msg);
        }

View on GitHub (pinned to fb71496164)

Solutions

  1. If the token is meant to be unsecured, emit it as a JWS (3 segments) with alg=none and parse with parser builder configured via unsecured().
  2. If the token is meant to be encrypted, set a real JWE key-management algorithm (e.g. RSA-OAEP-256, A128KW) via Jwts.builder().encryptWith(key, alg, enc).
  3. Verify the token's structure (count dot-separated segments) to confirm whether it is a JWS or JWE and use the appropriate parse method.
  4. Fix the issuing service so it never produces alg=none JWEs.

Example fix

// before: JWE built with alg=none (invalid)
String jwe = Jwts.builder().claims(map)
    .header().add("alg", "none").and() /* ... */ .compact();

// after: proper JWE encryption
String jwe = Jwts.builder().claims(map)
    .encryptWith(secretKey, Jwts.KEY.A128KW, Jwts.ENC.A128GCM)
    .compact();
Defensive patterns

Strategy: validation

Validate before calling

String headerJson = new String(java.util.Base64.getUrlDecoder().decode(token.split("\\.")[0]), java.nio.charset.StandardCharsets.UTF_8);
if (headerJson.contains("alg\":\"none\"") && token.split("\\.").length == 5) throw new IllegalArgumentException("JWE cannot use alg=none");

Try / catch

try { return parser.parse(jwe); }
catch (io.jsonwebtoken.MalformedJwtException e) { throw new InvalidTokenException("JWE declared alg=none", e); }

Prevention

When it happens

Trigger: Calling parse(), parseSignedClaims(), or parseSignedContent() on a JWE compact string (5 segments, with an AAD-auth tag) whose protected header declares "alg":"none".

Common situations: Custom token builders that mistakenly set alg=none on encrypted tokens; confusion between unsecured JWS and JWE formats; hand-rolled JWE serialization copying a JWS 'none' header; misconfigured issuer that disables encryption but still emits JWE structure.

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