arrow-left

All pages
gitbookPowered by GitBook
1 of 13

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Advanced Topics

  • Serialization

  • Nested Tokens

  • Custom Algorithm

  • Signed tokens and

  • Encrypted tokens and

Unprotected Header
Multiple Signatures
Detached Payload
Unprotected Header
Multiple Recipients
Unencoded Payload
Additional Authentication Data (AAD)

Unencoded Payload

The RFC7797arrow-up-right allows the use of an unencoded payload for the signed tokens. This behaviour is interesting when your tokens have a detached payload and may reduce the token computation.

Please note that when the Compact Serialization mode is used, the characters of the payload must be limited to the following ASCII ranges:

  • From 0x20 to 0x2d

  • From 0x2f to 0x7e

This feature is built in the framework and is enabled when the b64 header parameter is set to false. As per the RFC, this header MUST be protected and also listed as a critical (crit) header parameter.

Example:

As a remainder, both b64 and crit parameters MUST be in the protected header.

Multiple Signatures

When you need to sign the same payload for several audiences, you may want to do it at once. The JWS Builder supports multiple signatures.

With the example below, we will create three signatures using three different algorithms (and signature keys):

The variable $jws will be a valid JWS object with all computed signatures. Next step is the serialization of these signatures.

$jws = $jwsBuilder
    ->create()
    ->withPayload('Hello World!')
    ->addSignature($jwk, ['alg' => 'HS256', 'b64' => false, 'crit' => ['b64']])
    ->build();
$jws = $jwsBuilder
    ->create()
    ->withPayload('...')
    ->addSignature($signature_key1, ['alg' => 'HS256'])
    ->addSignature($signature_key2, ['alg' => 'RS384'])
    ->addSignature($signature_key3, ['alg' => 'ES512'])
    ->build();
use Jose\Component\Core\Converter\StandardConverter;
use Jose\Component\Signature\Serializer;

// The JSON Converter.
$jsonConverter = new StandardConverter();

$manager = Serializer\JWSSerializerManager::create([
    new Serializer\CompactSerializer($jsonConverter),
    new Serializer\JsonFlattenedSerializer($jsonConverter),
    new Serializer\JsonGeneralSerializer($jsonConverter),
]);

$tokenWithAllSignatures = $manager->serialize('jws_json_general', $jws);
$compactTokenWithSignatureAtIndex1 = $manager->serialize('jws_compact', $jws, 1);
$flattenedTokenWithSignatureAtIndex2 = $manager->serialize('jws_json_flattened', $jws, 2);

Nested Tokens

JWT can be signed or encrypted and both. A nested token is a signed token enclosed in an encrypted one. This order is very important: signed then encrypted.

The NestedTokenLoader and NestedTokenBuilder classes will help you to create nested tokens with ease. They are provided by the web-token/jwt-encryption component. However, you must also install the following component to use it:

  • web-token/jwt-checker

  • web-token/jwt-signature

hashtag
Nested Token Loading

To instantiate the NestedTokenLoader, you need a JWSLoader and a JWELoader.

Its use is very straightforward, you just have to call the method load using the token, the encryption and signature key sets.

The last argument ($signature in the following example) will represents the signature index of the verified signature. This is only useful when multiple signature support is used.

hashtag
Nested Token Building

To instantiate the NestedTokenBuilderder, you will need the following components:

  • a JWSBuilder,

  • a JWEBuilder,

  • a JWESerializerManager

Its use is a bit more complicated than the loading as the nested token may be designed for several recipients or may have several signatures.

As a reminder, if one of the following parameter is set, the compact serialization mode cannot be used:

  • signature unprotected header,

  • JWE shared unprotected header,

  • recipient unprotected header,

hashtag
Symfony Bundle

hashtag
Configuration

Hereafter an example of a Symfony application configuration:

This configuration will create two public services:

  • jose.nested_token_loader.loader_1

  • jose.nested_token_builder.builder_1

These services can be called from the container (unless private) or injected in your services.

hashtag
Configuration Helper

As any other services, you can create a nested token loader or builder from another bundle extension. The following bundle extension class will create the same configuration and services as above.

Signed tokens and

