Skip to main content

Securing Data with JSON Web Encryption (JWE)

· 10 min read
Wojciech Kotłowski
Senior Technical Writer

JSON-based tokens are a common tool for securely transmitting identity and access data across modern systems. While JSON Web Tokens (JWTs) are frequently signed with JWS (JSON Web Signature), sometimes you need more than integrity—you need confidentiality. That’s where JSON Web Encryption (JWE) comes in.

In this post, we'll explain what JWE is, how it works, and when you should use it. Whether you're building secure APIs or identity systems, understanding JWE is key to safeguarding sensitive information in transit.

What Is JSON Web Encryption (JWE)?

JWE is a standard defined by the IETF as part of the JOSE (JSON Object Signing and Encryption) family. It provides a way to encrypt content—such as JWTs—as compact, URL-safe strings.

Unlike JWS, which signs data to prove it hasn’t been altered, JWE encrypts data to ensure it can only be read by an intended recipient.

In short:

  • JWS = Signature only (integrity and authenticity)

  • JWE = Encryption (confidentiality)

How JWE Encrypted Token Looks Like

A JWE token has five Base64URL-encoded parts, separated by dots:

<Protected Header>.<Encrypted Key>.<Initialization Vector>.<Ciphertext>.<Authentication Tag>
ComponentDescription
Protected HeaderContains metadata about the encryption algorithm and keys (e.g., alg, enc, kid).
Encrypted KeyThe encrypted content encryption key (CEK), which is used to encrypt the payload.
Initialization Vector (IV)A random value used in block cipher algorithms to ensure uniqueness.
CiphertextThe actual encrypted data (e.g., user claims or session info).
Authentication TagUsed to verify integrity and prevent tampering with the encrypted message.

Key Exchange

JWE is a general-purpose JSON-based encryption standard that can be used in any direction or context where secure message confidentiality and integrity are needed. For example:

  • Client-to-Server Encryption:

    Clients can encrypt sensitive request payloads (e.g., API requests, JAR—JWT Secured Authorization Request objects) using the server’s public key. This protects sensitive data in transit and ensures only the server can decrypt it.

  • Server-to-Client Encryption:

    Before any encryption happens, the authorization server (AS) must have access to the public encryption key of the client (often a JSON Web Key – JWK – registered during dynamic or manual client registration).

    This key is used to encrypt the content so that only the private key holder (the client app) can decrypt it.

  • Peer-to-Peer or Multi-Party Encryption

    JWE supports encrypting data to multiple recipients, enabling complex trust models.

While the below examples focuse on the authorization server encrypting tokens for the client, JWE is a versatile standard that enables encryption in both directions.

→ Lock down your Authorization Requests and Responses with JAR/JARM + JWE: signed and encrypted JWTs.

Token Creation (by Authorization Server)

The AS does the following:

  1. Generates a random Content Encryption Key (CEK) – a symmetric key used to encrypt the payload.

  2. Encrypts the CEK using the client’s public key.

  3. Encrypts the payload (e.g., JWT claims) using the CEK and the encryption algorithm (e.g., A256GCM).

  4. Creates the Authentication Tag to ensure integrity.

  5. Builds the JWE.


The result is a compact JWE string sent back to the client.

Token Decryption (by the Client)

The client receives the JWE and:

  1. Uses its private key to decrypt the encrypted CEK.

  2. Uses the CEK to decrypt the ciphertext (payload).

  3. Verifies the Authentication Tag to confirm the message wasn't tampered with.


If everything checks out, the client now has access to the original claims.

Encrypt and Decrypt JSON Web Tokens: Example

This guide walks you through a complete Java project setup for encrypting and decrypting JWTs as JWEs using RSA key pairs. You'll learn how to load keys from .key and .pem files, use the jose4j library for cryptographic operations, and run the example end-to-end.

We’ll cover the prerequisites, project structure, sample pom.xml, and a Java class that handles both encryption and decryption. Finally, you’ll see how to compile and run the code to verify the results.

Prerequisites

  • Make sure you have Java JDK 8 or higher installed.

  • Use an IDE like IntelliJ IDEA, Eclipse, or a simple text editor with command-line tools.

  • Make sure Maven or Gradle is installed if you want to manage dependencies easily.

Sample Project Structure

jwe-rsa-example/
├── pom.xml
└── src/
└── main/
└── java/
└── org/
└── yourcompany/
└── yourproject/
└── JweWithRsaKeyFiles.java

Sample pom.xml

Add the jose4j dependency and configure Java version:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>
<groupId>org.yourcompany.yourproject</groupId>
<artifactId>jwe-test-project</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>24</maven.compiler.release>
<exec.mainClass>org.yourcompany.yourproject.JweWithRsaKeyFiles</exec.mainClass>
</properties>

