The concepts of JWT, JWS, JWE and JWK are part of the JSON Object Signing and Encryption (JOSE) framework that intends to provide a method to securely transfer claims between parties.
All these technologies can be used collectively to encrypt and/or sign content using a variety of algorithms. While the full set of permutations is extremely large, and might be daunting to some, it is expected that most applications will only use a small set of algorithms to meet their needs.
The JSON Object Signing and Encryption (JOSE) framework consists of several technologies:
- JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure, as the plaintext of a JSON Web Encryption (JWE) structure or enclosed in another JWE or JWS structure to create a Nested JWT, enabling nested signing and encryption to be performed. In fact a JWT does not exist itself — either it has to be a JWS and/or a JWE. It’s like an abstract class — the JWS and JWE are the concrete implementations.
- JSON Web Signature (JWS) represents signed content using JSON data structures and base64url encoding as defined in the specifications.
- JSON Web Encryption (JWE) specification standardizes the way to represent an encrypted content in a JSON-based data structure.
- JSON Web Key (JWK) defines a consistent way to represent a cryptographic key in a JSON structure which is used to sign (JWS) and/or encrypt (JWE) a specific content. The JSON Web Key Set (JWKS) extension defines a consistent way to represent a set of cryptographic keys in a JSON structure.
As itsme® only support the RSA cryptosystem, it requires that each party exposes its public keys as a simple JWK Set document on a URI accessible to all.
3. Creating your JWK Set URI
4. Signing, encrypting and decrypting
A signed content can be serialized in two ways: the JWS compact serialization and the JWS JSON serialization. Because the OpenID Connect specification mandates to use JWS compact serialization whenever necessary, we will not explain the JWS JSON serialization signing process in this document.
Following steps will show you how to generate a JWS Compact Serialization object:
- Build a JSON object including the below header elements, which express the cryptographic properties of the JWS object — this is known as the JWS Header.
|alg||Required||This parameter identifies the cryptographic algorithm used to secure the JWS. This MUST be set to "RS256".|
|kid||Optionnal||It is a hint indicating which key was used to secure the JWS. The structure of the "kid" value is unspecified. In case there are multiple signing keys referenced in your JWK Set document than it MUST be a case-sensitive string.|
- The JWS Header will then be encoded using UTF-8 and base64url to produce the string below.
- Construct the payload or the content to be signed as UTF-8 — this is known as the JWS Payload.
- The JWS Payload will then be encoded using base64url to produce the string below.
- Combine the JWS Header and JWS Payload, and separate them with period ('.') characters, to produce the JWS Signing Input.
- Complete the signing operation over the JWS Signing Input with the algorithm defined in the "alg" parameter, to produce the JWS Signature. The JWS Signing Input is signed using your private key corresponding to the public key referenced in your JWK Set document. This information SHOULD be made available via the URI you communicated when setting up your project in the itsme® B2B portal.
- The JWS Signature will then be encoded using base64url to produce the string below.
- Finally, you are now be able to build the JWS object by concatenating the three strings, and separate them with period ('.') characters. An example of a JWS object is given below.
An example illustrating the signing process is available at https://tools.ietf.org/html/rfc7520#section-4.1.
An encrypted content can be serialized in two ways: the JWE compact serialization and the JWE JSON serialization. Because the OpenID Connect specification mandates to use JWE compact serialization whenever necessary, we will not explain the JWE JSON serialization signing process in this document.
With the JWE compact serialization, a JWE object is built with five key components, each separated by a period (.): JWE Header, JWE Encrypted Key, JWE Initialization Vector, JWE Additional Authentication Data (AAD), JWE Ciphertext and JWE Authentication Tag. The following steps will show you how to generate a JWE Compact Serialization object:
- Create the JWS object, as described in the section above.
- Build a JSON object including all the header elements. The structure of the JWE Header is the same, as we discussed under JWS other than couple of exceptions. The JWE specification introduces three new elements ("enc", "zip" and "cty"), which are included in the JWE Header of the JWE object, in addition to what’s defined by the JSON Web Signature (JWS) specification.
An example can be found below.
|alg||Required||This parameter has the same meaning, syntax, and processing rules as the "alg" defined in the JWS section, except that it defines the cryptographic algorithm used to encrypt the Content Encryption Key (CEK). This MUST be set to "RSA-OAEP".|
|enc||Required||It identifies the content encryption algorithm used to perform authenticated encryption on the payload to produce the Ciphertext and the Authentication Tag. The "enc" value MUST be set to "A128CBC-HS256".|
|cty||Required||As the JWT MUST be signed then encrypted, this Header Parameter MUST be present; in this case, the value MUST be "JWT", to indicate that a Nested JWT is carried in this JWT.|
|kid||Optionnal||This parameter has the same meaning, syntax, and processing rules as the "kid" parameter defined in the JWS section, except that the key hint references the public key to which the JWE was encrypted; this can be used to determine the private key needed to decrypt the JWE.|
- The JWE Header will then be encoded using using UTF-8 and base64url to produce the string below.
- Generate a random CEK. This key will be used later to encrypt the payload.
- Compute the CEK with the algorithm defined in the "alg" parameter, to produce the JWE Encrypted Key. To perform the encryption operation you will need the public key of itsme®, which can be retrieved from the itsme® Discovery document, using the "jwks_uri" key.
- The JWE Encrypted Key will then be encoded using base64url to produce the string below.
- Generate a random JWE Initialization Vector. Its value will be used later to encrypt the payload.
- The JWE Initialization Vector will then be encoded using base64url to produce the string below.
- Compute the ASCII value of the encoded JWE Header to get the Additional Authenticated Data (AAD). Its value will be used later to encrypt the payload.
- Encrypt the payload with the encryption algorithm defined by the "enc" parameter. The algorithm takes as input four strings: the CEK, the AAD and the JWE Initialization Vector which were computed in the previous steps, plus the payload. The JWE Ciphertext value and Authentication Tag value are provided as outputs.
- Base64url-encode the JWE Ciphertext.
- Base64url-encode the JWE Authentication Tag.
- Assemble the final representation by concatenating the five strings, and separate them with period ('.') characters. An example of a JWE object is given below.
An example illustrating the signing process is available at https://tools.ietf.org/html/rfc7520#section-5.2.
To validate a Nested JWT object, you will first need to decrypt the JWE object, then extract the signed JWS Payload and verify the JWS Signature.
Decrypting the JWE object
The decryption process is the reverse of the encryption process:
- Parse the JWE object to extract the serialized values of the base64url-encoded JWE Header, the base64url-encoded JWE Encrypted Key, the base64url-encoded JWE Initialization Vector, the base64url-encoded JWE Ciphertext, and the base64url-encoded JWE Authentication Tag.
- Base64url decode the encoded representations of the JWE Header, the JWE Encrypted Key, the JWE Initialization Vector, the JWE Ciphertext, the JWE Authentication Tag, and the JWE Additional Authenticated Data (AAD).
- Verify that the octet sequence resulting from decoding the encoded JWE Header is a UTF-8-encoded representation of a completely valid JSON object.
- Determine the algorithm specified by the "alg" parameter in the JWE Header.
- Decrypt the JWE Encrypted Key with the algorithm defined in the "alg" parameter, to produce the Content Encryption Key (CEK). The JWE Encrypted Key is decoded using your private key corresponding to the public key referenced in your JWK Set document. This information SHOULD be made available via the URI you communicated when setting up your project in the itsme® B2B portal.
- Let the Additional Authenticated Data (AAD) encryption parameter be the octets of the ASCII representation of the encoded JWE Header value.
- Decrypt the JWE Ciphertext with the encryption algorithm defined by the "enc" parameter in the JWE Header. It will return the decrypted JWS object.
Extracting the payload
- Parse the JWS object to extract the JWS components serialized values, such as in the example below.
- Base64url-decode the encoded representation of the JWS Header.
- Verify that the resulting octet sequence is a UTF-8-encoded representation of a completely valid JSON object.
- Base64url-decode the encoded representation of the JWS Payload.
- Base64url-decode the encoded representation of the JWS Signature.
You have decoded the JWS object. The next step is to validate the JWS Signature.
Checking the JWS Signature
- Determine the signing algorithm "alg" and the key identifier "kid" from the JWS Header.
- Validate the JWS Signature in the manner defined for the algorithm being used, which MUST be accurately represented by the value of the "alg" parameter. The signature is verified using the public key of itsme®, which can be retrieved from the itsme® Discovery document, using the "jwks_uri" key. If the returned itsme® JSON Web Key (JWK) contains an array of keys, you MUST use the one corresponding to the value given by the "kid" parameter from the JWS Header.