arrow-left
Only this pageAll pages
gitbookPowered by GitBook
1 of 66

v2.x

Loading...

Introduction

Loading...

Loading...

Loading...

Loading...

The Easy Way

Loading...

The Components

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

The Symfony Bundle

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Console Command

Loading...

Loading...

Loading...

Loading...

Advanced Topics

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Benchmark

Loading...

Loading...

Migration

Loading...

Loading...

Profiling/Debugging

Encrypted tokens and

Key (JWK) and Key Set (JWKSet)

To perform cryptographic operations (signature/verification and encryption/decryption), you will need keys.

The JWK object is part of the web-token/jwt-core component:

composer require web-token/jwt-core

hashtag
JWK

A JWK object represents a key. It contains all parameters needed by the algorithm and also information parameters.

This framework is able to create private and public keys easily. It can also generate those keys from external resources.

hashtag
JWKSet

The keys can be grouped in key sets. A JWKSet object represents a key set. It can contain as many keys as you need.

circle-exclamation

We strongly recommend you to avoid mixing public, private or shared keys in the same key set.

Signed Tokens (JWS)

To use the signed tokens (JWS), you have to install the web-token/jwt-signature componentarrow-up-right.

composer require web-token/jwt-signature

This component provides lot of signature algorithms and classes to load and create signed tokens.

Please refer to this signature algorithm table to know what algorithms are available.

Then, you will find an example to create a signed token here and another example to load and verify incoming tokens.

Continous Integration

The framework has been successfully tested using PHP 7.1, PHP 7.2 and nightly with all algorithms.

Tests vectors from the RFC 7520arrow-up-right are fully implemented and all tests pass. Other test vector sources may be used (e.g. new algorithm specifications).

We also track bugs and code quality using Scrutinizer-CIarrow-up-right and Sensio Insightarrow-up-right.

Coding Standards are verified by StyleCIarrow-up-right.

Code coverage is analyzed by .

Pre-requisite

This framework needs at least:

  • ,

  • GMP extension.

Encrypted Tokens (JWE)

To use the encrypted tokens (JWE), you have to install the .

This component provides lot of encryption algorithms and classes to load and create encrypted tokens.

Please refer to to know what algorithms are available.

Then, you will find an and another incoming tokens.

JWS creation

hashtag
JWS Builder Factory Service

A JWSBuilderFactory is available as a service in your application container:

With this factory, you will be able to create the JWSBuilder you need:

You can now use the JWSBuilder as explained in the JWS Creation section.

Symfony Bundle

This framework provides a Symfony bundle that will help you to use the components within your Symfony application. The bundle is available

  • when you just install the bundle (composer require web-token/jwt-bundle)

  • when you install the whole framework (composer require web-token/jwt-framework)

Contributing

Requests for new features, bug fixed and all other ideas to make this framework useful are welcome. If you feel comfortable writing code, you could try to fix or .

Do not forget to .

If you think you have found a security issue, DO NOT open an issue. .

Standalone Application

hashtag
Installation

To install the standalone application, you have to clone the repository and install the dependencies. We consider that git and composer are correctly installed.

You will find the standalone console command in the bin folder.

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 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:

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.

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.

Migration

Coveralls.ioarrow-up-right
opened issues where help is wantedarrow-up-right
those that are easy to fixarrow-up-right
follow these best practicesarrow-up-right
You MUST submit your issue herearrow-up-right
From v1.x to v2.0

Signed tokens and

composer require web-token/jwt-encryption
web-token/jwt-encryption componentarrow-up-right
this encryption algorithm table
example to create an encrypted token here
example to load and decrypt
git clone https://github.com/web-token/jwt-app.git
cd jwt-app
composer install --no-dev --optimize-autoloader --classmap-authoritative
./bin/jose
$jws = $jwsBuilder
    ->create()
    ->withPayload('...')
    ->addSignature($jwk, ['alg' => 'HS256'], ['description' => 'Small description here', 'author' => 'John Doe'])
    ->build();
RFC7515arrow-up-right
$jwe = $jweBuilder
    ->create()
    ->withPayload('...')
    ->withSharedProtectedHeader([
        'enc' => 'A256CBC-HS512',
        'alg' => 'RSA-OAEP-256',
        'zip' => 'DEF',
    ])
    ->addRecipient($recipient_key)
    ->withAAD('A,B,C,D')
    ->build();
hashtag
JWS Builder As Service

There is also another way to create a JWSBuilder object: using the bundle configuration.

With the previous configuration, the bundle will create a public JWS Builder service named jose.jws_builder.builder1 with selected signature algorithms.

hashtag
Custom Tags

You can add custom tags and attributes to the services you create.

<?php
use Jose\Component\Signature\JWSBuilderFactory;

$jwsBuilderFactory = $container->get(JWSBuilderFactory::class);
$jwsBuilder = $jwsBuilderFactory->create(['HS256']);
jose:
    jws:
        builders:
            builder1:
                signature_algorithms: ['HS256', 'RS256', 'ES256']
                is_public: true
<?php
$jwsBuilder = $container->get('jose.jws_builder.builder1');
jose:
    jws:
        builders:
            builder1:
                signature_algorithms: ['HS256', 'RS256', 'ES256']
                tags:
                    tag_name1: ~
                    tag_name2: {attribute1: 'foo'}
MBString extension.

Depending on the algorithms you using, other PHP extensions may be required (e.g. OpenSSL).

Please also consider the following optional requirements:

  • If you intent to use EdDSA or ECDH-ES algorithm with Ed25519/X25519 curves on PHP 7.1, please install this third party extensionarrow-up-right

If you just install the bundle on an application with Symfony Flex support, then there is nothing to do. Otherwise, you have to register the bundle:

The bundle capabilities will depend on the components installed in your application. The core component is always available.

  • Algorithm Management

  • Header and Claim Checkers

  • Keys and key sets

/**
 * {@inheritdoc}
 */
public function registerBundles()
{
    $bundles = [
        ...
        new Jose\Bundle\JoseFramework\JoseFrameworkBundle(),
    ];

    return $bundles;
}

Introduction

This project is a framework that provides an implementation of:

  • JWS JSON Web Signature (RFC 7515)arrow-up-right,

  • JWE JSON Web Encryption (RFC 7516)arrow-up-right,

  • JWK .

  • JWA .

  • JWT ,

  • JSON Web Key Thumbprint ().

  • Unencoded Payload Option ().

It also provides:

  • a Symfony bundle

  • a standalone console application

hashtag
Licence

This project is release under .

Signed Tokens

To use the signed tokens (JWS), you have to install the web-token/jwt-signature componentarrow-up-right.

composer require web-token/jwt-signature

When this component is installed, signature algorithms are automatically handles by the Algorithm Manager Factory.

  • JWS serializers,

  • JWS creation,

  • .

You can use symfony/serializer to serialize/unserialize your tokens:

Header and Claim Checker Management

hashtag
Checker Manager Factory Services

The Symfony Bundle provides Header and Claim Checker Manager Factory services. These services are available when the web-token/jwt-checker component is installed:

hashtag
Checker Manager Services

You can create Header and Claim Checker Managers using the bundle configuration.

With the previous configuration, the bundle will create public Header and Claim Checker Managers named jose.header_checker.checker1 and jose.claim_checker.checker1 with selected checkers.

hashtag
Custom Header Or Claim Checker

Some claim or header checkers are provided by this framework, but it is important to create custom checkers that fit on your application requirements.

In the following example, we will assume that the class exist and implement either Jose\Component\Checker\HeaderChecker or Jose\Component\Checker\ClaimChecker.

These checkers will be loaded by the factories and you will be able to create a header or a claim checker manager using the aliases foo or bar.

hashtag
Custom Tags

You can add custom tags and attributes to the header and claim checker managers.

JWS serializers

hashtag
JWS Serializer Manager Factory Service

A JWSSerializerManagerFactory is available as a service in your application container:

With this factory, you will be able to create the JWSSerializerManager you need:

You can now use the JWSSerializerManager as explained in the JWS Creation/Loading section.

Available JWS serialization modes are:

  • jws_compact

  • jws_json_general

  • jws_json_flattened

hashtag
JWS Serializer Manager As Service

There is also another way to create a JWSSerializerManager object: using the bundle configuration.

With the previous configuration, the bundle will create a public JWS Serializer Manager service named jose.jws_serializer.serializer1 with selected serialization modes.

hashtag
Custom Tags

You can add custom tags and attributes to the services you create.

Key and Key Set Management

The JWK and JWKSet objects are provided by the web-token/jwt-core component. We recommend you to load these objects through environment variables.

With Symfony 3.4 or 4.0+, an environment variables processor is provided:

parameters:
    my_private_key: '%env(jwk:MY_PRIVATE_KEY)%'
    my_public_keyset: '%env(jwkset:MY_PUBLIC_KEYSET)%'

With the previous configuration, the environment variables MY_PRIVATE_KEY and MY_PUBLIC_KEYSET will be processed by Symfony and the container will contain the my_private_key and my_public_keyset with JWK and JWKSet objects respectively.

But it may not be sufficient for your project. You may need to load keys or key sets from other sources (e.g. key file) You may also want to use your keys as a container services you inject to other services.

This behaviour is possible by installing the web-token/jwt-key-mgmt component. To install it, just execute the following command line:

Signature Algorithms

This framework comes with several signature algorithms. These algorithms are in the following namespace: Jose\Component\Signature\Algorithm.

  • HMAC with SHA-2 Functions. Package web-token/jwt-signature-algorithm-hmac

JWE Loading

Encrypted tokens are loaded by a serializer or the serializer manager and decrypted by the JWEDecrypter object. This JWEDecrypter object requires several services for the process:

  • an algorithm manager with key encryption algorithms

  • an algorithm manager with content encryption algorithms

Provided Features

hashtag
Supported Input Types:

JWS or JWE objects support every input that can be encoded into JSON:

  • string, array

JWS Creation

Now that you have an algorithm manager and a key, it is time to create your first signed token.

The computation is done by the JWSBuilder object. This object only requires the algorithm manager.

Now let's create our first JWS object.

Great! If everything is fine you will get a JWS object with one signature. We want to send it to the audience. Before that, it must be serialized.

We will use the compact serialization mode. This is the most common mode as it is URL safe and very compact. Perfect for a use in a web context!

All good! The variable

JWE creation

hashtag
JWE Builder Factory Service

A JWEBuilderFactory is available as a service in your application container:

With this factory, you will be able to create the JWEBuilder you need:

Available compression methods are:

Algorithm Management

hashtag
Algorithm Manager Factory Service

The Symfony Bundle provides an Algorithm Manager Factory service. The available algorithms depends on the components installed on your application.

hashtag

JWE serializers

hashtag
JWE Serializer Manager Factory Service

A JWESerializerManagerFactory is available as a service in your application container:

With this factory, you will be able to create the JWESerializerManager you need:

You can now use the JWESerializerManager as explained in the JWE Creation/Loading section.

Unprotected Headers

As well as the , 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:

Key Set (JWKSet)

You can create a JWKSet object using three static methods:

  • new JWKSet(array $keys): creates a JWKSet using a list of JWK objects.

  • JWKSet::createFromJson(string $json): creates a JWKSet using a JSON object.

PHAR Application

hashtag
Installation

To install the application, you just have to download it and download the associated public key (the application is digitally signed):

If everything is fine, you should have two files:

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.

Symfony Console

To enable the commands on a Symfony application, you have to install and add the associated bundle into your kernel:

If you use Symfony Flex, you have nothing to do. Otherwise you have to enable to bundle.

Then execute your Symfony Console command to use the command provided by this component:

composer require web-token/jwt-checker
<?php

use Jose\Component\Checker\HeaderCheckerManagerFactory;
use Jose\Component\Checker\ClaimCheckerManagerFactory;

$headerCheckerManagerFactory = $container->get(HeaderCheckerManagerFactory::class);
$headerCheckerManager = $headerCheckerManagerFactory->create([...]);

$claimCheckerManagerFactory = $container->get(ClaimCheckerManagerFactory::class);
$claimCheckerManager = $claimCheckerManagerFactory->create([...]);
<?php
use Jose\Component\Signature\JWSSerializerManagerFactory;

$jwsSerializerManagerFactory = $container->get(JWSSerializerManagerFactory::class);
$jwsSerializerManager = $jwsSerializerManagerFactory->create(['jws_compact']);
Signed tokens
Encrypted tokens
JSON Web Key (RFC 7517)arrow-up-right
JSON Web Algorithms (RFC 7518)arrow-up-right
JSON Web Token (RFC 7519)arrow-up-right
RFC 7638arrow-up-right
RFC7797arrow-up-right
MIT licencearrow-up-right
PHP 7.2+
// $serializer corresponds to the Symfony serializer
$serializer->serialize($data, 'jws_compact');
JWS verification
composer require web-token/jwt-key-mgmt
Key Management (JWK)
Key Set Management (JWKSet)
$jws = $jwsBuilder
    ->create()
    ->withPayload('...')
    ->addSignature($signature_key1, ['alg' => 'HS256'])
    ->addSignature($signature_key2, ['alg' => 'RS384'])
    ->addSignature($signature_key3, ['alg' => 'ES512'])
    ->build();
use Jose\Component\Signature\Serializer;

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