,
  • a JWSSerializerManager

  • Additional Authenticated Data.

    Unprotected Header

    You may want to set data in a token header that are not important for your application (e.g. general information). The integrity protection of the data is therefore not needed at all.

    The RFC7515arrow-up-right introduces an unprotected header. This header is supported by this framework.

    With the example below, we will create a signed token with some unprotected header parameters:

    $jws = $jwsBuilder
        ->create()
        ->withPayload('...')
        ->addSignature($jwk, ['alg' => 'HS256'], ['description' => 'Small description here', 'author' => 'John Doe'])
        ->build();

    The variable $jws will be a valid JWS object with one signature and both headers.

    Note: when an unprotected header is set, the Compact Serialization mode is not available.

    use Jose\Component\Encryption\NestedTokenLoader;
    
    $nestedTokenLoader = new NestedTokenLoader($jweLoader, $jwsLoader);
    $jws = $nestedTokenLoader->load($token, $encryptionKeySet, $signatureKeySet, $signature);
    use Jose\Component\Encryption\NestedTokenBuilder;
    
    $nestedTokenBuilder = new NestedTokenBuilder($jweLoader, $jweSerializerManager, $jwsLoader, $jwsSerializerManager);
    $token = $builder->create(
        $payload,                                     // The payload to protect
        [[                                            // A list of signatures. 'key' is mandatory and at least one of 'protected_header'/'header' has to be set.
            'key'              => $signature_key,     // The key used to sign. Mandatory.
            'protected_header' => ['alg' => 'PS256'], // The protected header. Optional.
            'header'           => ['foo' => 'bar'],   // The unprotected header. Optional.
        ]],
        'jws_json_flattened',                         // The serialization mode for the JWS
        ['alg' => 'RSA-OAEP', 'enc' => 'A128GCM'],    // The shared protected header. Optional.
        ['foo' => 'bar'],                             // The shared unprotected header. Optional.
        [[                                            // A list of recipients. 'key' is mandatory.
            'key'    => $encryption_key,              // The recipient key.
            'header' => ['bar' => 'foo'],             // The recipient unprotected header.
        ]],
        'jwe_json_flattened'                          // The serialization mode for the JWE.
        '1, 2, 3, 4'                                  // Additional Authenticated Data (AAD). Optional.
    );
    jose:
        nested_token:
            loaders:
                loader_1:
                    signature_algorithms: ['PS256']
                    key_encryption_algorithms: ['RSA-OAEP']
                    content_encryption_algorithms: ['A128GCM']
                    jws_serializers: ['jws_compact']
                    jws_header_checkers: [...]
                    jwe_serializers: ['jwe_compact']
                    jwe_header_checkers: [...]
                    is_public: true
            builders:
                builder_1:
                    signature_algorithms: ['PS256']
                    key_encryption_algorithms: ['RSA-OAEP']
                    content_encryption_algorithms: ['A128GCM']
                    jws_serializers: ['jws_compact']
                    jwe_serializers: ['jwe_compact']
                    is_public: true
    class AcmeExtension extends Extension implements PrependExtensionInterface
    {
        ...
    
        public function prepend(ContainerBuilder $container)
        {
            ConfigurationHelper::addNestedTokenLoader($container, 'loader_1', ['jwe_compact'], ['RSA-OAEP'], ['A128GCM'], ['DEF'], [], ['jws_compact'], ['PS256'], [], true, []);
            ConfigurationHelper::addNestedTokenBuilder($container, 'builder_1', ['jwe_compact'], ['RSA-OAEP'], ['A128GCM'], ['DEF'], ['jws_compact'], ['PS256'], true, []);
        }
    }

    Detached Payload

    As per the ,the payload of a JWS may be detached. This framework supports this feature.

    hashtag
    JWS Creation

    There is not much difference between the creation of a JWS with or without detached payload. The following example comes from the . There is only one argument that will change during the call of withPayload.

    And voilĂ ! When you will serialize this token, the payload will not be present.

    Additional Authentication Data (AAD)

    The Additional Authenticated Data (AAD) is an input to an Authenticated Encryption operation. The AAD is integrity protected but not encrypted.

    Its value can be any string you want that is needed by your application. With the example below, we will add a dummy AAD:

    Note: when the AAD is set, the Compact Serialization mode is not available.

    Encrypted tokens and

    $jwe = $jweBuilder
        ->create()
        ->withPayload('...')
        ->withSharedProtectedHeader([
            'enc' => 'A256CBC-HS512',
            'alg' => 'RSA-OAEP-256',
            'zip' => 'DEF',
        ])
        ->addRecipient($recipient_key)
        ->withAAD('A,B,C,D')
        ->build();

    Unprotected Headers

    As well as the signed tokens, the encrypted tokens also have unprotected header. But with one difference: there are two unprotected headers:

    • Shared unprotected header applicable to all recipients.

    • Per-recipient unprotected header.

    With the example below, we will create an encrypted token for two recipient and some unprotected header parameters:

    $jwe = $jweBuilder
        ->create()
        ->withPayload('...')
        ->withSharedProtectedHeader(['enc' => 'A256GCM', 'alg' => 'A256KW'])
        ->withSharedHeader(['author' => 'John Doe'])
        ->addRecipient($recipient_public_key_1, ['message' => 'Hello World!'])
        ->addRecipient($recipient_public_key_2, ['description' => 'Nice song for you'])
        ->build();

    The variable $jwe will be a valid JWE object built for two recipients. The unprotected header parameter author is applicable to the whole token while message and description are available only for the first and second recipient respectively.

    Note: when an unprotected header is set, the Compact Serialization mode is not available.

    hashtag
    JWS Loading

    The loading of a signed token with a detached payload is as easy as when the payload is attached. The only difference is that you have to pass the payload to the JWS Verifier when you want to check the signature.

    RFC7519arrow-up-right
    JWS Creation page
    <?php
    
    use Jose\Component\Core\AlgorithmManager;
    use Jose\Component\Core\Converter\StandardConverter;
    use Jose\Component\Core\JWK;
    use Jose\Component\Signature\Algorithm\HS256;
    use Jose\Component\Signature\JWSBuilder;
    
    // The algorithm manager with the HS256 algorithm.
    $algorithmManager = AlgorithmManager::create([
        new HS256(),
    ]);
    
    // Our key.
    $jwk = JWK::create([
        'kty' => 'oct',
        'k' => 'dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g',
    ]);
    
    // The JSON Converter.
    $jsonConverter = new StandardConverter();
    
    // We instantiate our JWS Builder.
    $jwsBuilder = new JWSBuilder(
        $jsonConverter,
        $algorithmManager
    );
    
    // The payload we want to sign
    $payload = $jsonConverter->encode([
        'iat' => time(),
        'nbf' => time(),
        'exp' => time() + 3600,
        'iss' => 'My service',
        'aud' => 'Your application',
    ]);
    
    $jws = $jwsBuilder
        ->create()                               // We want to create a new JWS
        ->withPayload($payload, true)            // /!\ Here is the change! We set the payload and we indicate it is detached
        ->addSignature($jwk, ['alg' => 'HS256']) // We add a signature with a simple protected header
        ->build();
    <?php
    
    use Jose\Component\Core\AlgorithmManager;
    use Jose\Component\Core\Converter\StandardConverter;
    use Jose\Component\Core\JWK;
    use Jose\Component\Signature\Algorithm\HS256;
    use Jose\Component\Signature\JWSVerifier;
    use Jose\Component\Signature\Serializer\JWSSerializerManager;
    use Jose\Component\Signature\Serializer\CompactSerializer;
    
    // The algorithm manager with the HS256 algorithm.
    $algorithmManager = AlgorithmManager::create([
        new HS256(),
    ]);
    
    // Our key.
    $jwk = JWK::create([
        'kty' => 'oct',
        'k' => 'dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g',
    ]);
    
    // The JSON Converter.
    $jsonConverter = new StandardConverter();
    
    // The serializer manager. We only use the JWS Compact Serialization Mode.
    $serializerManager = JWSSerializerManager::create([
        new CompactSerializer($jsonConverter),
    ]);
    
    // We instantiate our JWS Verifier.
    $jwsVerifier = new JWSVerifier(
        $algorithmManager
    );
    
    // The detached payload
    $payload = '{"iat":1507896992,"nbf":1507896992,"exp":1507900592,"iss":"My service","aud":"Your application"}';
    
    // The input we want to check
    $token = 'eyJhbGciOiJIUzI1NiJ9..eycp9PTdgO4WA-68-AMoHPwsKDr68NhjIQKz4lUkiI0';
    
    // We try to load the token.
    $jws = $serializerManager->unserialize($token);
    
    // We verify the signature.
    // /!\ The third argument is the detached payload.
    $jwsVerifier->verifyWithKey($jws, $jwk, $payload);

    Serialization

    The (JWS) and (JWE) introduce several serialization modes.

    • Compact

    • JSON Flattened

    JSON General

    The Compact mode is most know and commonly used as it is compact and URL safe i.e. it is designed for web context. JSON Flattened and General are not URL safe, but provides features that may fit on your application context.

    hashtag
    JWS Serialization

    To use the JWS serializers, you have to install the jwt-signature component.

    hashtag
    JWS Compact

    This serialization mode is probably the one you know the most. It it a string composed of three parts encoded in Base64 Url Safe and separated by a dot (.).

    The serializer class is Jose\Component\Signature\Serializer\CompactSerializer. The associated name is jws_compact.

    Example:

    There are some limitations when you use this serialization mode:

    • Unprotected header not supported.

    • Unencoded payload must contain characters within the following range of ASCII characters: 0x20-0x2d and 0x2f-0x7e

    hashtag
    JWS JSON Flattened

    This serialization mode is useful when you need to use the unprotected header. It it a simple JSON object.

    The serializer class is Jose\Component\Signature\Serializer\JSONFlattenedSerializer. The associated name is jws_json_flattened.

    Example:

    hashtag
    JWS JSON General

    This serialization mode is similar to the JWS JSON Flattened, but may contain more than one signature. It it a JSON object.

    The serializer class is Jose\Component\Signature\Serializer\JSONGeneralSerializer. The associated name is jws_json_general.

    Example:

    hashtag
    JWS Serializer Manager

    The serializer manager can be helpful when your application deals more than one serialization mode.

    hashtag
    JWE Serialization

    To use the JWE serializers, you have to install the jwt-encryption component.

    hashtag
    JWE Compact

    This serialization mode is probably the one you know the most. It it a string composed of five parts encoded in Base64 Url Safe and separated by a dot (.).

    The serializer class is Jose\Component\Encryption\Serializer\CompactSerializer. The associated name is jwe_compact.

    Example:

    There are some limitations when you use this serialization mode:

    • No Additional Authentication Data can be used.

    • No shared unprotected header or per-recipient header can be used.

    hashtag
    JWE JSON Flattened

    This serialization mode is useful when you need to use the unprotected header. It it a simple JSON object.

    The serializer class is Jose\Component\Encryption\Serializer\JSONFlattenedSerializer. The associated name is jwe_json_flattened.

    Example:

    hashtag
    JWE JSON General

    This serialization mode is similar to the JWE JSON Flattened, but may contain more than one recipient. It it a JSON object.

    The serializer class is Jose\Component\Encryption\Serializer\JSONGeneralSerializer. The associated name is jwe_json_general.

    Example:

    hashtag
    JWE Serializer Manager

    The serializer manager can be helpful when your application deals more than one serialization mode.

    RFC7515arrow-up-right
    RFC7516arrow-up-right
    composer require web-token/jwt-signature
    eyJhbGciOiJFUzUxMiJ9.UGF5bG9hZA.AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZqwqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8KpEHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn
    {
      "payload": "SW4gb3VyIHZpbGxhZ2UsIGZvbGtzIHNheSBHb2QgY3J1bWJsZXMgdXAgdGhlIG9sZCBtb29uIGludG8gc3RhcnMu",
      "protected": "eyJhbGciOiJFUzI1NiJ9",
      "header": {
        "kid": "myEcKey"
      },
      "signature": "b7V2UpDPytr-kMnM_YjiQ3E0J2ucOI9LYA7mt57vccrK1rb84j9areqgQcJwOA00aWGoz4hf6sMTBfobdcJEGg"
    }
    {
      "payload": "SW4gb3VyIHZpbGxhZ2UsIGZvbGtzIHNheSBHb2QgY3J1bWJsZXMgdXAgdGhlIG9sZCBtb29uIGludG8gc3RhcnMu",
      "signatures": [
        {
          "protected": "eyJhbGciOiJSUzI1NiJ9",
          "header": {
            "kid": "myRsaKey"
          },
          "signature": "B04c24gSnpVm1Z-_bemfyNMCpZm6Knj1yB-yzaIOvijsWfDgoF_mSJccTIbzapNgwJudnobr5iDOfZWiRR9iqCyDJLe5M1S40vFF7MFEI3JecYRgrRc6n1lTkYLMRyVq48BwbQlmKgPqmK9drun3agklsr0FmgNx65pfmcnlYdXsgwxf8WbgppefrlrMImp-98-dNtBcUL8ce1aOjbcyVFjGMCzpm3JerQqIzWQvEwBstnMEQle73KHcyx_nsTmlzY70CaydbRTsciOATL7WfiMwuX1q9Y2NIpTg3CbOTWKdwjh7iyfiAKQxNBaF2mApnqj9hjpf8GwR-CfxAzJtPg"
        },
        {
          "protected": "eyJhbGciOiJFUzI1NiJ9",
          "header": {
            "kid": "myEcKey"
          },
          "signature": "2cbugKq0ERaQMh01n2B-86EZFYleeMf8bsccaQMxzOxAg14PxfjR3IImvodTJYqkmfBJYW203etz2-7ZtJUOGw"
        },
        {
          "protected": "eyJhbGciOiJIUzI1NiJ9",
          "header": {
            "kid": "myMacKey"
          },
          "signature": "e7R9gjx0RsUNa3c7qd8k9mQGEhtcG8vsN1W7jbLb2MA"
        }
      ]
    }
    <?php
    
    require_once 'vendor/autoload.php';
    
    use Jose\Component\Core\Converter\StandardConverter;
    use Jose\Component\Signature\Serializer;
    
    $jsonConverter = new StandardConverter();
    
    $manager = Serializer\JWSSerializerManager::create([
        new Serializer\CompactSerializer($jsonConverter),
        new Serializer\JSONFlattenedSerializer($jsonConverter),
        new Serializer\JSONGeneralSerializer($jsonConverter),
    ]);
    
    // Serializes the second signature (index = 1) of the variable $jws (JWS object) into JSON Flattened serialization mode.
    $token = $manager->serialize('jws_json_flattened', $jws, 1);
    
    // Retrieve the JWS object from a token
    $jws = $manager->unserialize($token);
    eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A.AxY8DCtDaGlsbGljb3RoZQ.KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.9hH0vgRfYgPnAHOd8stkvw
    {
      "protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",
      "unprotected":{"jku":"https://server.example.com/keys.jwks"},
      "header":{"alg":"A128KW","kid":"7"},
      "encrypted_key":"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ",
      "iv":"AxY8DCtDaGlsbGljb3RoZQ",
      "ciphertext":"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",
      "tag":"Mz-VPPyU4RlcuYv1IwIvzw"
    }
     {
      "protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",
      "unprotected":{"jku":"https://server.example.com/keys.jwks"},
      "recipients":[
        {
          "header":{"alg":"RSA1_5","kid":"2011-04-29"},
          "encrypted_key":"UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A"
        },
        {
          "header":{"alg":"A128KW","kid":"7"},
          "encrypted_key":"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ"
        }
      ],
      "iv":"AxY8DCtDaGlsbGljb3RoZQ",
      "ciphertext":"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",
      "tag":"Mz-VPPyU4RlcuYv1IwIvzw"
     }
    <?php
    
    require_once 'vendor/autoload.php';
    
    use Jose\Component\Core\Converter\StandardConverter;
    use Jose\Component\Encryption\Serializer;
    
    $jsonConverter = new StandardConverter();
    
    $manager = Serializer\JWESerializerManager::create([
        new Serializer\CompactSerializer($jsonConverter),
        new Serializer\JSONFlattenedSerializer($jsonConverter),
        new Serializer\JSONGeneralSerializer($jsonConverter),
    ]);
    
    // Serializes the second recipient (index = 1) of the variable $jwe (JWE object) into JSON Flattened serialization mode.
    $token = $manager->serialize('jwe_json_flattened', $jwe, 1);
    
    // Retrieve the JWE object from a token
    $jwe = $manager->unserialize($token);

    Custom Algorithm

    This framework provides dozens of signature or encryption algorithms. If your application uses a custom algorithm or if another algorithm has been recently approved in the JWT context, it may be interesting to use it with this framework.

    Hopefully, this is very easy. In the following example, we will create a class to use the ChaCha20 + Poly 1305 (IETF variant) encryption algorithm as a Key Encryption Algorithm.

    <?php
    
    declare(strict_types=1);
    
    namespace Acme\Algorithm;
    
    use Base64Url\Base64Url;
    use Jose\Component\Core\JWK;
    use const Sodium\CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES;
    
    /**
     * This algorithm is a custom algorithm that use the ChaCha20 + Poly 1305 with a 192 bits nonce (IETF variant).
     */
    final class ChaCha20Poly1305IETF implements KeyEncryption //The algorithm acts as a Key Encryption algorithm
    {
        /**
         * {@inheritdoc}
         */
        public function name(): string
        {
            return 'ChaCha20+Poly1305+IETF'; // The name of our algorithm. This name will be used in our JWE headers
        }
    
        /**
         * {@inheritdoc}
         */
        public function allowedKeyTypes(): array
        {
            return ['oct']; // Key types for this algorithm are octet keys
        }
    
        /**
         * {@inheritdoc}
         */
        public function encryptKey(JWK $key, string $cek, array $completeHeader, array &$additionalHeader): string
        {
            $this->checkKey($key); // We check the key
            $kek = Base64Url::decode($key->get('k')); // We retrieve the secret
            $nonce = random_bytes(CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES); // We create a nonce
            $additionalHeader['nonce'] = Base64Url::encode($nonce); // We add the nonce to the header
    
            return sodium_crypto_aead_chacha20poly1305_ietf_encrypt($cek, '', $nonce, $kek); // We return the encrypted CEK
        }
    
        /**
         * {@inheritdoc}
         */
        public function decryptKey(JWK $key, string $encrypted_cek, array $header): string
        {
            $this->checkKey($key); // We check the key
            $this->checkAdditionalParameters($header); // We verify the nonce is in the headers
            $nonce = Base64Url::decode($header['nonce']); // We retrieve the nonce
            $kek = Base64Url::decode($key->get('k')); // an the secret
    
            $decrypted = sodium_crypto_aead_chacha20poly1305_ietf_decrypt($encrypted_cek, '', $nonce, $kek); // We try to decrypt the CEK
            if (false === $decrypted) { // If it fails we throw an exception
                throw new \RuntimeException('Unable to decrypt.');
            }
    
            return $decrypted; // Otherwise we return the decrypted CEK
        }
    
        /**
         * @param JWK $key
         */
        protected function checkKey(JWK $key)
        {
            if (!in_array($key->get('kty'), $this->allowedKeyTypes())) {
                throw new \InvalidArgumentException('Wrong key type.');
            }
            if (!$key->has('k')) {
                throw new \InvalidArgumentException('The key parameter "k" is missing.');
            }
        }
    
        /**
         * @param array $header
         */
        protected function checkAdditionalParameters(array $header)
        {
            foreach (['nonce'] as $k) {
                if (!array_key_exists($k, $header)) {
                    throw new \InvalidArgumentException(sprintf('Parameter "%s" is missing.', $k));
                }
            }
        }
    
        /**
         * {@inheritdoc}
         */
        public function getKeyManagementMode(): string
        {
            return self::MODE_ENCRYPT; //Key Management Mode is 'enc'.
        }
    }

    Multiple Recipients

    When you need to encrypt the same payload for several audiences, you may want to do it at once. The JWE Builder supports multiple recipients.

    With the example below, we will create an encrypted token for three different recipients using three different key encryption algorithms.

    Important notes:

    • The content encryption algorithm MUST be the same for all recipients.

    • The Key Management Modes of the key encryption algorithms MUST be compatible (see table below).

    Note: when an unprotected header is set, the Compact Serialization mode is not available.

    hashtag
    Key Management Modes

    Each Key Encryption Algorithm has its own Key Management Mode.

    hashtag
    Key Encryption Algorithms And Associated Key Management Mode.

    hashtag
    Compatibility table between Key Management Modes:

    X

    A128KW

    X

    A192KW

    X

    A256KW

    X

    ECDH-ES

    X

    ECDH-ES+A128KW

    X

    ECDH-ES+A192KW

    X

    ECDH-ES+A256KW

    X

    PBES2-HS256+A128KW

    X

    PBES2-HS384+A192KW

    X

    PBES2-HS512+A256KW

    X

    RSA1_5

    X

    RSA-OAEP

    X

    RSA-OAEP-256

    X

    A128GCMKW

    X

    A192GCMKW

    X

    A256GCMKW

    X

    YES

    NO

    Key Wrapping

    YES

    YES

    NO

    YES

    NO

    Direct Key Agreement

    NO

    NO

    NO

    NO

    NO

    Key Agreement with Key Wrapping

    YES

    YES

    NO

    NO

    NO

    Direct Encryption

    NO

    NO

    NO

    NO

    NO

    Algorithm Key Management Mode

    Key Encryption

    Key Wrapping

    Direct Key Agreement

    Key Agreement with Key Wrapping

    Direct Encryption

    dir

    Key Management Mode

    Key Encryption

    Key Wrapping

    Direct Key Agreement

    Key Agreement with Key Wrapping

    Direct Encryption

    Key Encryption

    YES

    YES

    NO

    $jweBuilder
        ->create()
        ->withPayload('...')
        ->withSharedProtectedHeader(['enc' => 'A128GCM'])
        ->addRecipient($recipient_key_1, ['alg' => 'RSA1_5'])
        ->addRecipient($recipient_key_2, ['alg' => 'RSA-OAEP-256'])
        ->build();