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 LovelaceJWE 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 LovelaceJWE 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 LovelaceJWE 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
algandencvalues 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,issandaudafter decrypting. - Generate keys randomly (32 bytes for A256KW) and keep them out of source control.