Skip to content

Encrypted tokens (JWE) examples

Encrypt a token with RSA-OAEP

RSA-OAEP is widely supported. The sender encrypts with the recipient's public key; only the recipient can decrypt with their private key.

<?php

use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Encryption\Algorithm\ContentEncryption\A256GCM;
use Jose\Component\Encryption\Algorithm\KeyEncryption\RSAOAEP256;
use Jose\Component\Encryption\JWEBuilder;
use Jose\Component\Encryption\Serializer\CompactSerializer;
use Jose\Component\KeyManagement\JWKFactory;

require_once 'vendor/autoload.php';

// Generate an RSA key pair (2048-bit minimum)
$jwkFactory = new JWKFactory();
$privateKey = $jwkFactory->rsa(2048, ['alg' => 'RSA-OAEP-256', 'use' => 'enc']);
$publicKey = $privateKey->toPublic();

$algorithmManager = new AlgorithmManager([
    new RSAOAEP256(),
    new A256GCM(),
]);
$jweBuilder = new JWEBuilder($algorithmManager);

$payload = json_encode([
    'iss' => 'https://auth.example.com',
    'sub' => 'user-42',
    'iat' => time(),
    'exp' => time() + 3600,
    'email' => 'user@example.com',
]);

$jwe = $jweBuilder
    ->withPayload($payload)
    ->withSharedProtectedHeader([
        'alg' => 'RSA-OAEP-256',
        'enc' => 'A256GCM',
    ])
    ->addRecipient($publicKey)
    ->build();

$token = (new CompactSerializer())->serialize($jwe, 0);

Decrypt the token

<?php

use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Encryption\Algorithm\ContentEncryption\A256GCM;
use Jose\Component\Encryption\Algorithm\KeyEncryption\RSAOAEP256;
use Jose\Component\Encryption\JWEDecrypter;
use Jose\Component\Encryption\Serializer\CompactSerializer;

require_once 'vendor/autoload.php';

$algorithmManager = new AlgorithmManager([
    new RSAOAEP256(),
    new A256GCM(),
]);
$jweDecrypter = new JWEDecrypter($algorithmManager);

$jwe = (new CompactSerializer())->unserialize($token);

$jwe = $jweDecrypter->decrypt($jwe, $privateKey, 0)->getJwe();

$payload = json_decode($jwe->getPayload(), true);
// ['iss' => 'https://auth.example.com', 'sub' => 'user-42', ...]

Encrypt a token with ECDH-ES (Elliptic curve)

ECDH-ES performs a key agreement directly, producing smaller tokens than RSA.

<?php

use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Encryption\Algorithm\ContentEncryption\A128GCM;
use Jose\Component\Encryption\Algorithm\KeyEncryption\ECDHES;
use Jose\Component\Encryption\JWEBuilder;
use Jose\Component\Encryption\Serializer\CompactSerializer;
use Jose\Component\KeyManagement\JWKFactory;

require_once 'vendor/autoload.php';

// Generate an EC key pair on the P-256 curve
$jwkFactory = new JWKFactory();
$privateKey = $jwkFactory->ec('P-256', ['alg' => 'ECDH-ES', 'use' => 'enc']);
$publicKey = $privateKey->toPublic();

$algorithmManager = new AlgorithmManager([new ECDHES(), new A128GCM()]);
$jweBuilder = new JWEBuilder($algorithmManager);

$payload = json_encode([
    'iss' => 'https://auth.example.com',
    'sub' => 'user-42',
    'iat' => time(),
    'exp' => time() + 3600,
]);

$jwe = $jweBuilder
    ->withPayload($payload)
    ->withSharedProtectedHeader(['alg' => 'ECDH-ES', 'enc' => 'A128GCM'])
    ->addRecipient($publicKey)
    ->build();

$token = (new CompactSerializer())->serialize($jwe, 0);

Decrypt the token

<?php

use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Encryption\Algorithm\ContentEncryption\A128GCM;
use Jose\Component\Encryption\Algorithm\KeyEncryption\ECDHES;
use Jose\Component\Encryption\JWEDecrypter;
use Jose\Component\Encryption\Serializer\CompactSerializer;

require_once 'vendor/autoload.php';

$algorithmManager = new AlgorithmManager([new ECDHES(), new A128GCM()]);
$jweDecrypter = new JWEDecrypter($algorithmManager);

$jwe = (new CompactSerializer())->unserialize($token);

$jwe = $jweDecrypter->decrypt($jwe, $privateKey, 0)->getJwe();

$payload = json_decode($jwe->getPayload(), true);

Encrypt a token with a password (PBES2)

Password-based encryption is useful when sharing tokens with users who only have a passphrase.

Warning

spomky-labs/aes-key-wrap is required for PBES2 algorithms:

composer require spomky-labs/aes-key-wrap

<?php

use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Encryption\Algorithm\ContentEncryption\A256CBCHS512;
use Jose\Component\Encryption\Algorithm\KeyEncryption\PBES2HS512A256KW;
use Jose\Component\Encryption\JWEBuilder;
use Jose\Component\Encryption\Serializer\CompactSerializer;
use Jose\Component\KeyManagement\JWKFactory;

require_once 'vendor/autoload.php';