<dependencies>
<dependency>
<groupId>org.bitbucket.b_c</groupId>
<artifactId>jose4j</artifactId>
<version>0.9.3</version>
</dependency>
</dependencies>

</project>

Sample Java Code to Encrypt and Decrypt Tokens

Place this file under src/main/java/org/yourcompany/yourproject/

package org.yourcompany.yourproject;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import org.jose4j.jwe.ContentEncryptionAlgorithmIdentifiers;
import org.jose4j.jwe.JsonWebEncryption;
import org.jose4j.jwe.KeyManagementAlgorithmIdentifiers;

public class JweWithRsaKeyFiles {

private static byte[] readPemFile(String filepath) throws Exception {
String pem = new String(Files.readAllBytes(Paths.get(filepath)));
// Remove all PEM header/footer lines (any line starting with -----BEGIN or -----END)
pem = pem.replaceAll("-----BEGIN[^-]*-----", "")
.replaceAll("-----END[^-]*-----", "");
// Remove all whitespace (spaces, tabs, newlines)
pem = pem.replaceAll("\\s+", "");
return Base64.getDecoder().decode(pem);
}

private static PrivateKey loadPrivateKey(String filepath) throws Exception {
byte[] keyBytes = readPemFile(filepath);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}

// Load PublicKey from PEM .pem or .crt file (X.509 format)
private static PublicKey loadPublicKey(String filepath) throws Exception {
byte[] keyBytes = readPemFile(filepath);
// If you use a certificate file (.crt), replace "PUBLIC KEY" with "CERTIFICATE" here and parse accordingly
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}

public static void main(String[] args) throws Exception {
String privateKeyPath = "src/main/resources/private_key.pem";
String publicKeyPath = "src/main/resources/public_key.pem";

PrivateKey privateKey = loadPrivateKey(privateKeyPath);
PublicKey publicKey = loadPublicKey(publicKeyPath);

JsonWebEncryption jwe = new JsonWebEncryption();
jwe.setPayload("{\"sub\":\"user123\",\"name\":\"John Doe\",\"iat\":1516239022}");

// Use correct identifiers
jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.RSA_OAEP_256);
jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_256_GCM);

jwe.setKey(publicKey);
String jweString = jwe.getCompactSerialization();
System.out.println("Encrypted: " + jweString);

JsonWebEncryption jweDecrypter = new JsonWebEncryption();
jweDecrypter.setCompactSerialization(jweString);
jweDecrypter.setKey(privateKey);
System.out.println("Decrypted: " + jweDecrypter.getPayload());
}
}

Key Files

Security Disclaimer

Storing private keys as plain files within your source code or project directories is NOT recommended for production environments. This approach is only suitable for development, testing, or proof-of-concept purposes.


For production, always use secure key storage solutions such as:

  • Hardware Security Modules (HSMs)

  • Cloud Key Management Services (KMS) like AWS KMS, Azure Key Vault, Google Cloud KMS

  • Dedicated secrets management tools (e.g., HashiCorp Vault)

Place your RSA keys inside src/main/resources/:

  • private_key.pem — your RSA private key in PKCS#8 PEM format:
-----BEGIN PRIVATE KEY-----
(base64 encoded data)
-----END PRIVATE KEY-----
  • public_key.pem — your RSA public key in PEM format:
-----BEGIN PUBLIC KEY-----
(base64 encoded data)
-----END PUBLIC KEY-----

If you have a certificate (.crt), you can extract the public key from it or adjust the code to parse the certificate.

Need keys?

Request Raidiam Sandbox Access in order to get the necessary key pair/certificate for encryption.


You can explore the sandbox and start securing the data you share with other organizations and/or internally.

Build and Run

From the project root, run:

mvn clean compile exec:java

You should see output similar to:

Encrypted: eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0.I4yh2myqKvfblGzOccK2kttnQwz9mqkPLBOfnGmCSAziPHzNYvrtY9widb5xiZ7Y75eLLRwsK21Ox63BhpgCQo283xkzzr6UM2AmrPKe1SeDV9lLVG4Mk8ktowANqwGkxiRCMSZQCwLp3OXKy1LrKcagUWFeflW7CogyeVBAoREzckgzx5ghUf23QA_FYtOAzX4wwhrzq4_6SJVwGBysRF1_7XZ6fq3GuSQIRYiLcf9KRLBTpx3R9ozf2X_7ggmjRpYrFsmWimtqmRGLr0IukdeLdKUony60as5eJwowjQvItOz3ZRu9uKrc8-Nhyg6sHmq8SAVMm6x6lCr2nsQViQ.5e1orB404NY3aCs2.2h9IZ2s7J8g-JuSBksQ2effPOJt0OOKxVvTH0rCPA0_TeET3YHJroVPzE8wT60OckfGEzg.ZgsFO2DJ8BNwqqyeD-iiRQ

