Understanding JSON Web Tokens: encrypting with JWE

A signed token is readable by anyone holding it. When that is unacceptable, RFC 7516 defines JSON Web Encryption.

Five parts, not three

header . encrypted_key . iv . ciphertext . authentication_tag

The extra parts exist because JWE uses two keys, not one. A fresh symmetric key, the Content Encryption Key, encrypts the payload. That key is itself encrypted for the recipient, and the result is the second part of the token.

This is not a complication for its own sake. Asymmetric cryptography is slow and limited in the size it can handle. Encrypting a short random key with RSA and the payload with AES is both faster and unbounded in length.

Two algorithms to declare

  • alg: how the content encryption key is handled. RSA-OAEP-256, ECDH-ES, A256KW, dir.
  • enc: how the payload itself is encrypted. A128CBC-HS256, A256GCM.

Both are mandatory and both are in the protected header. Get one of them wrong and the token will not decrypt, which is the good failure mode.

The authentication tag is not optional

Every content encryption algorithm in JWE is authenticated: it produces a tag that proves the ciphertext has not been altered. Encryption without authentication lets an attacker flip bits in your payload and see what happens.

That is not an abstract concern. An experimental algorithm in this very library generated its tag and then discarded it, silently degrading an authenticated construction to an unauthenticated one.

What encryption does not give you

A JWE proves nothing about who produced it. Anyone with your public key can encrypt something for you. If you need to know the sender, you need a signature as well, and the order in which you combine them matters.