$tokenWithAllSignatures = $manager->serialize('jws_json_general', $jws);
$compactTokenWithSignatureAtIndex1 = $manager->serialize('jws_compact', $jws, 1);
$flattenedTokenWithSignatureAtIndex2 = $manager->serialize('jws_json_flattened', $jws, 2);
composer require web-token/jwt-console
composer require web-token/jwt-bundle
// app/AppKernel.php

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = [
            ...
            new Jose\Bundle\JwtFramework\JwtFrameworkBundle(),
        ];

        return $bundles;
    }
    ...
}
./bin/console
HS256
  • HS384

  • HS512

  • Elliptic Curve Digital Signature Algorithm (ECDSA). Package web-token/jwt-signature-algorithm-ecdsa

    • ES256

    • ES384

    • ES512

  • RSASSA-PKCS1 v1_5. Package web-token/jwt-signature-algorithm-rsa

    • RS256

    • RS384

    • RS512

  • RSASSA-PSS. Package web-token/jwt-signature-algorithm-rsa

    • PS256

    • PS384

    • PS512

  • Edwards-curve Digital Signature Algorithm (EdDSA) Package web-token/jwt-signature-algorithm-eddsa

    • EdDSA (only with the Ed25519 curve)

  • Unsecured algorithm Package web-token/jwt-signature-algorithm-none

    • none

  • The following signature algorithms are experimental and must not be used in production unless you know what you are doing. They are proposed for testing purpose only.

    They are all part of the package web-token/jwt-signature-algorithm-experimental

    • RS1: RSASSA-PKCS1 v1_5 with SHA-1 hashing function.

    • HS1: HMAC with SHA-1 hashing function.

    • ES256K: Elliptic curve secp256k1 support (v2.1+).

    hashtag
    How To Use

    These algorithms have to be used with the Algorithm Manager. They do not need any arguments.

    Example:

    a compression method manager. No compression method is needed if you do not intent to compress the payload.

    In the following example, we will use the same assumptions as the ones used during the JWE Creation process.

    Now we can try to deserialize and decrypt the input we receive. We will continue with the result we got during the JWE creation section.

    circle-exclamation

    We do not check header parameters here, but it is very important to do it. This step is described in the Header Checker section.

    Note: we do not check header parameters here, but it is very important to do it. This step is described in the Header Checker section.

    OK so if not exception is thrown, then your token is loaded and the payload correctly decrypted.

    hashtag
    JWELoader Object

    To avoid duplication of code lines, you can create a JWELoader object. This object contains a serializer, a decrypter and an optional header checker (highly recommended).

    In the following example, the JWELoader object will try to unserialize the token $token, check the header parameters and decrypt with the key $key.

    If the decryption succeeded, the variable $recipient will be set with the recipient index and should be in case of multiple recipients. The method returns the JWE object.

    In case you use a key set, you can use the method loadAndDecryptWithKeySet.

    hashtag
    JWELoaderFactory Object

    This feature was introduced in version 1.1.

    The JWELoaderFactory object is able to create JWELoader objects on demand. It requires the following factories:

    • JWESerializerManagerFactory

    • JWEDecrypterFactory

    • HeaderCheckerManagerFactory (optional)

    $token
    now contains a string that should be something like this:

    Other serialization modes exist. We will see them in the Advanced Topics section.

    <?php
    
    use Jose\Component\Core\AlgorithmManager;
    use Jose\Component\Core\JWK;
    use Jose\Component\Signature\Algorithm\HS256;
    use Jose\Component\Signature\JWSBuilder;
    
    // The algorithm manager with the HS256 algorithm.
    $algorithmManager = new AlgorithmManager([
        new HS256(),
    ]);
    
    // Our key.
    $jwk = new JWK([
        'kty' => 'oct',
        'k' => 'dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g',
    ]);
    
    // We instantiate our JWS Builder.
    $jwsBuilder = new JWSBuilder($algorithmManager);
    // The payload we want to sign. The payload MUST be a string hence we use our JSON Converter.
    $payload = json_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)                  // We set the payload
        ->addSignature($jwk, ['alg' => 'HS256']) // We add a signature with a simple protected header
        ->build();                               // We build it
  • DEF: deflate (recommended)

  • GZ: gzip

  • ZLIB: zlib

  • You can now use the JWEBuilder as explained in the JWE Creation section.

    hashtag
    JWE Builder As Service

    There is also another way to create a JWEBuilder object: using the bundle configuration.

    With the previous configuration, the bundle will create a public JWE Builder service named jose.jwe_builder.builder1 with selected encryption algorithms.

    hashtag
    Custom Tags

    You can add custom tags and attributes to the services you create.

    Custom Algorithm

    This factory handles all algorithms services tagged with jose.algorithm.

    Example:

    Your algorithm will be available through the algorithm manager factory service and the alias FOO.

    hashtag
    PBES2-* Algorithms

    When installed, the PBES2-* algorithms available throught the algorithm manager factory. They have the default configuration i.e. salt size = 62 bits and count = 4096. If these values does not fit on your needs, you can create a new algorithm service with your own values:

    You can now use your custom alias:

    <?php
    
    use Jose\Component\Core\AlgorithmManagerFactory;
    
    $algorithmManagerFactory = $container->get(AlgorithmManagerFactory::class);
    $algorithmManager = $algorithmManagerFactory->create(['RS256', 'HS512']);
    Available JWE serialization modes are:
    • jwe_compact

    • jwe_json_general

    • jwe_json_flattened

    hashtag
    JWE Serializer Manager As Service

    There is also another way to create a JWESerializerManager object: using the bundle configuration.

    With the previous configuration, the bundle will create a public JWE Serializer Manager service named jose.jwe_serializer.serializer1 with selected serialization modes.

    hashtag
    Custom Tags

    You can add custom tags and attributes to the services you create.

    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.

    signed tokens
    $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();

    JWKSet::createFromKeyData(array $values): creates a JWKSet using a decoded JSON object.

    Hereafter all methods available for a JWKSet object. The variable $jwkset is a valid JWKSet object.

    circle-exclamation

    Please note a JWKSet object is an immutable object

    <?php
    // Returns all keys
    $jwkset->all();
    
    // Check if the key set has the key with the key ID 'KEY ID'.
    $jwkset->has('KEY ID');
    
    // Retreive the key with the key ID 'KEY ID'.
    $jwkset->get('KEY ID');
    
    // Counts the keys in the key set.
    $jwkset->count(); // The method count($jwkset) has the same behaviour.
    
    // Adds a key to the key set.
    // /!\ As the JWKSet object is immutable, this method will create a new key set. The previous key set is unchanged.
    $newJwkset = $jwkset->with($jwk);
    
    // Removes a key to the key set.
    // /!\ As the JWKSet object is immutable, this method will create a new key set. The previous key set is unchanged.
    $newJwkset = $jwkset->without('KEY ID');
    
    // Selects a key according to the requirements.
    // The first argument is the key usage ("sig" of "enc")
    // The second argument is the algorithm to be used (optional)
    // The third argument is an associative array this constraints (optional)
    $key = $jwkset->selectKey('sig', $algorithm, ['kid' => 'KEY ID']);
    
    // You can iterate on a key set
    foreach($jwkset as $kid => $jwk) {
        // Action with the key done here
    }
    
    // The JWKSet object can be serialized into JSON
    json_encode($jwkset);
    jose.phar
  • jose.phar.pubkey

  • You can move these files wherever you want (e.g. /usr/local/bin).

    To use it, just execute the following line:

    hashtag
    Update

    The application can be updated easily:

    If a new version exists, it will be downloaded automatically. If there is something wrong with the new version, you can reinstall the previous revision:

    curl -OL https://github.com/web-token/jwt-app/raw/gh-pages/jose.phar
    curl -OL https://github.com/web-token/jwt-app/raw/gh-pages/jose.phar.pubkey
    ./jose.phar
    jose:
        checkers:
            claims:
                checker1:
                    is_public: true
                    claims: [...]
            headers:
                checker1:
                    is_public: true
                    headers: [...]
    services
        Acme\Checker\CustomHeaderChecker:
            public: false
            tags:
                - { name: 'jose.checker.header', alias: 'foo' }
        Acme\Checker\CustomClaimChecker:
            public: false
            tags:
                - { name: 'jose.checker.claim', alias: 'bar' }
    jose:
        checkers:
            claims:
                checker1:
                    claims: [...]
                    tags:
                        tag_name1: ~
                        tag_name2: {attribute1: 'foo'}
    jose:
        jws:
            serializers:
                serializer1:
                    serializers: ['jws_compact']
                    is_public: true
    <?php
    $jwsSerializerManager = $container->get('jose.jws_serializer.serializer1');
    jose:
        jws:
            serializers:
                serializer1:
                    serializers: ['jws_compact']
                    tags:
                        tag_name1: ~
                        tag_name2: {attribute1: 'foo'}
    <?php
    
    use Jose\Component\Core\AlgorithmManager;
    use Jose\Component\Signature\Algorithm\PS256;
    use Jose\Component\Signature\Algorithm\ES512;
    use Jose\Component\Signature\Algorithm\None;
    
    $algorithm_manager = new AlgorithmManager([
        new PS256(),
        new ES512(),
        new None(),
    ]);
    <?php
    
    use Jose\Component\Core\AlgorithmManager;
    use Jose\Component\Encryption\Algorithm\KeyEncryption\A256KW;
    use Jose\Component\Encryption\Algorithm\ContentEncryption\A256CBCHS512;
    use Jose\Component\Encryption\Compression\CompressionMethodManager;
    use Jose\Component\Encryption\Compression\Deflate;
    use Jose\Component\Encryption\JWEDecrypter;
    
    // The key encryption algorithm manager with the A256KW algorithm.
    $keyEncryptionAlgorithmManager = new AlgorithmManager([
        new A256KW(),
    ]);
    
    // The content encryption algorithm manager with the A256CBC-HS256 algorithm.
    $contentEncryptionAlgorithmManager = new AlgorithmManager([
        new A256CBCHS512(),
    ]);
    
    // The compression method manager with the DEF (Deflate) method.
    $compressionMethodManager = new CompressionMethodManager([
        new Deflate(),
    ]);
    
    // We instantiate our JWE Decrypter.
    $jweDecrypter = new JWEDecrypter(
        $keyEncryptionAlgorithmManager,
        $contentEncryptionAlgorithmManager,
        $compressionMethodManager
    );
    <?php
    
    use Jose\Component\Core\JWK;
    use Jose\Component\Encryption\Serializer\JWESerializerManager;
    use Jose\Component\Encryption\Serializer\CompactSerializer;
    
    // Our key.
    $jwk = JWK::create([
        'kty' => 'oct',
        'k' => 'dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g',
    ]);
    
    // The serializer manager. We only use the JWE Compact Serialization Mode.
    $serializerManager = new JWESerializerManager([
        new CompactSerializer(),
    ]);
    
    // The input we want to decrypt
    $token = 'eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiemlwIjoiREVGIn0.9RLpf3Gauf05QPNCMzPcH4XNBLmH0s3e-YWwOe57MTG844gnc-g2ywfXt_R0Q9qsR6WhkmQEhdLk2CBvfqr4ob4jFlvJK0yW.CCvfoTKO9tQlzCvbAuFAJg.PxrDlsbSRcxC5SuEJ84i9E9_R3tCyDQsEPTIllSCVxVcHiPOC2EdDlvUwYvznirYP6KMTdKMgLqxB4BwI3CWtys0fceSNxrEIu_uv1WhzJg.4DnyeLEAfB4I8Eq0UobnP8ymlX1UIfSSADaJCXr3RlU';
    
    // We try to load the token.
    $jwe = $serializerManager->unserialize($token);
    
    // We decrypt the token. This method does NOT check the header.
    $success = $jweDecrypter->decryptUsingKey($jwe, $jwk, 0);
    <?php
    
    use Jose\Component\Encryption\JWELoader;
    
    $jweLoader = new JWELoader(
        $serializerManager,
        $jweDecrypter,
        $headerCheckerManager
    );
    
    $jwe = $jweLoader->loadAndDecryptWithKey($token, $key, $recipient);
    <?php
    
    use Jose\Component\Encryption\JWELoaderFactory;
    
    $jweLoaderFactory = new JWELoaderFactory(
        $jweSerializerManagerFactory,
        $jweDecrypterFactory,
        $headerCheckerManagerFactory
    );
    
    $jweLoader = $jweLoaderFactory->create(
        ['jwe_compact'], // List of serializer aliases
        ['A128KW'],      // List of key encryption algorithm aliases
        ['A128KW'],      // List of content encryption algorithm aliases
        ['DEF'],         // List of compression method aliases
        ['alg', 'enc']   // Optional list of header checker aliases
    );
    use Jose\Component\Signature\Serializer\CompactSerializer;
    
    $serializer = new CompactSerializer(); // The serializer
    
    $token = $serializer->serialize($jws, 0); // We serialize the signature at index 0 (we only have one signature).
    eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MDc4OTY5OTIsIm5iZiI6MTUwNzg5Njk5MiwiZXhwIjoxNTA3OTAwNTkyLCJpc3MiOiJNeSBzZXJ2aWNlIiwiYXVkIjoiWW91ciBhcHBsaWNhdGlvbiJ9.eycp9PTdgO4WA-68-AMoHPwsKDr68NhjIQKz4lUkiI0
    <?php
    use Jose\Component\Encryption\JWEBuilderFactory;
    
    $jweBuilderFactory = $container->get(JWEBuilderFactory::class);
    $jweBuilder = $jweBuilderFactory->create(
        ['A256GCMKW'],
        ['A256CBC-HS256'],
        ['DEF'] // Compression methods
    );
    jose:
        jwe:
            builders:
                builder1:
                    key_encryption_algorithms: ['A256GCMKW']
                    content_encryption_algorithms: ['A256CBC-HS256']
                    compression_methods: ['DEF']
                    is_public: true
    <?php
    $jweBuilder = $container->get('jose.jwe_builder.builder1');
    jose:
        jwe:
            builders:
                builder1:
                    key_encryption_algorithms: ['A256GCMKW']
                    content_encryption_algorithms: ['A256CBC-HS256']
                    compression_methods: ['DEF']
                    tags:
                        tag_name1: ~
                        tag_name2: {attribute1: 'foo'}
    services:
        Acme\Bundle\Algorithm\FooAlgorihtm:
            tags:
                - {'name': 'jose.algorithm', 'alias': 'FOO'}
    services:
        my.custom.PBES2HS256A128KW.algorithm:
            class: Jose\Component\Encryption\Algorithm\KeyEncryption\PBES2HS256A128KW
            arguments:
                - 128   # salt size
                - 10240 # counts
            tags:
                - {'name': 'jose.algorithm', 'alias': 'Ultra-Secured PBES2-HS256+A128KW'}
    $algorithmManager = $algorithmManagerFactory->create(['Ultra-Secured PBES2-HS256+A128KW']);
    <?php
    use Jose\Component\Encryption\JWESerializerManagerFactory;
    
    $jweSerializerManagerFactory = $container->get(JWESerializerManagerFactory::class);
    $jweSerializerManager = $jweSerializerManagerFactory->create(['jwe_compact']);
    jose:
        jwe:
            serializers:
                serializer1:
                    serializers: ['jwe_compact']
                    is_public: true
    <?php
    $jweSerializerManager = $container->get('jose.jwe_serializer.serializer1');
    jose:
        jwe:
            serializers:
                serializer1:
                    serializers: ['jwe_compact']
                    tags:
                        tag_name1: ~
                        tag_name2: {attribute1: 'foo'}
    ./jose.phar selfupdate
    ./jose.phar rollback
    ,
    integer
    ,
    float
    ...
  • Objects that implement the \JsonSerializable interface such as JWK or JWKSet

  • The detached payload is supported.

    hashtag
    Supported Serialization Modes

    • Compact JSON Serialization Syntax for JWS and JWE

    • Flattened JSON Serialization Syntax for JWS and JWE

    • General JSON Serialization Syntax for JWS and JWE

    hashtag
    Supported Compression Methods

    Compression Method

    Supported

    Deflate (DEF)

    YES

    hashtag
    Supported Key Types (JWK)

    Key Type

    Supported

    Comment

    oct

    YES

    Symmetric keys

    RSA

    YES

    RSA based asymmetric keys

    EC

    YES

    Elliptic Curves based asymmetric keys

    OKP

    JWK objects support JSON Web Key Thumbprint (RFC 7638arrow-up-right).

    circle-info

    A none key type for the none algorithm. It is used to explicitly allow this unsecured algorithm.

    hashtag
    Key Sets (JWKSet)

    JWKSet is fully supported.

    hashtag
    Supported Signature Algorithms

    Signature Algorithm

    Supported

    Comment

    HS256, HS384 and HS512

    YES

    ES256, ES384 and ES512

    YES

    RS256, RS384 and RS512

    YES

    PS256, PS384 and PS512

    circle-info

    Other signature algorithms like RS1, HS1 or HS256/64 are also available. These algorithms should be used for testing purpose only or for compatibility with old systems

    hashtag
    Supported Key Encryption Algorithms

    Key Encryption Algorithm

    Supported

    dir

    YES

    RSA1_5, RSA-OAEP and RSA-OAEP-256

    YES

    ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW and ECDH-ES+A256KW

    YES

    A128KW, A192KW and A256KW

    YES

    PBES2-HS256+A128KW, PBES2-HS384+A192KW and PBES2-HS512+A256KW

    YES

    circle-info

    Other encryption algorithms like RSA-OEAP-384 or ChaCha20-Poly1305 are also available. These algorithms should be used for testing purpose only or for compatibility with old systems

    circle-exclamation

    For the ECDH-ES with X25519 curve with PHP 7.1, the third party extension highly recommendedarrow-up-right

    triangle-exclamation

    The algorithms RSA1_5 and RSA-OAEP are now deprecated. Please use with caution.

    hashtag
    Supported Content Encryption Algorithms

    Content Encryption Algorithm

    Supported

    A128CBC+HS256, A192CBC+HS384 and A256CBC+HS512

    YES

    A128GCM, A192GCM and A256GCM

    YES

    circle-info

    Other encryption algorithms like A128CTR, A192CTR and A256CTR are also available. These algorithms should be used for testing purpose only or for compatibility with old systems

    Encrypted Tokens

    To use the encrypted tokens (JWE), you have to install the web-token/jwt-encryption componentarrow-up-right.

    composer require web-token/jwt-encryption

    When this component is installed, encryption algorithms are automatically handles by the Algorithm Manager Factory.

    • JWE serializers,

    • JWE creation,

    • .

    You can use symfony/serializer to serialize/unserialize your tokens:

    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.

    From v1.x to v2.0

    Contrary to upgrade a minor version (where the middle number changes) where no difficulty should be encountered, upgrade a major version (where the first number changes) is subject to significant modifications.

    hashtag
    Update the libraries

    First of all, you have to make sure you are using the last v1.x release (1.3.8).

    hashtag
    Spot deprecations

    Next, you have to verify you don’t use any deprecated class, interface, method or property. If you have PHPUnit tests, .

    hashtag
    List of deprecations:

    • Jose\Component\Core\JWK::create(): this static function is removed. Use the constructor instead

    • Jose\Component\Core\JWKSet::createFromKeys() : this static function is removed. Use the constructor instead

    • Jose\Component\Core\Converter\JsonConverter: this interface is removed. No replacement.

    With the Symfony bundle, the configuration option jose.json_converter is removed.

    hashtag
    Add missing dependencies

    In v1.x, when you install the web-token/jwt-signature or web-token/jwt-encryption, the algorithms are automatically install.

    In v2.0, you must explicitly install the algorithms you need. Please refer to the or to know what package you need to install.

    hashtag
    Upgrade the libraries

    It is now time to upgrade the libraries. In your composer.json, change all web-token/* dependencies from v1.x to v2.0. When done, execute composer update.

    You can also update all other dependencies if needed. You can list upgradable libraries by calling composer outdated. This step is not mandatory, but highly recommended.

    Algorithm Management (JWA)

    For each cryptographic operation you will perform, you will need at least one algorithm.

    The algorithm list depends on the cypher operation to be performed (signature or encryption).

    These algorithms are managed by an Algorithm Manager. In the following example, we will create an algorithm manager that will handle two algorithms: PS256 and ES512.

    The algorithm management is part of the web-token/jwt-core component:

    Header Checker

    When you receive a JWT (JWS or JWE), it is important to check its headers before any other action. In case something went wrong, the token has to be rejected.

    To use the header checker, install the corresponding component:

    The header parameters are checked by a Header Checker Manager. This manager can contain several header checkers.

    Please note that:

    • the header parameter crit

    JWS verification

    hashtag
    JWS Verifier Factory Service

    A JWSVerifierFactory is available as a service in your application container:

    With this factory, you will be able to create the JWSVerifier you need:

    You can now use the JWSVerifier as explained in the JWS Creation section.

    JWE Creation

    The computation of a JWE is done by the JWEBuilder object. This object requires the following services:

    • an algorithm manager with key encryption algorithms

    • an algorithm manager with content encryption algorithms

    Encryption Algorithms

    This framework comes with several encryption algorithms. These algorithms are in the following namespaces:

    • Jose\Component\Encryption\Algorithm\KeyEncryption: key encryption algorithms

    • Jose\Component\Encryption\Algorithm\ContentEncryption: content encryption algorithms

    JWE decryption

    hashtag
    JWE Decrypter Factory Service

    A JWEDecrypterFactory is available as a service in your application container:

    With this factory, you will be able to create the JWEDecrypter you need:

    You can now use the JWEDecrypter as explained in the JWE Creation section.

    YES

    Octet Key Pair based asymmetric keys

    YES

    none

    YES

    Please note that this is not a secured algorithm. USE IT WITH CAUTION!

    EdDSA with Ed25519 curve

    YES

    With PHP 7.1, third party extension highly recommendedarrow-up-right

    EdDSA with Ed448 curve

    NO

    No extension or built-in implementation available

    A128GCMKW, A192GCMKW and A256GCMKW

    YES

    ECDH-ES with X25519 curve

    YES

    ECDH-ES with X448 curve

    NO

    Jose\Component\Core\Converter\StandardConverter: this class is removed. No replacement.

  • Jose\Component\Encryption\Compression\CompressionMethodManager::create(): this static function is removed. Use the constructor instead

  • Jose\Component\Encryption\Compression\GZip: this class is removed. No replacement.

  • Jose\Component\Encryption\Compression\ZLib: this class is removed. No replacement.

  • Jose\Component\Encryption\Serializer\JWESerializerManager::list(): this method is removed. Please use names()

  • Jose\Component\Checker\ClaimCheckerManager::create() : this static function is removed. Use the constructor instead

  • Jose\Component\Checker\HeaderCheckerManager::create() : this static function is removed. Use the constructor instead

  • Jose\Component\Core\AlgorithmManager::create() : this static function is removed. Use the constructor instead

  • Jose\Component\Encryption\Serializer\JWESerializerManager::create() : this static function is removed. Use the constructor instead

  • Jose\Component\Signature\Serializer\JWSSerializerManager::create() : this static function is removed. Use the constructor instead

  • you can easily get the list of deprecation used in your applicationarrow-up-right
    signature algorithms page
    encryption algorithms page
    // $serializer corresponds to the Symfony serializer
    $serializer->serialize($data, 'jwe_compact');
    JWE decryption
    $jws = $jwsBuilder
        ->create()
        ->withPayload('Hello World!')
        ->addSignature($jwk, ['alg' => 'HS256', 'b64' => false, 'crit' => ['b64']])
        ->build();

    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.

    circle-info

    The signature algorithms are available in other packages. See signature or encryption algorithm pages for more information

    circle-exclamation

    It is not possible to set the same algorithm twice in the same algorithm manager.

    hashtag
    Algorithm Manager Factory

    Your application may need several algorithm managers for several use cases. For example you application may use JWT for:

    • signed events,

    • authentication tokens.

    To avoid mixing algorithms in one algorithm manager or instantiate several times the same algorithms, this framework provides an Algorithm Manager Factory.

    This factory will create algorithm managers on demand. It allows the same algorithm to be instantiated multiple times but with different configuration options.

    Each algorithm is identified using an alias.

    The first argument of the method add is the alias for the algorithm. It must be unique. In general, this alias corresponds to the algorithm name.

    As you can see in the example, we added the algorithm PBES2-HS512+A256KW twice:

    • with the default configuration,

    • with custom arguments.

    Now our algorithm manager factory is ready. We can create several algorithm managers by passing a list of aliases to the method create:

    Reminder: it is important to check the token headers. See the checker section of this documentation.

    hashtag
    JWS Verifier As Service

    There is also another way to create a JWSVerifier object: using the bundle configuration.

    With the previous configuration, the bundle will create a public JWS Verifier service named jose.jws_verifier.verifier1 with selected signature algorithms.

    hashtag
    Custom Tags

    This feature was introduced in version 1.1.

    You can add custom tags and attributes to the services you create.

    hashtag
    JWS Loader Service

    This feature was introduced in version 1.1.

    The JWSLoaderFactory is available as a public service. You can retrieve it using the container or inject it into your services. It will help you to create JWSLoader objects on demand.

    You can also create JWSLoader objects as services using the configuration of the bundle.

    Or using the ConfigurationHelper.

    a compression method manager. No compression method is needed if you do not intent to compress the payload.

    Now let's create our first JWE object.

    Great! If everything is fine you will get a JWE object with one recipient. We want to send it to the audience. Before that, it must be serialized.

    We will use the compact serialization mode. This is the most common mode as it is URL safe and very compact. Perfect for a use in a web context!

    All good! The variable $token now contains a string that should be something like that:

    <?php
    
    use Jose\Component\Core\AlgorithmManager;
    use Jose\Component\Encryption\Algorithm\KeyEncryption\A256KW;
    use Jose\Component\Encryption\Algorithm\ContentEncryption\A256CBCHS512;
    use Jose\Component\Encryption\Compression\CompressionMethodManager;
    use Jose\Component\Encryption\Compression\Deflate;
    use Jose\Component\Encryption\JWEBuilder;
    
    // The key encryption algorithm manager with the A256KW algorithm.
    $keyEncryptionAlgorithmManager = new AlgorithmManager([
        new A256KW(),
    ]);
    
    // The content encryption algorithm manager with the A256CBC-HS256 algorithm.
    $contentEncryptionAlgorithmManager = new AlgorithmManager([
        new A256CBCHS512(),
    ]);
    
    // The compression method manager with the DEF (Deflate) method.
    $compressionMethodManager = new CompressionMethodManager([
        new Deflate(),
    ]);
    
    // We instantiate our JWE Builder.
    $jweBuilder = new JWEBuilder(
        $keyEncryptionAlgorithmManager,
        $contentEncryptionAlgorithmManager,
        $compressionMethodManager
    );
    Reminder: it is important to check the token headers. See the checker section of this documentation.

    hashtag
    JWE Decrypter As Service

    There is also another way to create a JWEDecrypter object: using the bundle configuration.

    With the previous configuration, the bundle will create a public JWE Decrypter service named jose.jwe_decrypter.decrypter1 with selected encryption algorithms.

    hashtag
    Custom Tags

    This feature was introduced in version 1.1.

    You can add custom tags and attributes to the services you create.

    hashtag
    JWE Loader Service

    The JWELoaderFactory is available as a public service. You can retrieve it using the container or inject it into your services. It will help you to create JWELoader objects on demand.

    You can also create JWELoader objects as services using the configuration of the bundle.

    Or using the ConfigurationHelper.

    <?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'.
        }
    }
    composer require web-token/jwt-core
    <?php
    
    use Jose\Component\Core\AlgorithmManager;
    use Jose\Component\Signature\Algorithm\PS256;
    use Jose\Component\Signature\Algorithm\ES512;
    
    $algorithmManager = new AlgorithmManager([
        new PS256(),
        new ES512(),
    ]);
    <?php
    
    use Jose\Component\Core\AlgorithmManagerFactory;
    use Jose\Component\Signature\Algorithm\PS256;
    use Jose\Component\Encryption\Algorithm\KeyEncryption\PBES2HS512A256KW;
    use Jose\Component\Encryption\Algorithm\ContentEncryption\A128CBCHS256;
    
    $algorithmManagerFactory = new AlgorithmManagerFactory();
    $algorithmManagerFactory 
        ->add('PS256', new PS256())
        ->add('A128CBC-HS256', new A128CBCHS256())
        ->add('PBES2-HS512+A256KW', new PBES2HS512A256KW())
        ->add('PBES2-HS512+A256KW with custom configuration', new PBES2HS512A256KW(128, 8192))
    ;
    <?php
    
    $signatureAlgorithmManager = $algorithmManagerFactory ->create(['PS256']);
    $encryptionAlgorithmManager = $algorithmManagerFactory ->create(['A128CBC-HS256', 'PBES2-HS512+A256KW']);
    $encryptionAlgorithmManagerForParanoid = $algorithmManagerFactory ->create(['A128CBC-HS256', 'PBES2-HS512+A256KW with custom configuration']);
    <?php
    use Jose\Component\Signature\JWSVerifierFactory;
    
    $jwsVerifierFactory = $container->get(JWSVerifierFactory::class);
    $jwsVerifier = $jwsVerifierFactory->create(['HS256']);
    jose:
        jws:
            verifiers:
                verifier1:
                    signature_algorithms: ['HS256', 'RS256', 'ES256']
                    is_public: true
    <?php
    $jwsVerifier = $container->get('jose.jws_verifier.verifier1');
    jose:
        jws:
            verifiers:
                verifier1:
                    signature_algorithms: ['HS256', 'RS256', 'ES256']
                    tags:
                        tag_name1: ~
                        tag_name2: {attribute1: 'foo'}
    <?php
    use Jose\Component\Signature\JWSLoaderFactory;
    
    $jwsLoaderFactory = $container->get(JWSLoaderFactory::class);
    jose:
        jws:
            loaders:
                jws_loader1:
                    serializers: ['jws_compact']
                    signature_algorithms: ['HS256']
                    header_checkers: ['alg']
                    is_public: true
    <?php
    use Jose\Bundle\JoseFramework\Helper\ConfigurationHelper;
    
    ...
    ConfigurationHelper::addJWSLoader($container, 'jws_loader1', ['jws_compact'], ['HS256'], ['alg'], true);
    use Jose\Component\Core\JWK;
    
    // Our key.
    $jwk = new JWK([
        'kty' => 'oct',
        'k' => 'dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g',
    ]);
    
    // The payload we want to encrypt. It MUST be a string.
    $payload = json_encode([
        'iat' => time(),
        'nbf' => time(),
        'exp' => time() + 3600,
        'iss' => 'My service',
        'aud' => 'Your application',
    ]);
    
    $jwe = $jweBuilder
        ->create()              // We want to create a new JWE
        ->withPayload($payload) // We set the payload
        ->withSharedProtectedHeader([
            'alg' => 'A256KW',        // Key Encryption Algorithm
            'enc' => 'A256CBC-HS512', // Content Encryption Algorithm
            'zip' => 'DEF'            // We enable the compression (irrelevant as the payload is small, just for the example).
        ])
        ->addRecipient($jwk)    // We add a recipient (a shared key or public key).
        ->build();              // We build it
    use Jose\Component\Encryption\Serializer\CompactSerializer;
    
    $serializer = new CompactSerializer(); // The serializer
    
    $token = $serializer->serialize($jwe, 0); // We serialize the recipient at index 0 (we only have one recipient).
    eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiemlwIjoiREVGIn0.9RLpf3Gauf05QPNCMzPcH4XNBLmH0s3e-YWwOe57MTG844gnc-g2ywfXt_R0Q9qsR6WhkmQEhdLk2CBvfqr4ob4jFlvJK0yW.CCvfoTKO9tQlzCvbAuFAJg.PxrDlsbSRcxC5SuEJ84i9E9_R3tCyDQsEPTIllSCVxVcHiPOC2EdDlvUwYvznirYP6KMTdKMgLqxB4BwI3CWtys0fceSNxrEIu_uv1WhzJg.4DnyeLEAfB4I8Eq0UobnP8ymlX1UIfSSADaJCXr3RlU
    <?php
    use Jose\Component\Encryption\JWEDecrypterFactory;
    
    $jweDecrypterFactory = $container->get(JWEDecrypterFactory::class);
    $jweDecrypter = $jweDecrypterFactory->create(['HS256']);
    jose:
        jwe:
            decrypters:
                decrypter1:
                    key_encryption_algorithms: ['A256GCMKW']
                    content_encryption_algorithms: ['A256CBC-HS256']
                    compression_methods: ['DEF']
                    is_public: true
    <?php
    $jweDecrypter = $container->get('jose.jwe_decrypter.decrypter1');
    jose:
        jwe:
            decrypters:
                decrypter1:
                    key_encryption_algorithms: ['A256GCMKW']
                    content_encryption_algorithms: ['A256CBC-HS256']
                    compression_methods: ['DEF']
                    tags:
                        tag_name1: ~
                        tag_name2: {attribute1: 'foo'}
    <?php
    use Jose\Component\Encryption\JWELoaderFactory;
    
    $jweLoaderFactory = $container->get(JWELoaderFactory::class);
    jose:
        jwe:
            loaders:
                jwe_loader1:
                    key_encryption_algorithms: ['A256GCMKW']
                    content_encryption_algorithms: ['A256CBC-HS256']
                    compression_methods: ['DEF']
                    header_checkers: ['alg', 'enc']
                    is_public: true
    <?php
    use Jose\Bundle\JoseFramework\Helper\ConfigurationHelper;
    
    ...
    ConfigurationHelper::addJWELoader($container, 'jwe_loader1', ['jwe_compact'], ['A256GCMKW'], ['A256CBC-HS256'], ['DEF'], ['alg', 'enc'], true);
    (critical) is always checked.
  • even if the JWS and JWE Loaders will check the alg/enc header parameters, it is interesting to check them through this manager.

  • hashtag
    Header Checker Manager

    To create a header checker manager, you will need to add header checkers and at least one token type. You will find token type classes for the JWS and JWE tokens in the web-token/jwt-signature and web-token/jwt-encryption components respectively.

    In the following example, we want to check the alg header parameter for the signed tokens (JWS) received by our application.

    The usage of this class is pretty easy you just have to call the check method. The first parameter is the JWT to check, the second one is the index of the signature/recipient.

    In some cases, it could be interesting to reject tokens that do not contain some mandatory header parameters. A list of mandatory parameters can be set as third argument. If one of those parameters is missing an exception is thrown, even if that header parameter have not been checked.

    In the following example, an exception will be thrown if the alg, enc or crit parameters is missing.

    hashtag
    Header Checker Manager Factory

    The Header Checker Manager Factory will help you to create as many Header Checker Manager as you want to fit on your application requirements.

    hashtag
    Custom Header Checker

    with the previous examples, we will only check the alg (algorithm) header parameter. But your application may use other header parameters e.g. cty, typ...

    The following header checkers are provided:

    • AlgorithmChecker: checks the alg header parameter.

    • AudienceChecker: checks the aud header parameter. This is a replicated claim as per the RFC7516 section 5.3arrow-up-right

    • UnencodedPayloadChecker: checks the b64 header parameter. See for more information.

    If you need, you can create you own header checker. It must implement the interface Jose\Component\Checker\HeaderChecker. In the following example, we will check that the protected header parameter custom is an array with value foo or bar.

    Key Encryption
    • Package web-token/jwt-encryption-algorithm-aeskw

      • A128KW

      • A192KW

      • A256KW

    • Package web-token/jwt-encryption-algorithm-aesgcmkw

      • A128GCMKW

      • A192GCMKW

    • Package web-token/jwt-encryption-algorithm-dir

      • dir (class Dir)

    • Package web-token/jwt-encryption-algorithm-ecdh-es

      • ECDH-ES (class ECDHES) READ THE NOTE BELOW

    • Package web-token/jwt-encryption-algorithm-pbes2

      • PBES2-HS256+A128KW (class PBES2HS256A128KW)

    • Package web-token/jwt-encryption-algorithm-rsa

      • RSA1_5 (class RSA15) READ THE NOTE BELOW

    Content Encryption

    • Package web-token/jwt-encryption-algorithm-aesgcm

      • A128GCM

      • A192GCM

      • A256GCM

    • Package web-token/jwt-encryption-algorithm-aescbc

      • A128CBC-HS256 (class A128CBCHS256)

    triangle-exclamation

    The algorithm RSA1_5 is deprecated due to known security vulnerabilityarrow-up-right.

    The algorithms ECDH-ES* are not recommended unless used with the OKP key type.

    hashtag
    Experimental Algorithms

    The following algorithms are experimental and must not be used in production unless you know what you are doing. They are proposed for testing purpose only.

    They are all part of the package web-token/jwt-encryption-algorithm-experimental

    Key Encryption

    • A128CTR, A192CTR and A256CTR: AES CTR based encryption.

    • Chacha20+Poly1305 : Please note that this algorithm requires OpenSSL 1.1

    • RSA-OAEP-384 and RSA-OAEP-512: Same algorithm as RSA-OAEP-256 but with SHA-384 and SHA-512 hashing functions.

    Content Encryption

    • AxxxCCM-16-128, AxxxCCM-16-64, AxxxCCM-64-128, AxxxCCM-64-64: AES-CCM based aalgorithms. xxx can be 128 or 256.

    hashtag
    How To Use

    These algorithms have to be used with the Algorithm Manager.

    By default, PBES2* algorithms use the following parameter values:

    • Salt size: 64 bytes (512 bits)

    • Count: 4096

    You may need to use other values. This can be done during the instantiation of the algorithm:

    Example with 16 bytes (128 bits) salt and 1024 counts:

    JWS Loading

    Signed tokens are loaded by a serializer or the serializer manager and verified by the JWSVerifier object. This JWSVerifier object just requires an algorithm manager.

    hashtag
    Serializer And Verifier

    In the following example, we will try to load a signed token. We will only use the HS256 algorithm.

    Now we can deserialize the input we receive and check the signature using our key. We will continue with the data we got in the JWS creation section.

    circle-exclamation

    We do not check header parameters here, but it is very important to do it. This step is described in the .

    Note: we do not check header parameters here, but it is very important to do it. This step is described in the Header Checker section.

    The method verifyWithKey returns a boolean. If true, then your token signature is valid. You can then check the claims (if any) using the claim checker manager.

    hashtag
    JWSLoader Object

    To avoid duplication of code lines, you can create a JWSLoader object. This object contains a serializer, a verifier and an optional header checker (highly recommended).

    In the following example, the JWSLoader object will try to unserialize the token $token, check the header parameters and verify the signature with the key $jwk. The variable $payload corresponds to the detached payload (null by default).

    If the verification succeeded, the variable $signature will be set with the signature index and should be in case of multiple signatures. The method returns the JWS object.

    In case you use a key set, you can use the method loadAndVerifyWithKeySet.

    hashtag
    JWSLoaderFactory Object

    This feature was introduced in version 1.1.

    The JWSLoaderFactory object is able to create JWSLoader objects on demand. It requires the following factories:

    • JWSSerializerManagerFactory

    • JWSVerifierFactory

    • HeaderCheckerManagerFactory (optional)

    Events

    How to use Symfony events

    With the version 2.0 of the Symfony Bundle, you will be able to listen or subscribe to events.

    All events can be found in the class Jose\Bundle\JoseFramework\Event\Events.

    • JWS:

      • Events::JWS_BUILT_SUCCESS

      • Events::JWS_BUILT_FAILURE

      • Events::JWS_VERIFICATION_SUCCESS

      • Events::JWS_VERIFICATION_FAILURE

      • Events::JWS_LOADING_SUCCESS

      • Events::JWS_LOADING_FAILURE

    • JWE:

      • Events::JWE_BUILT_SUCCESS

      • Events::JWE_BUILT_FAILURE

    • Nested Tokens:

      • Events::NESTED_TOKEN_ISSUED Events::NESTED_TOKEN_LOADING_SUCCESS Events::NESTED_TOKEN_LOADING_FAILURE

    • Checked Header:

      • Events::HEADER_CHECK_SUCCESS

      • Events::HEADER_CHECK_FAILURE

    • Checked Claim:

      • Events::CLAIM_CHECK_SUCCESS

      • Events::CLAIM_CHECK_FAILURE

    hashtag
    Example:

    Detached Payload

    As per the RFC7519arrow-up-right,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 JWS Creation page. 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.

    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.

    Configuration Helper

    When you want to create keys/key sets, JWS loader/verifier... services, you have to create a dedicated jose section in your configuration. It may confuse your users to configure your bundle and the Jose Framework bundle. Sometimes, you may also want to be sure that the configuration is correctly defined. Lastly, the configuration size increases with numerous details, options or service IDs and it becomes difficult to read or modify.

    Hopefully, the Symfony bundle provide a configuration helper: Jose\Bundle\JoseFramework\Helper\ConfigurationHelper. This helper will configure the jose section for you. This helper has to be called in your bundle extension during the prepend step (your extension has to implement Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface).

    Key Management (JWK)

    hashtag
    Keys As Services

    When the component is installed, you will be able to define your keys in your application configuration and load your keys from several sources or formats. All these methods have the following option:

    • is_public

    Claim Checker

    JSON Web Tokens can be used to transport any kind of data. They are mainly used to transport claims. When you receive a tokens that contains claims, it is important to check the values of these claims.

    The Claim Checker Manager is responsible of this task. To use it, install the corresponding component:

    hashtag
    Claim Checker Manager

    In the following example, we will create a manager able to check the aud

    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. Just instal the package web-token/jwt-nested-token. It contains all the classes and dependencies will be directly managed by composer. You can install it if needed.

    hashtag

    Security Recommendations

    Signed or Encrypted Tokens are not just the next trendy/popular way to authenticate users. They provide great features but when used incorrectly they can expose your application to major security issues.

    Please read the following recommendations carefully.

    hashtag
    Algorithms

    Key Set Management (JWKSet)

    hashtag
    Key Sets As Services

    All these methods have the following common option:

    • is_public: set the service public or private.

    composer require web-token/jwt-checker
    <?php
    
    use Jose\Component\Checker\HeaderCheckerManager;
    use Jose\Component\Checker\AlgorithmChecker;
    use Jose\Component\Signature\JWSTokenSupport;
    
    $headerCheckerManager = new HeaderCheckerManager(
        [
            new AlgorithmChecker(['HS256']), // We check the header "alg" (algorithm)
        ],
        [
            new JWSTokenSupport(), // Adds JWS token type support
        ]
    );
    $headerCheckerManager->check($jwt, 0);
    $headerCheckerManager->check($jwt, 0, ['alg', 'enc', 'crit']);
    <?php
    
    use Jose\Component\Checker\HeaderCheckerManagerFactory;
    use Jose\Component\Checker\AlgorithmChecker;
    use Jose\Component\Encryption\JWETokenSupport;
    use Jose\Component\Signature\JWSTokenSupport;
    
    $headerCheckerManagerFactory = new HeaderCheckerManagerFactory();
    $headerCheckerManagerFactory->add('signature_alg', new AlgorithmChecker(['HS256']));
    $headerCheckerManagerFactory->add('key_encryption_alg', new AlgorithmChecker(['RSA1_5']));
    $headerCheckerManagerFactory->addTokenTypeSupport(new JWSTokenSupport());
    $headerCheckerManagerFactory->addTokenTypeSupport(new JWETokenSupport());
    
    $headerCheckerManagerForSignatures = $headerCheckerManagerFactory->create(['signature_alg']);
    $headerCheckerManagerForEncryption = $headerCheckerManagerFactory->create(['key_encryption_alg']);
    Acme\Checker\CustomChecker.php
    <?php
    
    namespace Acme\Checker;
    
    use Jose\Component\Checker\HeaderChecker;
    use Jose\Component\Checker\InvalidHeaderException;
    
    final class CustomChecker implements HeaderChecker
    {
        public function checkHeader($value)
        {
            if (!is_array($value) || !in_array($value, ['foo', 'bar'])) {
                throw new InvalidHeaderException('Invalid header "custom".', 'custom', $value);
            }
        }
    
        // This header parameter name.
        public function supportedHeader(): string
        {
            return 'custom';
        }
    
        // This method indicates if this parameter must be in the protected header or not.
        public function protectedHeaderOnly(): bool
        {
            return true;
        }
    }
    <?php
    
    use Jose\Component\Core\AlgorithmManager;
    use Jose\Component\Encryption\Algorithm\KeyEncryption\A128KW;
    use Jose\Component\Encryption\Algorithm\KeyEncryption\PBES2HS256A128KW;
    use Jose\Component\Encryption\Algorithm\ContentEncryption\A128CBCHS256;
    
    $algorithmManager = new AlgorithmManager([
        new A128KW(),
        new PBES2HS256A128KW(),
        new A128CBCHS256(),
    ]);
    <?php
    
    use Jose\Component\Core\AlgorithmManager;
    use Jose\Component\Encryption\Algorithm\KeyEncryption\PBES2HS256A128KW;
    
    $algorithmManager = new AlgorithmManager([
        new PBES2HS256A128KW(16, 1024),
    ]);
    <?php
    
    use Jose\Component\Core\AlgorithmManager;
    use Jose\Component\Signature\Algorithm\HS256;
    use Jose\Component\Signature\JWSVerifier;
    
    // The algorithm manager with the HS256 algorithm.
    $algorithmManager = new AlgorithmManager([
        new HS256(),
    ]);
    
    // We instantiate our JWS Verifier.
    $jwsVerifier = new JWSVerifier(
        $algorithmManager
    );
    <?php
    
    use Jose\Component\Core\AlgorithmManager;
    use Jose\Component\Core\JWK;
    use Jose\Component\Signature\Algorithm\HS256;
    use Jose\Component\Signature\JWSBuilder;
    
    // The algorithm manager with the HS256 algorithm.
    $algorithmManager = new AlgorithmManager([
        new HS256(),
    ]);
    
    // Our key.
    $jwk = new JWK([
        'kty' => 'oct',
        'k' => 'dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g',
    ]);
    
    // We instantiate our JWS Builder.
    $jwsBuilder = new JWSBuilder(
        $algorithmManager
    );
    
    // The payload we want to sign
    $payload = json_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();
  • A256GCMKW

  • ECDH-ES+A128KW (class ECDHESA128KW) READ THE NOTE BELOW
  • ECDH-ES+A192KW (class ECDHESA192KW) READ THE NOTE BELOW

  • ECDH-ES+A256KW (class ECDHESA256KW) READ THE NOTE BELOW

  • PBES2-HS384+A192KW (class PBES2HS384A192KW)
  • PBES2-HS512+A256KW (class PBES2HS512A256KW)

  • RSA-OAEP (class RSAOAEP)
  • RSA-OAEP-256 (class RSAOAEP256)

  • A192CBC-HS384 (class A192CBCHS384)
  • A256CBC-HS512 (class A256CBCHS512)

  • hashtag
    Algorithm Choice

    If all parties are able to protect their keys (e.g. private applications), symmetric algorithms are a good choice as they are faster in general. If you use public clients, you should prefer asymmetric algorithms.

    hashtag
    Available Algorithms

    This framework provides dozen of signature and encryption algorithms, but you do not need all of them. Most applications only support 1 or 2 algorithms.

    You should only use necessary algorithms. For example, HS384 algorithm may be avoided if you already have HS256 and HS512.

    hashtag
    Avoid Weak Algorithms

    Some algorithms are not recommended as there are known security issues:

    • none: this algorithm is not a real algorithm. It should only be used when other security means exist. An encrypted connection is certainly not enough!

    • RSA1_5: there are known attacks using this algorithm. If you can avoid its use, then do it.

    hashtag
    Keys And Key Sets

    hashtag
    Key Size

    A small key size is as secured as a password like 123456789. You should use at least 256 bits symmetric keys and at lease 2048 bits RSA keys.

    In any case, you MUST use a true random number generator.

    hashtag
    Additional Information

    It is highly recommended to set the following parameters to your key:

    • kid: A unique key ID,

    • use: indicates the usage of the key. Either sig (signature/verification) or enc (encryption/decryption).

    • alg: the algorithm allowed to be used with this key.

    hashtag
    Key Rotation

    A key is fine but may be cracked e.g. by bruteforce. Changing your keys after several days or weeks is encouraged.

    hashtag
    Token Creation

    hashtag
    Header Parameters

    Broadly speaking, you should set your header parameter in the protected header. The use of the unprotected header should be limited to specific use cases.

    When using encrypted tokens, the claims iss and aud should be duplicated into the header. This will avoid unwanted decryption when tokens are sent to a wrong audience.

    hashtag
    Payload And Claims

    There is no size constraint for the payload, but when tokens are used in a web context, it should be as small as possible. When used, claims should be limited to the minimum.

    Does your application really need to get all the information about a user? For each context, you should choose carefully the claims you want to use.

    hashtag
    Token Unique ID

    A unique token ID should be set to all tokens you create. The associated claim is jti.

    This claim is highly recommended as it can prevent replay attacks.

    hashtag
    Time Indicators

    The JWT specification introduces several claims to limit the period of validity of the tokens:

    • exp: expiration time,

    • iat: issuance time,

    • nbf: validity point in time.

    These claims are not mandatory, but it is recommended to define a period of time for the token validity. When used, the expiration time should be appropriate to its context in your application. A security token with 2 weeks lifetime is something you should avoid.

    hashtag
    Issuer And Audience

    The claims iss (issuer) and aud (audience) should always be set. When duplicated in the header, their values MUST be identical.

    hashtag
    Application Communication

    hashtag
    Secured Connection

    Unless you use encrypted tokens, you should use a secured connection when transmitting tokens between parties. A secured communication is not only needed when transmitting tokens, but also when you exchange keys and key sets with other applications.

    hashtag
    Loading Process

    When you receive a token, the following steps should be followed in this order. If one failed, you you reject the whole token.

    1. Unserialize the token

    2. For each signature/recipient (may be possible when using the Json General Serialization Mode):

      1. Check the complete header (protected and unprotected)

      2. Verify the signature (JWS) or decrypt the token (JWE)

      3. Check the claims in the payload (if any)

    hashtag
    Unserialize The Token

    You should only use the serialization mode(s) you need. If you intend to use your tokens in a web context, then use only the Compact Serialization. If an error occurred during this process, you should consider the token as invalid.

    hashtag
    Check The Header

    Header parameters have to be checked. You should at least check the alg (algorithm) and enc (only for JWE) parameters. The crit (critical) header parameter is always checked.

    Please note that unknown header parameters are ignored. If your token is verified, those parameters should not be used.

    When used, unprotected header parameters should be handled with care.

    hashtag
    Signature Verification / Payload Decryption

    Let the component do its job. The most important step for developers is to ensure that the right key/ket set is used.

    hashtag
    Check The Claims

    This step is only required if the payload contains claims. When present, you should always check the exp, iat, nbf, iss and aud claims. Application specific claims should also always checked.

    The whole token should be rejected in case of failure. Unknown claims should be ignored.

    hashtag
    Stay Tuned

    You should subscribe to security forums of similar websites and have a continuous technological watch. The tokens may be compromised because of malicious attacks on the algorithms, keys or other components related to the JWT (directly or indirectly).

    The Security group on Stack Exchangearrow-up-right is a good start.

    unencoded payload
    Header Checker section
    Events::JWE_DECRYPTION_SUCCESS
  • Events::JWE_DECRYPTION_FAILURE

  • Events::JWE_LOADING_SUCCESS

  • Events::JWE_LOADING_FAILURE

  • : set the service public or private.

    The key configuration will look like as follow:

    The key will be available as a container service with the ID jose.key.key_name where key_name is the unique name of your key. Each key service will be an instance of the Jose\Component\Core\JWK class.

    As any other configuration values, you can use environment variables.

    hashtag
    From A Shared Secret

    This method will directly get a shared secret.

    hashtag
    From A JWK Object

    This method will directly load a JWK object.

    hashtag
    From A X509 Certificate File

    This method will load a X509 Certificate file.

    hashtag
    From A X509 Certificate

    This method will load a key from a X509 Certificate.

    hashtag
    From A PKCS#1/PKCS#8 Key File

    This method will load a key from a PKCS#1 or PKCS#8 key file.

    hashtag
    From A Key In A Key Set

    This method will retrieve a key from a JWKSet service.

    hashtag
    Custom Tags

    You can add custom tags and attributes to the services you create.

    (Audience),
    iat
    (Issued At),
    nbf
    (Not Before) and
    exp
    (Expiration) claims.

    When instantiated, call the method check to check the claims of a JWT object. This method only accept an associative array. You have to retrieve this array by converting the JWT payload.

    In some cases, it could be interesting to reject tokens that do not contain some mandatory claims. A list of mandatory claims can be set as second argument. If one of those claims is missing an exception is thrown, even if the claim have not been checked.

    In the following example, an exception will be thrown if the iss, sub or aud claim is missing.

    hashtag
    Custom Claim Checker

    Your application may use other claims that you will have to check therefore custom claim checkers have to be created.

    In this example, we will create a class that will check the claim foo. The claim accept only a string with the value bar or bat. All claim checker have to implement the interface Jose\Component\Checker\ClaimChecker;

    All done! Now you can instantiate your class and add it to your Claim Checker Manager.

    hashtag
    Replicating Claims as Header Parameters

    The RFC7516 section 5.3arrow-up-right allows to replicate some claims in the header. This behaviour is very useful with encrypted tokens as it helps to reject invalid tokens without decryption of the payload.

    The Claim Checker Manager cannot check those replicated claims, you have to create a custom header checker. However, to avoid duplicated classes, your claim checker can implement the Jose\Component\Checker\HeaderChecker interface.

    Have a look at the IssuedAtChecker or the NotBeforeChecker classes. These checkers can be used for claim and header checks.

    hashtag
    Claim Checker Manager Factory

    Your application may use JSON Web Tokens in different contexts and thus the meaning of a claim may be different. You will need several Claim Checker Managers with dedicated claim checkers.

    This framework provides an Claim Checker Manager Factory. This factory is able to accept as many claim checkers as you need. Each claim checker you add to this factory is associated to an alias. You will then be able to create a claim checker manager using those aliases.

    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 NestedTokenBuilder, you will need the following components:

    • a JWSBuilder,

    • a JWEBuilder,

    • a JWESerializerManager,

    • a JWSSerializerManager

    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,

    • Additional Authenticated Data.

    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.

    The key set configuration will look like as follow:

    The key set will be available as a container service with the ID jose.key_set.keyset_name where keyset_name is the unique name of your key set. Each key set service will be an instance of the Jose\Component\Core\JWKSet class.

    As any other configuration values, you can use environment variables.

    hashtag
    From A JWKSet Object

    This method will directly get a JWKSet object.

    hashtag
    Distant Key Sets

    You can load key sets shared by a distant service (e.g. Google, Microsoft, Okta...). You must install and enable the Httplug Bundlearrow-up-right.

    When done, you have to create a client and enable the JKU Factory service by indicating the request factory service to use:

    Important recommendations:

    • It is highly recommended to use a cache plugin for your HTTP client and thus avoid unnecessary calls to the key set endpoint.

    • The connection must be secured and certificate verification should not be disabled.

    hashtag
    From A JKU (JWK Url)

    The following example will allow you tu load a key set from a distant URI. The key set must be a JWKSet object.

    hashtag
    From A X5U (X509 Certificates Url)

    The following example will allow you tu load a key set from a distant URI. The key set must be a list of X509 certificates.

    hashtag
    Shared Key Sets

    It can be interesting to share your key sets through an Url. This can easily achieved by adding a dedicated controller. This controller is automatically created by the bundle.

    You can enable these routes by adding the following configuration to your routing file.

    Then you can share your key set.

    Now when you go to the URL http://128.0.0.1:8000/certs, you will get your key set.

    hashtag
    Custom Tags

    This feature was introduced in version 1.1.

    You can add custom tags and attributes to the services you create.

    <?php
    
    use Jose\Component\Core\JWK;
    use Jose\Component\Signature\Serializer\JWSSerializerManager;
    use Jose\Component\Signature\Serializer\CompactSerializer;
    
    // Our key.
    $jwk = new JWK([
        'kty' => 'oct',
        'k' => 'dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g',
    ]);
    
    // The serializer manager. We only use the JWS Compact Serialization Mode.
    $serializerManager = new JWSSerializerManager([
        new CompactSerializer(),
    ]);
    
    // The input we want to check
    $token = 'eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MDc4OTY5OTIsIm5iZiI6MTUwNzg5Njk5MiwiZXhwIjoxNTA3OTAwNTkyLCJpc3MiOiJNeSBzZXJ2aWNlIiwiYXVkIjoiWW91ciBhcHBsaWNhdGlvbiJ9.eycp9PTdgO4WA-68-AMoHPwsKDr68NhjIQKz4lUkiI0';
    
    // We try to load the token.
    $jws = $serializerManager->unserialize($token);
    
    // We verify the signature. This method does NOT check the header.
    // The arguments are:
    // - The JWS object,
    // - The key,
    // - The index of the signature to check. See 
    $isVerified = $jwsVerifier->verifyWithKey($jws, $jwk, 0);
    <?php
    
    use Jose\Component\Signature\JWSLoader;
    
    $jwsLoader = new JWSLoader(
        $serializerManager,
        $jwsVerifier,
        $headerCheckerManager
    );
    
    $jws = $jwsLoader->loadAndVerifyWithKey($token, $jwk, $signature, $payload);
    <?php
    
    use Jose\Component\Signature\JWSLoaderFactory;
    
    $jwsLoaderFactory = new JWSLoaderFactory(
        $jwsSerializerManagerFactory,
        $jwsVerifierFactory,
        $headerCheckerManagerFactory
    );
    
    $jwsLoader = $jwsLoaderFactory->create(
        ['jws_compact'], // List of serializer aliases
        ['HS256'],       // List of signature algorithm aliases
        ['alg']          // Optional list of header checker aliases
    );
    App\EventSubscriber\JwsSubscriber.php
    <?php
    
    declare(strict_types=1);
    
    namespace App\EventSubscriber;
    
    use Jose\Bundle\JoseFramework\Event\Events;
    use Jose\Bundle\JoseFramework\Event\JWSBuiltSuccessEvent;
    use Jose\Bundle\JoseFramework\Event\JWSVerificationFailureEvent;
    use Jose\Bundle\JoseFramework\Event\JWSVerificationSuccessEvent;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    
    class JwsSubscriber implements EventSubscriberInterface
    {
        public static function getSubscribedEvents()
        {
            return [
                Events::JWS_VERIFICATION_SUCCESS => ['onJwsVerificationSuccess'],
                Events::JWS_VERIFICATION_FAILURE => ['onJwsVerificationFailure'],
                Events::JWS_BUILT_SUCCESS => ['onJwsBuiltSuccess'],
                Events::JWS_BUILT_FAILURE => ['onJwsBuiltFailure'],
            ];
        }
    
        public function onJwsVerificationSuccess(JWSVerificationSuccessEvent $event): void
        {
            // Do something here
        }
    
        public function onJwsVerificationFailure(JWSVerificationFailureEvent $event): void
        {
            // Do something here
        }
    
        public function onJwsBuiltSuccess(JWSBuiltSuccessEvent $event): void
        {
            // Do something here
        }
    
        public function onJwsBuiltFailure(JWSBuiltFailureEvent $event): void
        {
            // Do something here
        }
    }
    <?php
    
    use Jose\Component\Core\AlgorithmManager;
    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 = new AlgorithmManager([
        new HS256(),
    ]);
    
    // Our key.
    $jwk = new JWK([
        'kty' => 'oct',
        'k' => 'dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g',
    ]);
    
    // The serializer manager. We only use the JWS Compact Serialization Mode.
    $serializerManager = new JWSSerializerManager([
        new CompactSerializer(),
    ]);
    
    // 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);
    jose: # Configuration of the JWT Framework
        keys: # Configuration of the keys
            key_name: # Unique key name
                method_name: # Name of the method
                    ...
                    is_public: true
    jose:
        keys:
            key_name:
                secret: # Method
                    secret: 'This is my shared secret'
                    additional_values:
                        use: 'sig'
                        alg: 'RS512'
    jose:
        keys:
            key_name:
                jwk: # Method
                    value: '{"kty":"oct","k":"dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g"}'
    jose:
        keys:
            key_name:
                certificate: # Method
                    path: '/path/to/your/X509/certificate'
                    additional_values: # Optional values
                        use: 'sig'
                        alg: 'RS256'
    jose:
        keys:
            key_name:
                x5c: # Method
                    value: '-----BEGIN CERTIFICATE----- ....'
                    additional_values: # Optional values.
                        use: 'sig'
                        alg: 'RS256'
    jose:
        keys:
            key_name:
                file: # Method
                    path: '/path/to/your/key/file'
                    password: 'secret' # Optional. Only if the key is encrypted
                    additional_values: # Optional values.
                        use: 'sig'
                        alg: 'RS256'
    jose:
        keys:
            key_name:
                jwkset: # Method
                    key_set: 'jose.key_set.my_key_set' # JWKSet service
                    index: 0 # Use key at index 0
    jose:
        jwe:
            key_name:
                jwk: # Method
                    value: '{"kty":"oct","k":"dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g"}'
                    tags:
                        tag_name1: ~
                        tag_name2: {attribute1: 'foo'}
    composer require web-token/jwt-checker
    <?php
    
    use Jose\Component\Checker\ClaimCheckerManager;
    use Jose\Component\Checker;
    
    $claimCheckerManager = new ClaimCheckerManager(
        [
            new Checker\IssuedAtChecker(),
            new Checker\NotBeforeChecker(),
            new Checker\ExpirationTimeChecker(),
            new Checker\AudienceChecker('Audience'),
        ]
    );
    $claims = json_decode($jwt->getPayload(), true);
    $claimCheckerManager->check($claims);
    $claimCheckerManager->check($claims, ['iss', 'sub', 'aud']);
    <?php
    
    declare(strict_types=1);
    
    namespace Acme\Checker;
    
    use Jose\Component\Checker\ClaimChecker;
    use Jose\Component\Checker\InvalidClaimException;
    
    /**
     * Class FooChecker.
     */
    final class FooChecker implements ClaimChecker
    {
        /**
         * {@inheritdoc}
         */
        public function checkClaim($value)
        {
            if (!is_string($value)) { // If the value is not a string, then we throw an exception
                throw new InvalidClaimException('The claim "foo" must be a string.', 'foo', $value);
            }
            if (!in_array($value, ['bar', 'bat'])) { // Check if the value is allowed
                throw new InvalidClaimException('The claim "foo" must be "bar" or "bat".', 'foo', $value);
            }
        }
    
        /**
         * {@inheritdoc}
         */
        public function supportedClaim(): string
        {
            return 'foo'; //The claim to check.
        }
    }
    <?php
    
    use Jose\Component\Checker\ClaimCheckerManagerFactory;
    use Jose\Component\Checker;
    
    $claimCheckerManagerFactory = new ClaimCheckerManagerFactory();
    $claimCheckerManagerFactory->add('iat', new Checker\IssuedAtChecker());
    $claimCheckerManagerFactory->add('nbf', new Checker\NotBeforeChecker());
    $claimCheckerManagerFactory->add('exp', new Checker\ExpirationTimeChecker());
    $claimCheckerManagerFactory->add('aud1', new Checker\AudienceChecker('Audience for service #1'));
    $claimCheckerManagerFactory->add('aud2', new Checker\AudienceChecker('Audience for service #2'));
    
    $claimCheckerManager1 = $claimCheckerManagerFactory->create(['iat', 'exp', 'aud2']);
    $claimCheckerManager2 = $claimCheckerManagerFactory->create(['aud1', 'exp']);
    use Jose\Component\NestedToken\NestedTokenLoader;
    
    $nestedTokenLoader = new NestedTokenLoader($jweLoader, $jwsLoader);
    $jws = $nestedTokenLoader->load($token, $encryptionKeySet, $signatureKeySet, $signature);
    use Jose\Component\NestedToken\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, []);
        }
    }
    jose: # Configuration of the JWT Framework
        key_sets: # Configuration of the keys
            keyset_name: # Unique key name
                method_name: # Name of the method
                    ...
                    is_public: true
    jose:
        key_sets:
            key_name:
                jwkset: # Method
                    value: '{"keys":[{"kty":"oct","k":"dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g"},{"kty":"oct","k":"bwIAv5Nn-fo8p4LCEvM4IR9eLXgzJRs8jXCLb3xR0tDJGiZ46KheO4ip6htFKyN2aqJqlNi9-7hB6I1aLLy1IRT9-vcBoCSGu977cNAUuRLkRp7vo8s6MsxhB8WvQBDRZghV7jIYaune-3vbE7iDU2AESr8BUtorckLoO9uW__fIabaa3hJMMQIHCzYQbJKZvlCRCKWMk2H_zuS4JeDFTvyZH1skJYF_TET1DrCZHMPicw-Yk3_m2P-ilC-yidPPoVzeU8Jj3tQ6gtX3975qiQW7pt2qbgjKAuq2wsz_9hxLBtMB5rQPafFoxop7O4BklvZ9-ECcK6dfI2CAx9_tjQ"}]}'
    httplug: # Example of client configuration
        plugins:
            cache: # We use the cache plugin
                cache_pool: 'cache.app' # We use the PSR-6 Cache service of the application
                config:
                    default_ttl: 1800 # TTL set to 30 min
        clients:
            acme:
                factory: 'httplug.factory.guzzle6'
                plugins: ['httplug.plugin.cache'] # We enable the cache plugin for that client.
    
    jose:
        jku_factory:
            enabled: true
            client: 'httplug.client.acme' # The Httplug client
            request_factory: 'httplug.message_factory' # In general, you will use the same message factory as the one used by Httplug
    jose:
        key_sets:
            key_name:
                jku: # Method
                    url: 'https://login.microsoftonline.com/common/discovery/keys'
    jose:
        key_sets:
            key_name:
                x5u: # Method
                    url: 'https://www.googleapis.com/oauth2/v1/certs'
    # config/routes.yaml
    jwkset_endpoints:
        resource: "@JoseFrameworkBundle/Resources/config/routing/jwkset_controller.php"
    jose:
        key_sets:
            public_keyset: # The key set we want to share
                jwkset:
                    value: '{"keys":[{"kty":"OKP","crv":"X25519","x":"ovuZiVcMXBN4r0VgCvJy_ChAsBv4YPJGC5w56PzndXY"},{"kty":"OKP","crv":"X25519","x":"4qyOJ4T9RkdciIn6LDxb2LdM1Ov-dtBSuj0jh6nCuyc"}]}'
        jwk_uris:
            shared_keyset:
                id: 'jose.key_set.public_keyset' # The key set service to share
                path: '/certs' # Path of the key set. Final path is hostname/route_prefix/path: https://www.foo.com/keys/certs
    jose:
        key_sets:
            key_name:
                jku: # Method
                    url: 'https://login.microsoftonline.com/common/discovery/keys'
                    tags:
                        tag_name1: ~
                        tag_name2: {attribute1: 'foo'}
    Let say you want to create a JWK as a service:

    For the key configuration, the arguments are:

    • The container

    • The name of the service (acme_my_key)

    • The key type (jwk)

    • An array with the expected values

    • An array with the custom tags (optional)

    Now a key service named jose.key.acme_my_key will be created. This service is public so you will be able to get it from your container or inject it to your services.

    This is exactly the same configuration as the following one:

    Other methods are:

    • For the jws section:

      • public static function addJWSBuilder(ContainerBuilder $container, string $name, array $signatureAlgorithms, bool $is_public = true, array $tags = [])

      • public static function addJWSVerifier(ContainerBuilder $container, string $name, array $signatureAlgorithms, bool $is_public = true, array $tags = [])

      • public static function addJWSSerializer(ContainerBuilder $container, string $name, array $serializers, bool $is_public = true, array $tags = [])

    • For the jwe section:

      • public static function addJWEBuilder(ContainerBuilder $container, string $name, array $keyEncryptionAlgorithm, array $contentEncryptionAlgorithms, array $compressionMethods = ['DEF'], bool $is_public = true, array $tags = [])

    • For the checker section:

      • public static function addClaimChecker(ContainerBuilder $container, string $name, array $claimCheckers, bool $is_public = true, array $tags = [])

      • public static function addHeaderChecker(ContainerBuilder $container, string $name, array $headerCheckers, bool $is_public = true, array $tags = [])

    • For the keys section:

      • public static function addKey(ContainerBuilder $container, string $name, string $type, array $parameters, array $tags = [])

    • For the key_sets section:

      • public static function addKeyset(ContainerBuilder $container, string $name, string $type, array $parameters, array $tags = [])

    • For the jwk_uris section:

      • public static function addKeyUri(ContainerBuilder $container, string $name, array $parameters, array $tags = [])

    Have a look at the spomky-labs/lexik-jose-bridge extensionarrow-up-right to see how we configure the Jose Bundle without dedicated configuration

    How To

    You can easily check if an algorithm is fast enough on your platform. This project has tests for all operations with (almost) all algorithms.

    hashtag
    All algorithms

    To run those tests, you must install the library with all dev dependencies. When done, just run the following command:

    The previous command will test ALL algorithms and may take more than 7 hours.

    <?php
    
    declare(strict_types=1);
    
    namespace AcmeBundle\DependencyInjection;
    
    use Jose\Bundle\JoseFramework\Helper\ConfigurationHelper;
    use Symfony\Component\DependencyInjection\ContainerBuilder;
    use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
    use Symfony\Component\HttpKernel\DependencyInjection\Extension;
    
    final class AcmeExtension extends Extension implements PrependExtensionInterface
    {
        ...
    
        /**
         * {@inheritdoc}
         */
        public function prepend(ContainerBuilder $container)
        {
            ... // The Helper will be called here
        }
    }
    ConfigurationHelper::addKey(
        $container,
        'acme_my_key',
        'jwk', [
            'value' => '{"kty":"oct","k":"dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g"}',
            'is_public' => true,
        ],
        [
            'tag_name1' => [],
            'tag_name2' => ['attribute1' => 'foo'],
        ]
    );
    jose:
        keys:
            acme_my_key:
                jwk:
                    value: '{"kty":"oct","k":"dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g"}'
                    is_public: true
                    tags:
                        tag_name1: ~
                        tag_name2: {attribute1: 'foo'}
    public static function addJWEDecrypter(ContainerBuilder $container, string $name, array $keyEncryptionAlgorithm, array $contentEncryptionAlgorithms, array $compressionMethods = ['DEF'], bool $is_public = true, array $tags = [])
  • public static function addJWESerializer(ContainerBuilder $container, string $name, array $serializers, bool $is_public = true, array $tags = [])

  • hashtag
    Algorithm Selection

    We recommend you to run tests only for the algorithm(s) you want to use. Just run the following command:

    The value of GROUP should be one of the following values:

    • All signature algorithms: JWS

      • All HMAC algorithms: hmac

        • HS256: HS256

        • HS384: HS384

        • HS512: HS512

      • All Elliptic Curves algorithms: ECDSA

        • ES256: ES256

        • ES384: ES384

      • All Edwards-curve algorithms: EdDSA

        • Ed25519: Ed25519

      • All RSA algorithms: RSASign

        • RS256: RS256

        • RS384: RS384

    • All key encryption algorithms: JWE

      • KW

        • A128KW:

    • Note 1: the dir algorithm is not tested as there is no key encryption or key decryption with this algorithm.

    • Note 2: tests consist in a full JWS/JWE creation and loading and sometimes using multiple key sizes.

    • Note 3: for JWE creation and loading, each key encryption algorithm is tested with the following content encryption algorithms:

      • A128GCM, A192GCM and A256GCM

    hashtag
    Examples

    Test all HMAC algorithms:

    Test the RSA1_5 algorithm:

    hashtag
    Benchmark Details

    The result of the following command will only give you partial result. For example:

    The main information is that the best algorithm takes only 31.275 µs... but you may need more information.

    Just run the following command:

    *The value of the --uuid option is given at the end of the previous command. You can also use latest.

    A report.md file will be created. It contains a detailed report (Markdown format). Example:

    If it is not enough for you, you can get a full report with the following command:

    Key (JWK)

    You can create a JWK object using two static methods:

    • new JWK(array $values): creates a JWK using direct values.

    • JWK::createFromJson(string $json): creates a JWK using a JSON object.

    Hereafter all methods available for a JWK object. The variable $jwk is a valid JWK object.

    circle-exclamation

    Please note a JWK object is an immutable object

    hashtag
    Generate A New Key

    This framework is able to create private and public keys easily using the JWKFactory. It is available in the web-token/jwt-key-mgmt component.

    4 types of keys are supported:

    • Symmetric Key:

      • oct: octet string

    • Asymmetric Key:

    circle-info

    The none algorithm needs a key of type none. This is a specific key type that must only be used with this algorithm.

    hashtag
    Octet String

    The following example will show you how to create an oct key.

    Additional parameters will be set to limit the scope of this key (e.g. signature/verification only with the HS256 algorithm).

    If you already have a shared secret, you can use it to create an oct key:

    hashtag
    RSA Key Pair

    The following example will show you how to create a RSA key.

    The key size must be of 384 bits at least.

    hashtag
    Elliptic Curve Key Pair

    The following example will show you how to create a EC key.

    The supported curves are:

    • P-256

    • P-384

    • P-521 (note that this is 521

    hashtag
    Octet Key Pair

    The following example will show you how to create a OKP key.

    The supported curves are:

    • Ed25519 for signature/verification only

    • X25519 for encryption/decryption only

    hashtag
    None Key

    The none key type is a special type used only for the none algorithm.

    hashtag
    Create Key From External Sources

    hashtag
    From Values

    In case you already have key values, you can create a key by passing those values as an argument:

    hashtag
    From A Key File

    You can convert a PKCS#1 or PKCS#8 key file into a JWK. The following method supports PEM and DER formats. Encrypted keys are also supported.

    hashtag
    From A PKCS#12 Certificate

    You can convert a PKCS#12 Certificate into a JWK. Encrypted certificates are also supported.

    hashtag
    From A X.509 Certificate

    You can convert a X.509 Certificate into a JWK.

    circle-info

    Please note that X.509 certificates only contains public keys.

    The "Easy" Way

    In version 2.1, an "Easy" component will be released. With this component you will be able to produce and consume tokens an easy way.

    • Step 1: install the package web-token/jwt-easy

    • Step 2: install the signature or encryption algorithms you want to use. e.g., web-token/jwt-signature-algorithm-rsa. if you do not, you may encounter an error like "The algorithm "ES256" is not supported."

    • Step 3: create your scripts.

    hashtag
    JWS Creation And Verification

    In the following example, we will create a signed token (JWS) with a set of standard and custom claims and headers.

    A token you receive can be read and verified. Verification is done on the signature and the claims or header parameters you want.

    If everything is ok, the variable $jwt contains a Jose\Easy\JWT object. This object has 2 properties: header and claims containing the loaded values.

    hashtag
    JWE Creation And Decryption

    The JWE creation and loading is very similar to the JWS process as they use common methods. In the following example, we will create an encrypted token (JWE) with a set of standard and custom claims and headers.

    The encrypted tokens can be decrypted the same way as the signed ones.

    If everything is ok, the variable $jwt contains a Jose\Easy\JWT object.

    Serialization

    The RFC7515arrow-up-right (JWS) and RFC7516arrow-up-right (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.

    Console

    The project comes with console commands. They are available:

    ./vendor/bin/phpbench run --store
    ./vendor/bin/phpbench run --group GROUP --store
    ./vendor/bin/phpbench run --group hmac --store
    ./vendor/bin/phpbench run --group RSA1_5 --store
    PhpBench 0.13.0. Running benchmarks.
    Using configuration file: phpbench.json
    
    ............
    
    12 subjects, 12 iterations, 12,000 revs, 0 rejects
    (best [mean mode] worst) = 31.275 [86.783 86.783] 31.275 (μs)
    ⅀T: 1,041.395μs μSD/r 0.000μs μRSD/r: 0.000%
    Storing results ... OK
    Run: 133c8a853cf321f0b7b63e4e60f819f9910e1285
    ./vendor/bin/phpbench report --report=simple --output=md --uuid=133c8a853cf321f0b7b63e4e60f819f9910e1285
    Jose Performance Test Suite
    ===========================
    
    ### suite: 133c8a853cf321f0b7b63e4e60f819f9910e1285, date: 2017-09-20, stime: 22:05:45
    
    benchmark | groups | subject | mean
     --- | --- | --- | --- 
    HS256Bench | JWS,hmac,HS256 | benchSignature | 104.450μs
    HS256Bench | JWS,hmac,HS256 | benchVerification | 161.093μs
    HS384Bench | JWS,hmac,HS384 | benchSignature | 112.788μs
    HS384Bench | JWS,hmac,HS384 | benchVerification | 161.978μs
    HS512Bench | JWS,hmac,HS512 | benchSignature | 105.686μs
    HS512Bench | JWS,hmac,HS512 | benchVerification | 163.139μs
    ./vendor/bin/phpbench report --report=default --output=md --uuid=133c8a853cf321f0b7b63e4e60f819f9910e1285
  • ES512: ES512

  • RS512: RS512

  • PS256: PS256

  • PS384: PS384

  • PS512: PS512

  • A128KW
  • A192KW: A192KW

  • A256KW: A256KW

  • GCMKW

    • A128GCMKW: A128GCMKW

    • A192GCMKW: A192GCMKW

    • A256GCMKW: A256GCMKW

  • ECDHES

    • ECDH-ES: ECDHES

    • ECDHESKW

      • ECDHESA128KW: ECDH-ES+A128KW

      • ECDHESA192KW: ECDH-ES+A192KW

      • ECDHESA256KW: ECDH-ES+A256KW

  • PBES2

    • PBES2-HS256+A128KW: PBES2HS256A128KW

    • PBES2-HS384+A192KW: PBES2HS384A192KW

    • PBES2-HS512+A256KW: PBES2HS512A256KW

  • RSAEnc

    • RSA1_5: RSA1_5

    • RSA-OAEP: RSA-OAEP

    • RSA-OAEP-256: RSA-OAEP-256

  • A128CBC-HS256, A192CBC-HS384 and A256CBC-HS512

    RSA: RSA key pair

  • EC : Elliptic Curve key pair

  • OKP: Octet key pair

  • and not 512)
    as a PHAR (PHP Archive)

    hashtag
    Available Commands

    In the following example, we will call commands using ./jose.phar. If you need more information about a command, call the command with the option --help.

    You can save the output in a file e.g. when you want to store a key or keyset in your local filesystem.

    hashtag
    Key Management Commands

    hashtag
    Private Key To Public Key Converter

    This command will convert a private key into a public key. It has no effect on shared keys (e.g. oct keys).

    hashtag
    Key Analyze

    The following command will analyze the key passed as argument and find issues.

    hashtag
    PKCS#1 Key Converter

    This command will convert a RSA or EC key into PKCS#1 key.

    hashtag
    Key Generators

    The key generator commands will generate a private or shared key. The following options are available:

    • -u or --use: indicates the usage of the key (sig or enc): --use enc. This option is highly recommended.

    • -a or --alg: indicates the algorithm to be used with the key: --alg RSA-OAEP-256. This option is highly recommended.

    Elliptic Curve Key

    This command will generate an Elliptic Curve key (EC). The supported curves are P-256, P-384 and P-521.

    RSA Key

    This command will generate a RSA key. The key size must be at least 384 bits. Recommended size is 2048 bits or more.

    Octet Key

    This command will generate a octet key (oct). Recommended size is 128 bits or more.

    Octet Key Pair Key

    This command will generate a octet key pair key (OKP). Supported curves are X25519 (for encryption only) and Ed25519 (signature only).

    None Key

    This command will generate a none key. This key type is only used by the none algorithm. Key parameters alg and use are automatically set.

    From An Existing Secret

    If you already have a secret, you can use it to create an octet key (oct).

    In case your secret is binary string, you will have to encode it first (Base64) and indicate it is encoded.

    hashtag
    Key Loaders

    The key loader commands will loader keys from various sources. The following options are available:

    • -u or --use: indicates the usage of the key (sig or enc): --use enc. This option is highly recommended.

    • -a or --alg: indicates the algorithm to be used with the key: --alg RSA-OAEP-256. This option is highly recommended.

    Convert From PEM/DER Keys

    This command can load and convert a DER/PEM key file into a JWK. It supports encrypted keys as well as PKCS#1 and PKCS#8 encodings or public/private keys.

    Convert From PKCS#12 Keys

    This command can load and convert a PKCS#12 key file into a JWK. It supports encrypted keys.

    Convert From A X.509 Certificate

    This command can load and convert a X.509 key file into a JWK.

    hashtag
    RSA Key Optimization

    This command optimizes a RSA key by calculating additional primes (CRT). The following option is available:

    RSA keys generated by this framework are already optimized. This command may be needed when you import RSA keys from external sources. The optimization is not mandatory but highly recommended. cryptographic operations are up to 10 times faster.

    hashtag
    Key Thumbprint

    This command will calculate the key thumbprint as per the RFC7638arrow-up-right. The following options are available:

    • --hash: the hashing method. Default is sha256. Supported methods are the one listed by hash_algosarrow-up-right.

    hashtag
    Keyset Management Commands

    hashtag
    Private Keys To Public Keys Converter

    This command has the same affect as key:convert:public except that it will convert all keys in the keyset. It has no effect on shared keys (e.g. oct keys).

    hashtag
    Key Analyze

    This command has the same behaviour as key:analyze except that it will analize all keys in the keyset.

    hashtag
    Keyset Generators

    The key set generator commands will generate key sets with random keys of the same type.

    These commands have the same options as the key generator commands. The only difference is that you have to indicate the number of keys you want in the key set.

    Examples:

    The result of these commands is a JWKSet object.

    hashtag
    Key Set Modification

    • keyset:add:key: Add a key into a key set.

    • keyset:merge: Merge several key sets into one.

    • keyset:rotate: Rotate a key set.

    hashtag
    Distant Key Set Loading

    • keyset:load:jku: Loads a key set from an url.

    • keyset:load:x5u: Loads a key set from an url.

    as a standalone command
    through the dedicated Symfony console command
    <?php
    // Check if the key has a parameter.
    $jwk->has('kty');
    
    // Retrieve the key parameter.
    $jwk->get('kty');
    
    // Retrieve all key parameters.
    $jwk->all();
    
    // Calculate the thumbprint of the key. Acceptable hash algorithms are those returned by the PHP function "hash_algos".
    $jwk->thumbprint('sha256');
    
    // If the key is a private key (RSA, EC, OKP), it can be converted into public:
    $public_key = $jwk->toPublic();
    
    // The JWK object can be serialized into JSON
    json_encode($jwk);
    composer require web-token/jwt-key-mgmt
    <?php
    
    use Jose\Component\KeyManagement\JWKFactory;
    
    $key = JWKFactory::createOctKey(
        1024, // Size in bits of the key. We recommend at least 128 bits.
        [
            'alg' => 'HS256', // This key must only be used with the HS256 algorithm
            'use' => 'sig'    // This key is used for signature/verification operations only
        ]
    );
    <?php
    
    use Jose\Component\KeyManagement\JWKFactory;
    
    $jwk = JWKFactory::createFromSecret(
        'My Secret Key',       // The shared secret
        [                      // Optional additional members
            'alg' => 'HS256',
            'use' => 'sig'
        ]
    );
    <?php
    
    use Jose\Component\KeyManagement\JWKFactory;
    
    $private_key = JWKFactory::createRSAKey(
        4096, // Size in bits of the key. We recommend at least 2048 bits.
        [
            'alg' => 'RSA-OAEP-256', // This key must only be used with the RSA-OAEP-256 algorithm
            'use' => 'enc'    // This key is used for encryption/decryption operations only
        ]);
    <?php
    
    use Jose\Component\KeyManagement\JWKFactory;
    
    $key = JWKFactory::createECKey('P-256');
    <?php
    
    use Jose\Component\KeyManagement\JWKFactory;
    
    $key = JWKFactory::createOKPKey('X25519');
    <?php
    
    use Jose\Component\KeyManagement\JWKFactory;
    
    $key = JWKFactory::createNoneKey();
    <?php
    
    use Jose\Component\KeyManagement\JWKFactory;
    
    $key = JWKFactory::createFromValues([
        'kid' => '71ee230371d19630bc17fb90ccf20ae632ad8cf8',
        'kty' => 'RSA',
        'alg' => 'RS256',
        'use' => 'sig',
        'n' => 'vnMTRCMvsS04M1yaKR112aB8RxOkWHFixZO68wCRlVLxK4ugckXVD_Ebcq-kms1T2XpoWntVfBuX40r2GvcD9UsTFt_MZlgd1xyGwGV6U_tfQUll5mKxCPjr60h83LXKJ_zmLXIqkV8tAoIg78a5VRWoms_0Bn09DKT3-RBWFjk=',
        'e' => 'AQAB',
    ]);
    <?php
    
    use Jose\Component\KeyManagement\JWKFactory;
    
    $key = JWKFactory::createFromKeyFile(
        '/path/to/my/key/file.pem', // The filename
        'Secret',                   // Secret if the key is encrypted
        [
            'use' => 'sig',         // Additional parameters
        ]
    );
    <?php
    
    use Jose\Component\KeyManagement\JWKFactory;
    
    $key = JWKFactory::createFromPKCS12CertificateFile(
        '/path/to/my/key/file.p12', // The filename
        'Secret',                   // Secret if the key is encrypted
        [
            'use' => 'sig',         // Additional parameters
        ]
    );
    <?php
    
    use Jose\Component\KeyManagement\JWKFactory;
    
    $key = JWKFactory::createFromCertificateFile(
        '/path/to/my/key/file.crt', // The filename
        [
            'use' => 'sig',         // Additional parameters
        ]
    );
    use Jose\Component\Core\JWK;
    use Jose\Easy\Build;
    
    $time = time(); // The current time
    
    $jwk = new JWK([
        'kty' => 'RSA',
        'kid' => '[email protected]',
        'use' => 'sig',
        'n' => 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw',
        'e' => 'AQAB',
        'd' => 'bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ',
        'p' => '3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k',
        'q' => 'uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc',
        'dp' => 'B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik',
        'dq' => 'CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8',
        'qi' => '3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4',
    ]);
    $jws = Build::jws() // We build a JWS
        ->exp($time + 3600) // The "exp" claim
        ->iat($time) // The "iat" claim
        ->nbf($time) // The "nbf" claim
        ->jti('0123456789', true) // The "jti" claim.
                                  // The second argument indicate this pair shall be duplicated in the header
        ->alg('RS512') // The signature algorithm. A string or an algorithm class.
        ->iss('issuer') // The "iss" claim
        ->aud('audience1') // Add an audience ("aud" claim)
        ->aud('audience2') // Add another audience
        ->sub('subject') // The "sub" claim
        ->claim('https://example.com/isRoot', true)
        ->header('prefs', ['field1', 'field7'])
        ->sign($jwk) // Compute the token with the given JWK
    ;
    
    //You can directly set the payload and the header at once
    
    $jws = Build::jws() // We build a JWS
        ->payload([
            'exp' => $time + 3600, // The "exp" claim
            'iat' => $time, // The "iat" claim
            'nbf' => $time, // The "nbf" claim
            'jti' => '0123456789', // The "jti" claim
            'iss' => 'issuer', // The "iss" claim
            'aud' => ['audience1', 'audience2'], // Add an audience ("aud" claim)
            'sub' => 'subject', // The "sub" claim
            'https://example.com/isRoot' => true,
        ])
        ->header('prefs', ['field1', 'field7'])
        ->header('jti', '0123456789'),
        ->header('alg', 'RS512')
        ->sign($jwk)
    ;
    
    echo $jws; // The variable $jws now contains your token
    use Jose\Component\Core\JWK;
    use Jose\Easy\Load;
    
    $jwk = new JWK([
        'kty' => 'RSA',
        'kid' => '[email protected]',
        'use' => 'sig',
        'n' => 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw',
        'e' => 'AQAB'
    ]);
    $jwt = Load::jws($token) // We want to load and verify the token in the variable $token
        ->algs(['RS256', 'RS512']) // The algorithms allowed to be used
        ->exp() // We check the "exp" claim
        ->iat(10) // We check the "iat" claim. Leeway is 10s
        ->nbf() // We check the "nbf" claim
        ->aud('audience1') // Allowed audience
        ->iss('issuer') // Allowed issuer
        ->sub('subject') // Allowed subject
        ->jti('0123456789') // Token ID
        ->key($jwk) // Key used to verify the signature
        ->run() // Go!
    ;
    $jwt->claims->all(); // All claims (array)
    $jwt->claims->iat(); // = $jwt->claims->get('iat');
    $jwt->claims->nbf();
    $jwt->claims->exp();
    $jwt->claims->jti(); // '0123456789'
    $jwt->claims->iss(); // 'issuer'
    $jwt->claims->sub(); // 'subject'
    $jwt->claims->aud(); // ['audience1', 'audience2']
    $jwt->claims->get('https://example.com/isRoot'); // true
    
    $jwt->header->all(); // ['jti' => '0123456789', 'alg' => 'RS512', 'crit' => ['alg']]
    $jwt->header->alg(); // 'RS512'
    $jwt->header->jti(); // '0123456789'
    use Jose\Component\Core\JWK;
    use Jose\Easy\Build;
    
    $time = time(); // The current time
    
    $jwk = new JWK([
        'kty' => 'RSA',
        'kid' => '[email protected]',
        'use' => 'enc',
        'n' => 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw',
        'e' => 'AQAB',
    ]);
    $jwe = Build::jwe() // We build a JWE
        ->exp($time + 3600)
        ->iat($time)
        ->nbf($time)
        ->jti('0123456789', true)
        ->iss('issuer')
        ->aud('audience1')
        ->aud('audience2')
        ->sub('subject')
        ->alg('RSA-OAEP-256') // The "alg" header parameter corresponds to the key encryption algorithm
        ->enc('A256GCM')      // The "enc" header parameter corresponds to the content encryption algorithm
        ->zip('DEF')          // We compress the payload (optional. Only recommended for large set of data)
        ->claim('is_root', true) // Custom claims
        ->claim('roles', ['ROLE1' => true, 'ROLE2' => 0.007])
        ->crit(['alg', 'enc']) // We mark some header parameters as critical
        ->encrypt($jwk) // Compute the token with the given JWK (public key)
    ;
    
    echo $jwe; // The variable $jwe now contains your token
    use Jose\Component\Core\JWK;
    use Jose\Easy\Load;
    
    $jwk = new JWK([
        'kty' => 'RSA',
        'kid' => '[email protected]',
        'use' => 'sig',
        'n' => 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw',
        'e' => 'AQAB',
        'd' => 'bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ',
        'p' => '3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k',
        'q' => 'uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc',
        'dp' => 'B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik',
        'dq' => 'CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8',
        'qi' => '3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4',
    ]);
    $jwt = Load::jwe($token) // We want to load and decrypt the token in the variable $token
        ->algs(['RSA-OAEP-256', 'RSA-OAEP']) // The key encryption algorithms allowed to be used
        ->encs(['A256GCM']) // The content encryption algorithms allowed to be used
        ->exp()
        ->iat()
        ->nbf()
        ->aud('audience1')
        ->iss('issuer')
        ->sub('subject')
        ->jti('0123456789')
        ->key($jwk) // Key used to decrypt the token
        ->run() // Go!
    ;
    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\Signature\Serializer;
    
    $manager = Serializer\JWSSerializerManager::create([
        new Serializer\CompactSerializer(),
        new Serializer\JSONFlattenedSerializer(),
        new Serializer\JSONGeneralSerializer(),
    ]);
    
    // 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\Encryption\Serializer;
    
    $manager = Serializer\JWESerializerManager::create([
        new Serializer\CompactSerializer(),
        new Serializer\JSONFlattenedSerializer(),
        new Serializer\JSONGeneralSerializer(),
    ]);
    
    // 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);
    ./jose.phar key:convert:pkcs1 '{"kty":"EC","crv":"P-256","d":"kiNCxSbRjlAbHrEbrwVKS8vIXUh6URChrmw","x":"-wdLWDWCZP6oFYl8aGVfU0MsFlckjaSVrO7hEsc8lgk","y":"rt8XDTalLMCRB5Tu9WQc2d0TOVwXXHkVDbI7cIig6r4"}' > key.pem
    ./jose.phar key:convert:public '{"kty":"EC","crv":"P-256","d":"kiNCxSbRjlAbHrEbrwVKS8vIXUh6URChrmw","x":"-wdLWDWCZP6oFYl8aGVfU0MsFlckjaSVrO7hEsc8lgk","y":"rt8XDTalLMCRB5Tu9WQc2d0TOVwXXHkVDbI7cIig6r4"}'
    
    {"kty":"EC","crv":"P-256","x":"-wdLWDWCZP6oFYl8aGVfU0MsFlckjaSVrO7hEsc8lgk","y":"rt8XDTalLMCRB5Tu9WQc2d0TOVwXXHkVDbI7cIig6r4"}
    ./jose.phar key:analyze '{"kty":"oct","k":"N2aIJSQCxTo"}'
    
    The parameter "alg" should be added.
    The parameter "use" should be added.
    The parameter "kid" should be added.
    The key length is less than 128 bits.
    ./jose.phar key:convert:pkcs1 '{"kty":"EC","crv":"P-256","d":"kiNCxSbRjlAbHrEbrwVKS8vIXUh6URChrmw","x":"-wdLWDWCZP6oFYl8aGVfU0MsFlckjaSVrO7hEsc8lgk","y":"rt8XDTalLMCRB5Tu9WQc2d0TOVwXXHkVDbI7cIig6r4"}'
    
    -----BEGIN EC PRIVATE KEY-----
    MHcCAQEEIJIjQsUm0Y5QGx6xG68N4GrprVrFSkvLyF1IelEQoa5soAoGCCqGSM49
    AwEHoUQDQgAE+wdLWDWCZP6oFYl8aGVfU0MsFlckjaSVrO7hEsc8lgmu3xcNNqUs
    wJEHlO71ZBzZ3RM5XBdceRUNsjtwiKDqvg==
    -----END EC PRIVATE KEY-----
    ./jose.phar key:generate:ec P-256
    
    {"kty":"EC","crv":"P-256","d":"BZ231BFhhHAhx-D4myu4O1hi-vUHnRqxoCsQKUKFNrA","x":"Tv5YeQuD1CWDbfre65kYX2Lq_MGnUq0Ek2yUFixy31M","y":"pj0FyoGaByyBlt5RbTHhBdgcC-S6cgxzLpxd6mGmsbM"}
    ./jose.phar key:generate:rsa 512
    
    {"kty":"RSA","n":"l1UPHqgOFThDUlfrP2DFnCwsD5ITls12nXer6A4YepUP_DnF9mFoXCkyflA_TOJtFiZW6NXWOY0NdE3YjzT-qQ","e":"AQAB","d":"CxVvxg8I-QTl6WIHGN09m_KgR4Ora6Agz-ez74sYv-GONPD3yjEWeAavdOsGK8iJX4Pe1Qss52VKddeKRQ9LAQ","p":"xkR0kbThGGD8HYtfPUv5Ds1zE5LlQvYgBiv15eOk9ns","q":"w2Xk86kiaRhXWXM8XPJ5Tn6bdTT6thzoqazuIO53SCs","dp":"RCBLibGEUvMoTiKQtChByRNRQl2MR2j48gXy9W42Rbc","dq":"T0x5910LxwUG5hl7ROluy6lcI9wFZ4Uh80JoPdspc5M","qi":"Z73ha9Fmg6s-rgRbF0dG0QMd1aY9g1i8qnAxXp3JMus"}
    ./jose.phar key:generate:oct 256
    
    {"kty":"oct","k":"kWpZXidz3sVVx2Jn1J-5ANXnA2IKwfIAY2CoBW1q7I0"}
    ./jose.phar key:generate:okp 256
    
    {"kty":"OKP","crv":"X25519","x":"TgTD7RS0KF3eU8HdTM6ACxu365uco3x2Cee9SBXiu2I","d":"BypCXV7KUai-zrwrdoAmgnHX6Kosw0sVpDVPwrXoNKY"}
    ./jose.phar key:generate:none
    
    {"kty":"none","use":"sig","alg":"none"}
    ./jose.phar key:generate:from_secret "This is my secret"
    ./jose.phar key:generate:from_secret "VGhpcyBpcyBteSBzZWNyZXQ=" --is_b64
    ./jose.phar key:load:key /path/to/file.pem "This is my secret to decrypt the key"
    
    {"kty":"OKP","crv":"X25519","x":"TgTD7RS0KF3eU8HdTM6ACxu365uco3x2Cee9SBXiu2I","d":"BypCXV7KUai-zrwrdoAmgnHX6Kosw0sVpDVPwrXoNKY"}
    ./jose.phar key:load:p12 /path/to/file.p12 "This is my secret to decrypt the key"
    
    {"kty":"OKP","crv":"X25519","x":"TgTD7RS0KF3eU8HdTM6ACxu365uco3x2Cee9SBXiu2I","d":"BypCXV7KUai-zrwrdoAmgnHX6Kosw0sVpDVPwrXoNKY"}
    ./jose.phar key:load:x509 /path/to/file.cert
    
    {"kty":"OKP","crv":"X25519","x":"TgTD7RS0KF3eU8HdTM6ACxu365uco3x2Cee9SBXiu2I"}
    ./jose.phar key:optimize '{"kty":"RSA","n":"l4mLzvr6ewIWrPvP6j5PYp0yPRhtkMW1F-dbQ1VWGoB_Mq5IIuflOo7W2ERyh71exUGkmvoesWL3zCtFIOnlxw","e":"AQAB","d":"lxh8oLq7el9QwNasL0JF4WwgJa7vwISB1v3Gj9LM8cpZPqXnPGPeoE5QAOUi1bJsIEqzHsR-rnLHsarlTfXMIQ"}'
    
    {"kty":"RSA","n":"l4mLzvr6ewIWrPvP6j5PYp0yPRhtkMW1F-dbQ1VWGoB_Mq5IIuflOo7W2ERyh71exUGkmvoesWL3zCtFIOnlxw","e":"AQAB","d":"lxh8oLq7el9QwNasL0JF4WwgJa7vwISB1v3Gj9LM8cpZPqXnPGPeoE5QAOUi1bJsIEqzHsR-rnLHsarlTfXMIQ","p":"w0WuNlrO16rSPKHQn02FsOwzczlchC9ZpdS-00JKOr8","q":"xqn5LMfXwhWK-RGlXkSUHKCPb-SLKV8f8p41pDkjvvk","dp":"NGGAtfvt-FROSQ1vFQyKjEcQFhyRALRi6-UBu1HQ76k","dq":"kUqaO4_kUcNjogivwqOxFsauYIzq4dT6Dnx6iqJnbDE","qi":"TwJ4WOG0r1q6vZ13Kze2HPXtlnllyq9ZfClrVwovC_I"}
    ./jose.phar key:thumbprint '{"kty":"RSA","n":"l4mLzvr6ewIWrPvP6j5PYp0yPRhtkMW1F-dbQ1VWGoB_Mq5IIuflOo7W2ERyh71exUGkmvoesWL3zCtFIOnlxw","e":"AQAB","d":"lxh8oLq7el9QwNasL0JF4WwgJa7vwISB1v3Gj9LM8cpZPqXnPGPeoE5QAOUi1bJsIEqzHsR-rnLHsarlTfXMIQ","p":"xqn5LMfXwhWK-RGlXkSUHKCPb-SLKV8f8p41pDkjvvk","q":"w0WuNlrO16rSPKHQn02FsOwzczlchC9ZpdS-00JKOr8","dp":"kUqaO4_kUcNjogivwqOxFsauYIzq4dT6Dnx6iqJnbDE","dq":"NGGAtfvt-FROSQ1vFQyKjEcQFhyRALRi6-UBu1HQ76k","qi":"dkguRXkQcrvYbvFcnmGrcjIs36FJa-1dtd7QCRYHTBo"}'
    
    gNur2UtA8NMAoxJfgMYhJqnuWR8u-60aeRbKtZwj4DE
    ./jose.phar keyset:convert:public '<keyset here>'
    
    <public keyset>
    ./jose.phar keyset:analyze '<keyset here>'
    
    <keyset analyze result>
    ./jose.phar keyset:generate:rsa 3 512 # Create 3 RSA keys (512 bits each)
    ./jose.phar keyset:generate:oct 5 128 # Create 5 oct keys (128 bits each)
    ./jose.phar keyset:generate:okp 2 X25519 # Create 2 OKP keys (curve X25519)
    ./jose.phar keyset:generate:ec 3 P-521 # Create 3 EC keys (curve P-521)

    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:

    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

    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

    X

    A128KW

    Key Management Mode

    Key Encryption

    Key Wrapping

    Direct Key Agreement

    Key Agreement with Key Wrapping

    Direct Encryption

    Key Encryption

    YES

    YES

    NO

    YES

    NO

    Key Wrapping

    YES

    X

    YES

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

    Result table

    The table hereafter is the result of all benchmarks with our development environment. It is given to help you to select the appropriate algorithms for your application.

    The use of the algorithm ECDH-ES with curves P-256, P-384 or P-521 is not recommended with PHP7.1 or 7.2. The cryptographic operations with those curves are done using a pure PHP function and hence very slow.

    The use of the RSA algorithms with a very long key (more that 4096 bits) is quite slow, but offers a good protection.

    The PBES2* algorithms are quite slow, but also offer a good protection (see ). Default salt size (512 bits) and iterations (4096) and custom values (256/1024) used for the tests. Those values can be configured if needed.

    223.170μs

    sign

    JWS,ECDSA,ES384

    941.535μs

    verify

    JWS,ECDSA,ES384

    1,075.417μs

    sign

    JWS,ECDSA,ES512

    504.271μs

    verify

    JWS,ECDSA,ES512

    826.615μs

    sign

    JWS,hmac,HS256

    19.593μs

    verify

    JWS,hmac,HS256

    24.045μs

    sign

    JWS,hmac,HS384

    20.061μs

    verify

    JWS,hmac,HS384

    24.672μs

    sign

    JWS,hmac,HS512

    19.838μs

    verify

    JWS,hmac,HS512

    24.935μs

    sign

    JWS,none

    14.021μs

    verify

    JWS,none

    17.317μs

    sign

    JWS,RSASign,PS256

    1,310.264μs

    verify

    JWS,RSASign,PS256

    121.113μs

    sign

    JWS,RSASign,PS384

    1,300.622μs

    verify

    JWS,RSASign,PS384

    119.065μs

    sign

    JWS,RSASign,PS512

    1,302.404μs

    verify

    JWS,RSASign,PS512

    117.445μs

    sign

    JWS,RSASign,RS256

    1,280.885μs

    verify

    JWS,RSASign,RS256

    106.382μs

    sign

    JWS,RSASign,RS384

    1,280.652μs

    verify

    JWS,RSASign,RS384

    297.263μs

    sign

    JWS,RSASign,RS512

    1,659.753μs

    verify

    JWS,RSASign,RS512

    119.476μs

    encryption/decryption

    JWE,GCMKW,A128GCMKW

    63.022μs, 60.639μs, 58.909μs (with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 respectively)

    encryption/decryption

    JWE,GCMKW,A128GCMKW

    48.335μs, 50.021μs, 49.393μs (with A128GCM, A192GCM, A256GCM respectively)

    encryption/decryption

    JWE,GCMKW,A192GCMKW

    59.719μs, 59.396μs, 60.329μs (with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 respectively)

    encryption/decryption

    JWE,GCMKW,A192GCMKW

    48.432μs, 49.295μs, 50.244μs (with A128GCM, A192GCM, A256GCM respectively)

    encryption/decryption

    JWE,GCMKW,A256GCMKW

    60.966μs, 60.621μs, 59.821μs (with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 respectively)

    encryption/decryption

    JWE,GCMKW,A256GCMKW

    48.894μs, 49.165μs, 49.224μs (with A128GCM, A192GCM, A256GCM respectively)

    encryption/decryption

    JWE,KW,A128KW

    159.758μs, 176.995μs, 210.580μs (with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 respectively)

    encryption/decryption

    JWE,KW,A128KW

    93.752μs, 117.309μs, 162.917μs (with A128GCM, A192GCM, A256GCM respectively)

    encryption/decryption

    JWE,KW,A192KW

    137.808μs, 176.636μs, 214.446μs (with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 respectively)

    encryption/decryption

    JWE,KW,A192KW

    104.048μs, 122.472μs, 138.150μs (with A128GCM, A192GCM, A256GCM respectively)

    encryption/decryption

    JWE,KW,A256KW

    139.867μs, 176.727μs, 208.664μs (with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 respectively)

    encryption/decryption

    JWE,KW,A256KW

    93.840μs, 115.313μs, 140.135μs (with A128GCM, A192GCM, A256GCM respectively)

    encryption

    JWE,RSAEnc,RSA1_5

    from 178.368μs to 373.941μs (depending on the Content Encryption Algorithm and the key size)

    decryption

    JWE,RSAEnc,RSA1_5

    from 354.921μs to 10,148.146μs (depending on the Content Encryption Algorithm and the key size)

    encryption

    JWE,RSAEnc,RSA-OAEP

    from 188.228μs to 428.624μs (depending on the Content Encryption Algorithm and the key size)

    decryption

    JWE,RSAEnc,RSA-OAEP

    from 381.853μs to 13,079.733μs (depending on the Content Encryption Algorithm and the key size)

    encryption

    JWE,RSAEnc,RSA-OAEP-256

    from 195.231μs to 410.868μs (depending on the Content Encryption Algorithm and the key size)

    decryption

    JWE,RSAEnc,RSA-OAEP-256

    from 354.090μs to 11,238.001μs (depending on the Content Encryption Algorithm and the key size)

    encryption/decryption

    JWE,PBES2,PBES2HS256A128KW

    from 2,109.175μs (256 bit salt / 1024 counts) to 7,943.047μs (512 bit salt / 4096 counts)

    encryption/decryption

    JWE,PBES2,PBES2HS384A192KW

    from 2,719.313μs (256 bit salt / 1024 counts) to 10,466.043μs (512 bit salt / 4096 counts)

    encryption/decryption

    JWE,PBES2,PBES2HS256A128KW

    from 2,746.634μs (256 bit salt / 1024 counts) to 10,600.124μs (512 bit salt / 4096 counts)

    encryption

    JWE,ECDHES,ECDHESKW,ECDHESA128KW

    ~45,198.922μs (with curve P-256)

    encryption

    JWE,ECDHES,ECDHESKW,ECDHESA128KW

    ~77,320.816μs (with curve P-384)

    encryption

    JWE,ECDHES,ECDHESKW,ECDHESA128KW

    ~120,709.648μs (with curve P-521)

    encryption

    JWE,ECDHES,ECDHESKW,ECDHESA128KW

    ~453.445μs (with curve X25519)

    decryption

    JWE,ECDHES,ECDHESKW,ECDHESA128KW

    ~21,249.059μs (with curve P-256)

    decryption

    JWE,ECDHES,ECDHESKW,ECDHESA128KW

    ~37,207.750μs (with curve P-384)

    decryption

    JWE,ECDHES,ECDHESKW,ECDHESA128KW

    ~57,072.871μs (with curve P-521)

    decryption

    JWE,ECDHES,ECDHESKW,ECDHESA128KW

    ~387.441μs (with curve X25519)

    encryption

    JWE,ECDHES,ECDHESKW,ECDHESA192KW

    ~44,697.707μs (with curve P-256)

    encryption

    JWE,ECDHES,ECDHESKW,ECDHESA192KW

    ~76,731.773μs (with curve P-384)

    encryption

    JWE,ECDHES,ECDHESKW,ECDHESA192KW

    ~124,164.813μs (with curve P-521)

    encryption

    JWE,ECDHES,ECDHESKW,ECDHESA192KW

    ~501.742μs (with curve X25519)

    decryption

    JWE,ECDHES,ECDHESKW,ECDHESA192KW

    ~21,603.676μs (with curve P-256)

    decryption

    JWE,ECDHES,ECDHESKW,ECDHESA192KW

    ~36,172.617μs (with curve P-384)

    decryption

    JWE,ECDHES,ECDHESKW,ECDHESA192KW

    ~55,530.465μs (with curve P-521)

    decryption

    JWE,ECDHES,ECDHESKW,ECDHESA192KW

    ~378.129μs (with curve X25519)

    encryption

    JWE,ECDHES,ECDHESKW,ECDHESA256KW

    ~44,701.426μs (with curve P-256)

    encryption

    JWE,ECDHES,ECDHESKW,ECDHESA256KW

    ~76,805.012μs (with curve P-384)

    encryption

    JWE,ECDHES,ECDHESKW,ECDHESA256KW

    ~121,017.648μs (with curve P-521)

    encryption

    JWE,ECDHES,ECDHESKW,ECDHESA256KW

    ~451.094μs (with curve X25519)

    decryption

    JWE,ECDHES,ECDHESKW,ECDHESA256KW

    ~21,335.781μs (with curve P-256)

    decryption

    JWE,ECDHES,ECDHESKW,ECDHESA256KW

    ~36,207.594μs (with curve P-384)

    decryption

    JWE,ECDHES,ECDHESKW,ECDHESA256KW

    ~55,440.664μs (with curve P-521)

    decryption

    JWE,ECDHES,ECDHESKW,ECDHESA256KW

    ~377.367μs (with curve X25519)

    encryption

    JWE,ECDHES

    ~44,762.633μs (with curve P-256)

    encryption

    JWE,ECDHES

    ~76,660.664μs (with curve P-384)

    encryption

    JWE,ECDHES

    ~119,539.141μs (with curve P-521)

    encryption

    JWE,ECDHES

    ~369.992μs (with curve X25519)

    decryption

    JWE,ECDHES

    ~21,894.422μs (with curve P-256)

    decryption

    JWE,ECDHES

    ~37,380.137μs (with curve P-384)

    decryption

    JWE,ECDHES

    ~58,231.930μs (with curve P-521)

    decryption

    JWE,ECDHES

    ~263.254μs (with curve X25519)

    subject

    groups

    mean

    sign

    JWS,EdDSA,Ed25519

    139.323μs

    verify

    JWS,EdDSA,Ed25519

    169.125μs

    sign

    JWS,ECDSA,ES256

    139.144μs

    verify

    https://en.wikipedia.org/wiki/PBKDF2arrow-up-right

    JWS,ECDSA,ES256