Decrypted: {"sub":"user123","name":"John Doe","iat":1516239022}

Encrypting and Decrypting Tokens: Summary

  • You have a Maven project with jose4j dependency.

  • The Java code loads RSA keys from PEM files.

  • It encrypts a JWT payload into a JWE using RSA-OAEP-256 + AES-GCM.

  • It decrypts the JWE back to the original payload.

  • You can replace the payload with your actual JWT claims.

JWE vs. JWS Only: Key Differences

FeatureJWE (Encryption)JWS (Signature)
PurposeConfidentiality (encrypt data)Integrity (sign data)
Readable?No (encrypted)Yes (plain payload)
Used ForSensitive token contentsToken integrity & validation
Key TypePublic/private or shared secretSigning key pair or HMAC
VerificationRequires decryptionCan be verified without secrets

Common Algorithms in JWE

Key Management (alg)

  • RSA-OAEP, RSA-OAEP-256 (asymmetric)

  • ECDH-ES, ECDH-ES+A256KW (ephemeral Diffie-Hellman)

  • A128KW, A256GCMKW (symmetric key wrapping)

Content Encryption (enc)

  • A128CBC-HS256

  • A256GCM (recommended for most cases)

JWE in the Real World

JSON Web Encryption is used across many real-world systems where protecting the confidentiality of token data is critical. Here are some common scenarios:

OpenID Connect

In identity systems, ID tokens often contain personally identifiable information (PII) such as user names, emails, or authentication context. Encrypting these tokens ensures that only the intended recipient — typically the client — can access the identity details, preventing leaks during transmission or via intermediary systems.

OAuth 2.0

Access tokens, authorization codes, and request objects can carry sensitive claims or scopes that determine what actions a client can perform. Encrypting these elements ensures they can’t be intercepted or manipulated by unauthorized parties. It also safeguards internal business logic or application-specific claims that might otherwise expose implementation details.

Financial-grade APIs (FAPI)

In high-assurance financial ecosystems, such as those adhering to FAPI standards, encryption is mandatory for transmitting sensitive account or transaction data. JWE ensures confidentiality when tokens or request objects move between banks, payment providers, and third-party apps, even if the transport layer is compromised.

Enterprise APIs

In internal or partner-integrated systems, APIs may share confidential user, business, or operational data via tokens. Encrypting this data with JWE protects against unauthorized access — not only from external attackers but also from internal actors or systems that should not be able to inspect the payload.

Best Pracitices Around JWEs

Using JWE securely means following best practices:

  • Use strong algorithms like RSA-OAEP and A256GCM

  • Rotate encryption keys regularly

  • Store private keys securely (e.g., in a KMS or HSM)

  • Don’t skip TLS—JWE complements HTTPS, not replaces it

  • Beware of key reuse and algorithm downgrade attacks

Why Encrypt Your Tokens?

Signing tokens using JWS is an essential security measure—it ensures the token hasn’t been tampered with and verifies its authenticity. But in today’s threat landscape, integrity is only part of the story. If your tokens contain sensitive information such as personally identifiable data (PII), access scopes, roles, or internal claims, leaving them unencrypted exposes that data to unnecessary risk.

Every hop your token takes—through networks, proxies, intermediaries, or logging systems—presents an opportunity for exposure. One weak link is all it takes for private data to leak, even if your signature remains valid.

Meanwhile, the environment around APIs is becoming more hostile. Token interception and inspection are no longer theoretical risks—they’re active attack vectors. Regulatory expectations are rising too. Compliance with privacy standards like GDPR, HIPAA, and PSD2 increasingly hinges on the ability to demonstrate that sensitive data is adequately protected in transit and at rest.

In architectures built around zero-trust principles, encryption is not optional. It ensures that token contents remain confidential even in untrusted or partially trusted environments. And as breaches continue to make headlines and damage reputations, encryption becomes a critical layer of defense—one that could prevent costly incidents before they happen.

If you're already signing tokens, you're halfway there. But to fully secure your system and meet modern security demands, the next step is clear: encrypt your tokens using JWE.

To learn more about the current state of API security and best practices for hardening your integrations, download our comprehensive API Security Report.

API Security Report

Helping Enterprises Recognize and Address Critical Risks

More than 80% of organizations are exposing sensitive data with weak API security

Download Now →