// Create a key from a password
$password = 'my-super-secret-password';
$jwkFactory = new JWKFactory();
$jwk = $jwkFactory->fromSecret($password, ['alg' => 'PBES2-HS512+A256KW', 'use' => 'enc']);

$algorithmManager = new AlgorithmManager([
    new PBES2HS512A256KW(),
    new A256CBCHS512(),
]);
$jweBuilder = new JWEBuilder($algorithmManager);

$payload = json_encode([
    'iss' => 'https://auth.example.com',
    'sub' => 'user-42',
    'data' => 'confidential information',
]);

$jwe = $jweBuilder
    ->withPayload($payload)
    ->withSharedProtectedHeader([
        'alg' => 'PBES2-HS512+A256KW',
        'enc' => 'A256CBC-HS512',
    ])
    ->addRecipient($jwk)
    ->build();

$token = (new CompactSerializer())->serialize($jwe, 0);

Decrypt with the same password

<?php

use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Encryption\Algorithm\ContentEncryption\A256CBCHS512;
use Jose\Component\Encryption\Algorithm\KeyEncryption\PBES2HS512A256KW;
use Jose\Component\Encryption\JWEDecrypter;
use Jose\Component\Encryption\Serializer\CompactSerializer;
use Jose\Component\KeyManagement\JWKFactory;

require_once 'vendor/autoload.php';

$jwkFactory = new JWKFactory();
$jwk = $jwkFactory->fromSecret('my-super-secret-password', [
    'alg' => 'PBES2-HS512+A256KW',
    'use' => 'enc',
]);

$algorithmManager = new AlgorithmManager([
    new PBES2HS512A256KW(),
    new A256CBCHS512(),
]);
$jweDecrypter = new JWEDecrypter($algorithmManager);

$jwe = (new CompactSerializer())->unserialize($token);

$jwe = $jweDecrypter->decrypt($jwe, $jwk, 0)->getJwe();

$payload = json_decode($jwe->getPayload(), true);

Encrypt a token with a shared key (A256KW)

AES Key Wrap uses a symmetric key, useful for server-to-server communication.

Warning

spomky-labs/aes-key-wrap is required:

composer require spomky-labs/aes-key-wrap

<?php

use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Encryption\Algorithm\ContentEncryption\A256GCM;
use Jose\Component\Encryption\Algorithm\KeyEncryption\A256KW;
use Jose\Component\Encryption\JWEBuilder;
use Jose\Component\Encryption\Serializer\CompactSerializer;
use Jose\Component\KeyManagement\JWKFactory;

require_once 'vendor/autoload.php';

// Generate a 256-bit symmetric key
$jwkFactory = new JWKFactory();
$jwk = $jwkFactory->oct(256, ['alg' => 'A256KW', 'use' => 'enc']);

$algorithmManager = new AlgorithmManager([new A256KW(), new A256GCM()]);
$jweBuilder = new JWEBuilder($algorithmManager);

$payload = json_encode([
    'iss' => 'https://service-a.example.com',
    'aud' => 'https://service-b.example.com',
    'data' => ['key' => 'value'],
]);

$jwe = $jweBuilder
    ->withPayload($payload)
    ->withSharedProtectedHeader(['alg' => 'A256KW', 'enc' => 'A256GCM'])
    ->addRecipient($jwk)
    ->build();

$token = (new CompactSerializer())->serialize($jwe, 0);

// Decryption uses the same key
$jweDecrypter = new \Jose\Component\Encryption\JWEDecrypter($algorithmManager);
$jwe = (new CompactSerializer())->unserialize($token);
$jwe = $jweDecrypter->decrypt($jwe, $jwk, 0)->getJwe();
$payload = json_decode($jwe->getPayload(), true);

The JWELoader combines deserialization, header checking, and decryption in a single step.

<?php

use Jose\Component\Checker\AlgorithmChecker;
use Jose\Component\Checker\HeaderCheckerManager;
use Jose\Component\Checker\IsEqualChecker;
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Encryption\Algorithm\ContentEncryption\A256GCM;
use Jose\Component\Encryption\Algorithm\KeyEncryption\RSAOAEP256;
use Jose\Component\Encryption\JWEDecrypter;
use Jose\Component\Encryption\JWELoader;
use Jose\Component\Encryption\JWETokenSupport;
use Jose\Component\Encryption\Serializer\CompactSerializer;
use Jose\Component\Encryption\Serializer\JWESerializerManager;

require_once 'vendor/autoload.php';

$privateKey = /* your RSA private key */;

$algorithmManager = new AlgorithmManager([new RSAOAEP256(), new A256GCM()]);

$jweLoader = new JWELoader(
    new JWESerializerManager([new CompactSerializer()]),
    new JWEDecrypter($algorithmManager),
    new HeaderCheckerManager(
        [
            new AlgorithmChecker(['RSA-OAEP-256']),
            new IsEqualChecker('enc', 'A256GCM'),
        ],
        [new JWETokenSupport()]
    )
);

$jwe = $jweLoader->loadAndDecrypt($token, $privateKey)->getJwe();

$payload = json_decode($jwe->getPayload(), true);

Info

After decryption, you should also validate the claims (exp, iss, aud, etc.) contained in the payload. See the Claim and Header Validation page.