Skip to content
jwecodec

JWE code examples

Encrypting claims and decrypting them again with A256KW + A256GCM, using a popular library for each language. They all use the decoder's sample key SdXThyFaH147IZDIp80_ydTFvDzd4TeU9B8hwXqNn0w, so you can paste any token they print straight into the decoder.

JWE in Node.js / JavaScript

Install: npm install jose

jose works in Node.js, browsers, Deno, Bun and edge runtimes.

import { EncryptJWT, jwtDecrypt, base64url } from "jose";

const key = base64url.decode("SdXThyFaH147IZDIp80_ydTFvDzd4TeU9B8hwXqNn0w");

// Encrypt claims
const token = await new EncryptJWT({ name: "Ada Lovelace", admin: true })
  .setProtectedHeader({ alg: "A256KW", enc: "A256GCM", typ: "JWT" })
  .setSubject("1234567890")
  .setIssuedAt()
  .setExpirationTime("1h")
  .encrypt(key);
console.log(token);

// Decrypt (also checks exp/nbf). Allow-list the algorithms you expect.
const { payload, protectedHeader } = await jwtDecrypt(token, key, {
  keyManagementAlgorithms: ["A256KW"],
  contentEncryptionAlgorithms: ["A256GCM"],
});
console.log(protectedHeader.enc, payload.name); // A256GCM Ada Lovelace

JWE in Python

Install: pip install jwcrypto

import json
from jwcrypto import jwe, jwk

key = jwk.JWK(kty="oct", k="SdXThyFaH147IZDIp80_ydTFvDzd4TeU9B8hwXqNn0w")

# Encrypt
token = jwe.JWE(
    json.dumps({"sub": "1234567890", "name": "Ada Lovelace"}).encode(),
    protected={"alg": "A256KW", "enc": "A256GCM", "typ": "JWT"},
)
token.add_recipient(key)
compact = token.serialize(compact=True)
print(compact)

# Decrypt (raises jwcrypto.jwe.InvalidJWEData on a wrong key or a changed token)
received = jwe.JWE()
received.deserialize(compact, key=key)
print(json.loads(received.payload)["name"])  # Ada Lovelace

JWE in Java

Install: com.nimbusds:nimbus-jose-jwt (Maven / Gradle)

import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.AESDecrypter;
import com.nimbusds.jose.crypto.AESEncrypter;
import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jwt.EncryptedJWT;
import com.nimbusds.jwt.JWTClaimsSet;
import java.util.Date;

public class Main {
  public static void main(String[] args) throws Exception {
    byte[] key = new Base64URL("SdXThyFaH147IZDIp80_ydTFvDzd4TeU9B8hwXqNn0w").decode();

    // Encrypt claims
    JWTClaimsSet claims = new JWTClaimsSet.Builder()
        .subject("1234567890")
        .claim("name", "Ada Lovelace")
        .issueTime(new Date())
        .expirationTime(new Date(System.currentTimeMillis() + 3_600_000))
        .build();
    JWEHeader header = new JWEHeader.Builder(JWEAlgorithm.A256KW, EncryptionMethod.A256GCM)
        .type(JOSEObjectType.JWT)
        .build();
    EncryptedJWT jwt = new EncryptedJWT(header, claims);
    jwt.encrypt(new AESEncrypter(key));
    String token = jwt.serialize();
    System.out.println(token);

    // Decrypt (throws JOSEException on a wrong key or a changed token)
    EncryptedJWT parsed = EncryptedJWT.parse(token);
    parsed.decrypt(new AESDecrypter(key));
    System.out.println(parsed.getJWTClaimsSet().getStringClaim("name")); // Ada Lovelace
    // Check exp yourself, or use DefaultJWTProcessor, which does it for you.
  }
}

JWE in C# / .NET

Install: dotnet add package jose-jwt

using System;
using System.Collections.Generic;
using Jose;

byte[] key = Base64Url.Decode("SdXThyFaH147IZDIp80_ydTFvDzd4TeU9B8hwXqNn0w");

// Encrypt claims
var claims = new Dictionary<string, object>
{
    ["sub"] = "1234567890",
    ["name"] = "Ada Lovelace",
    ["exp"] = DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds(),
};
string token = JWT.Encode(claims, key, JweAlgorithm.A256KW, JweEncryption.A256GCM,
    extraHeaders: new Dictionary<string, object> { ["typ"] = "JWT" });
Console.WriteLine(token);

// Decrypt (throws on a wrong key or a changed token). jose-jwt doesn't check exp: do it yourself.
var decoded = JWT.Decode<Dictionary<string, object>>(token, key, JweAlgorithm.A256KW, JweEncryption.A256GCM);
Console.WriteLine(decoded["name"]); // Ada Lovelace

JWE in Go

Install: go get github.com/go-jose/go-jose/v4

package main

import (
	"encoding/base64"
	"fmt"

	"github.com/go-jose/go-jose/v4"
)

func main() {
	key, _ := base64.RawURLEncoding.DecodeString("SdXThyFaH147IZDIp80_ydTFvDzd4TeU9B8hwXqNn0w")

	// Encrypt
	enc, err := jose.NewEncrypter(jose.A256GCM,
		jose.Recipient{Algorithm: jose.A256KW, Key: key},
		(&jose.EncrypterOptions{}).WithType("JWT"))
	if err != nil {
		panic(err)
	}
	obj, err := enc.Encrypt([]byte(`{"sub":"1234567890","name":"Ada Lovelace"}`))
	if err != nil {
		panic(err)
	}
	token, _ := obj.CompactSerialize()
	fmt.Println(token)

	// Decrypt, allowing only the algorithms you expect
	parsed, err := jose.ParseEncryptedCompact(token,
		[]jose.KeyAlgorithm{jose.A256KW}, []jose.ContentEncryption{jose.A256GCM})
	if err != nil {
		panic(err)
	}
	plaintext, err := parsed.Decrypt(key)
	if err != nil {
		panic(err) // wrong key or changed token
	}
	fmt.Println(string(plaintext))
}

JWE in Rust

Install: cargo add josekit base64

josekit uses OpenSSL, so it needs the OpenSSL development headers to build.

use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use josekit::jwe::{JweHeader, A256KW};
use josekit::jwt::{self, JwtPayload};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let key = URL_SAFE_NO_PAD.decode("SdXThyFaH147IZDIp80_ydTFvDzd4TeU9B8hwXqNn0w")?;

    // Encrypt claims
    let mut header = JweHeader::new();
    header.set_token_type("JWT");
    header.set_content_encryption("A256GCM");
    let mut payload = JwtPayload::new();
    payload.set_subject("1234567890");
    payload.set_claim("name", Some("Ada Lovelace".into()))?;
    let token = jwt::encode_with_encrypter(&payload, &header, &A256KW.encrypter_from_bytes(&key)?)?;
    println!("{token}");

    // Decrypt (errors on a wrong key or a changed token)
    let (payload, _header) = jwt::decode_with_decrypter(&token, &A256KW.decrypter_from_bytes(&key)?)?;
    println!("{}", payload.claim("name").unwrap()); // "Ada Lovelace"
    Ok(())
}

Decryption checklist

  • Allow-list the alg and enc values you expect. Never let the token pick.
  • Encryption doesn't prove who made the token. If that matters, sign it first (a nested JWT) and verify the signature.
  • Check exp, nbf, iss and aud after decrypting.
  • Generate keys randomly (32 bytes for A256KW) and keep them out of source control.