jwtk/jjwt · error · MalformedJwtException

Compact JWE string represents an encrypted key, but the key

Error message

Compact JWE string represents an encrypted key, but the key is empty.

What it means

During compact JWE parsing, the parser decodes the base64url 'encrypted key' segment and requires it to decode to at least one byte. A present-but-empty encrypted key segment means the JWE structure is corrupt or was generated by a broken producer, so the library throws MalformedJwtException rather than attempting key-wrap decryption.

Source

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

        if (tokenized instanceof TokenizedJwe) {

            TokenizedJwe tokenizedJwe = (TokenizedJwe) tokenized;
            JweHeader jweHeader = Assert.stateIsInstance(JweHeader.class, header, "Not a JweHeader. ");

            // Ensure both an 'alg' and 'enc' header value exists and is supported before spending time/effort
            // base64Url-decoding anything:
            final AeadAlgorithm encAlg = this.encAlgs.apply(jweHeader);
            Assert.stateNotNull(encAlg, "JWE Encryption Algorithm cannot be null.");
            @SuppressWarnings("rawtypes") final KeyAlgorithm keyAlg = this.keyAlgs.apply(jweHeader);
            Assert.stateNotNull(keyAlg, "JWE Key Algorithm cannot be null.");

            byte[] cekBytes = Bytes.EMPTY; //ignored unless using an encrypted key algorithm
            CharSequence base64Url = tokenizedJwe.getEncryptedKey();
            if (Strings.hasText(base64Url)) {
                cekBytes = decode(base64Url, "JWE encrypted key");
                if (Bytes.isEmpty(cekBytes)) {
                    String msg = "Compact JWE string represents an encrypted key, but the key is empty.";
                    throw new MalformedJwtException(msg);
                }
            }

            base64Url = tokenizedJwe.getIv();
            if (Strings.hasText(base64Url)) {
                iv = decode(base64Url, "JWE Initialization Vector");
            }
            if (Bytes.isEmpty(iv)) {
                String msg = "Compact JWE strings must always contain an Initialization Vector.";
                throw new MalformedJwtException(msg);
            }

            // The AAD (Additional Authenticated Data) scheme for compact JWEs is to use the ASCII bytes of the
            // raw base64url text as the AAD, and NOT the base64url-decoded bytes per
            // https://www.rfc-editor.org/rfc/rfc7516.html#section-5.1, Step 14.
            ByteBuffer buf = StandardCharsets.US_ASCII.encode(Strings.wrap(base64UrlHeader));
            final byte[] aadBytes = new byte[buf.remaining()];
            buf.get(aadBytes);

View on GitHub (pinned to fb71496164)

Solutions

  1. Regenerate the JWE with a correct producer (ensure KeyAlgorithm writes a non-empty encrypted key for the configured alg).
  2. Verify the token wasn't truncated or altered in transit; compare against the original issuer output.
  3. Inspect the 4th component of the compact string and confirm it is valid non-empty base64url for the key-encryption mode.
  4. If using dir or direct CEK mode, expect no encrypted key segment at all — the string should have an empty 4th part, not whitespace or junk.

Example fix

// before
String jwe = header + "." + iv + "." + ciphertext + "." + ""; // empty encrypted key
// after
String jwe = header + "." + encryptedKey + "." + iv + "." + ciphertext + "." + tag;
Defensive patterns

Strategy: validation

Validate before calling

String[] parts = jwe.split("\\.", -1);
if (parts.length == 5 && !parts[1].isEmpty()) {
    byte[] ek = java.util.Base64.getUrlDecoder().decode(pad(parts[1]));
    if (ek.length == 0) throw new IllegalArgumentException("empty JWE encrypted key");
}

Try / catch

try { parser.parse(jwe); } catch (MalformedJwtException e) { log.warn("Corrupt JWE encrypted key: {}", e.getMessage()); throw new TokenFormatException(e); }

Prevention

When it happens

Trigger: Parsing a compact JWE whose fourth dot-separated component is non-empty base64url text that decodes to zero bytes (e.g. via JwtParser.parseClaimsJws/parse on a JWE string).

Common situations: Truncated or hand-edited JWE strings, buggy custom JWE builders that emit an empty key segment, round-tripping tokens through systems that mangle base64url padding.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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