Validate Token

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with HMAC algorithm) or a public/private key pair using RSA.

In OpenID Connect the id_token is represented as a JWT. the JWT is used as a way to propagate and verify end-user identity.

A JWT can be used to:

  • Authentication: This is the typical scenario for using JWT, once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. Single Sign On is a feature that widely uses JWT nowadays, because of its small overhead and its ability to be easily used among systems of different domains.

  • Information Exchange: JWTs are a good way of securely transmitting information between parties, because as they can be signed, for example using a public/private key pair, you can be sure that the sender is who they say they are. Additionally, as the signature is calculated using the header and the payload, you can also verify that the content hasn’t changed.

In Cidaas, we issue JWTs as a result of the authentication process. When the user logs in using cidaas, a JWT is created, signed, and sent to the user. cidaas supports signing JWT with both HMAC and RSA algorithms. This token will be then used to authenticate and authorize with APIs which will grant access to their protected routes and resources.

eyJhbGciOiJSUzI1NiIsImtpZCI6ImQ2M2ExOGU0LWI1NzMtNGJlNy1iYzM2LTNiM2RiMTUzNWExMiJ9.eyJzaWQiOiI1OWM0YzkyMi1kMzg2LTRmYzAtODY1OS00YjljOWRiNTA0M2MiLCJzdWIiOiJhOGUzODZhMS0wMTMyLTRmOTktYjhmNS0zYWNjMTM1YWFhMmYiLCJpc3ViIjoiZjljZjViMmEtZWVhNS00YzcyLThiNTAtYWQxMTI4ZDU2MWI5IiwiYXVkIjoiYzE3YzZmMmEtNDcyOS00Y2YzLTg5NjMtNzZmYjNmYzBjYzQ5IiwiaWF0IjoxNTU5ODcxODUzLCJhdXRoX3RpbWUiOjE1NTk4NzE4NTMsImlzcyI6Imh0dHBzOi8vbmlnaHRseWJ1aWxkLmNpZGFhcy5kZSIsImp0aSI6ImRiOTc3MzFhLWY2NmUtNDI0My05NDM2LWIwY2QxZDMzYzllNiIsInNjb3BlcyI6W10sInJvbGVzIjpbIlVTRVIiXSwiZ3JvdXBzIjpbeyJyb2xlcyI6WyJTRUNPTkRBUllfQURNSU4iXSwiX2lkIjoiMDEwMjdiMzctNjg4Zi00ODdjLWIxNjktM2I3ZmFlMzhiM2IzIiwiZ3JvdXBJZCI6IkNJREFBU19BRE1JTlMiLCJpZCI6IjAxMDI3YjM3LTY4OGYtNDg3Yy1iMTY5LTNiN2ZhZTM4YjNiMyJ9XSwiZXhwIjoxNTU5OTU4MjUzfQ.TDKKu7EFdH9haJFJ4JoemgwLrVfPhctFLQtxKtp_gmm59Mbv8dMKPg-YjOWuBPXZTSGyevguLfAnB5ss0bxpbsO91izTJjGojFHOnLerjqoL1e4rZubZn3l-Jl7UA8vf3zndBabMX8rQpQhPr-HHh2SxzK-rsEYM7btWmRYD53

This looks gibberish till you break it by periods (.) and base64url-decode each part. There are two periods in it, which break the whole string into three parts. Once you base64url-decode the fist part, it appears like below:

{
“alg”:”RS256",
”kid”:32d4was3gh56dsg3q42fdf"
}

JOSE Header

This first part(once parted by the periods) of the JWT is known as the JOSE header . JOSE stands for Javascript Object Signing and Encryption — and it’s the name of the IETF working group, which works on standardizing the representation of integrity-protected data using JSON data structures. The above JOSE header indicates that it’s a signed message. Google asserts the identity of the end-user by signing the JWT, which carries data related to the user’s identity.

A signed JWT is known as a JWS (JSON Web Signature). In fact a JWT does not exist itself — either it has to be a JWS or a JWE (JSON Web Encryption). Its like an abstract class — the JWS and JWE are the concrete implementations.

The alg and kid elements there, are not defined in the JWT specification, but in the JSON Web Signature (JWS) specification. The JWT specification only defines two elements (typ and cty) in the JOSE header and both the JWS and JWE specifications extend it to add more appropriate elements.

typ (type): The typ element is used to define the media type of the complete JWT. A media type is an identifier, which defines the format of the content, transmitted on the Internet. There are two types of components that process a JWT: JWT implementations and JWT applications. Nimbus is a JWT implementation in Java. The Nimbus library knows how to build and parse a JWT. A JWT application can be anything, which uses JWTs internally. A JWT application uses a JWT implementation to build or parse a JWT. In this case, the typ element is just another element for the JWT implementation. It will not try to interpret the value of it, but the JWT application would. The typ element helps JWT applications to differentiate the content of the JWT when the values that are not JWTs could also be present in an application data structure along with a JWT object. This is an optional element and if present for a JWT, it is recommended to use JWT as the media type.

cty (content type): The cty element is used to define the structural information about the JWT. It is only recommended to use this element in the case of a nested JWT.

JWTs consist of three parts separated by dots (.), which are:

  • Header
  • Payload
  • Signature

Therefore, a JWT typically looks like the following.

xxxxx.yyyyy.zzzzz

Let’s break down the different parts.

The header typically consists of two parts: the type of the token, which is JWT, and the hashing algorithm such as HMAC SHA256 or RSA.

For example:

{
"alg": "HS256",
"typ": "JWT"
}

Then, this JSON is Base64Url encoded to form the first part of the JWT.

Payload

The second part of the token is the payload, which contains the claims. Claims are statements about an entity (typically, the user) and additional metadata. There are three types of claims: reserved, public, and private claims.

  • Reserved claims: These are a set of predefined claims, which are not mandatory but recommended, thought to provide a set of useful, interoperable claims. Some of them are: iss (issuer), exp (expiration time), sub (subject), aud (audience), among others.

  • Public claims: These can be defined at will by those using JWTs. But to avoid collisions they should be defined in the IANA JSON Web Token Registry or be defined as a URI that contains a collision resistant namespace.

  • Private claims: These are the custom claims created to share information between parties that agree on using them.

An example of payload could be:

{
"sub": "3d4d64h64q",
"name": "David Jhonson",
"admin": true
}

The payload is then Base64Url encoded to form the second part of the JWT.

Signature

To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.

For example, if you want to use the HMAC SHA256 algorithm, the signature will be created in the following way.

HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

The signature is used to verify that the sender of the JWT is who it says it is and to ensure that the message was’t changed in the way.

The output is three Base64 strings separated by dots that can be easily passed in HTML and HTTP environments, while being more compact compared to XML-based standards such as SAML.

The following shows a JWT that has the previous header and payload encoded and it is signed with a secret.

eyJhbGciOiJSUzI1NiIsImtpZCI6ImQ2M2ExOGU0LWI1NzMtNGJlNy1iYzM2LTNiM2RiMTUzNWExMiJ9.eyJzaWQiOiI1OWM0YzkyMi1kMzg2LTRmYzAtODY1OS00YjljOWRiNTA0M2MiLCJzdWIiOiJhOGUzODZhMS0wMTMyLTRmOTktYjhmNS0zYWNjMTM1YWFhMmYiLCJpc3ViIjoiZjljZjViMmEtZWVhNS00YzcyLThiNTAtYWQxMTI4ZDU2MWI5IiwiYXVkIjoiYzE3YzZmMmEtNDcyOS00Y2YzLTg5NjMtNzZmYjNmYzBjYzQ5IiwiaWF0IjoxNTU5ODcxODUzLCJhdXRoX3RpbWUiOjE1NTk4NzE4NTMsImlzcyI6Imh0dHBzOi8vbmlnaHRseWJ1aWxkLmNpZGFhcy5kZSIsImp0aSI6ImRiOTc3MzFhLWY2NmUtNDI0My05NDM2LWIwY2QxZDMzYzllNiIsInNjb3BlcyI6W10sInJvbGVzIjpbIlVTRVIiXSwiZ3JvdXBzIjpbeyJyb2xlcyI6WyJTRUNPTkRBUllfQURNSU4iXSwiX2lkIjoiMDEwMjdiMzctNjg4Zi00ODdjLWIxNjktM2I3ZmFlMzhiM2IzIiwiZ3JvdXBJZCI6IkNJREFBU19BRE1JTlMiLCJpZCI6IjAxMDI3YjM3LTY4OGYtNDg3Yy1iMTY5LTNiN2ZhZTM4YjNiMyJ9XSwiZXhwIjoxNTU5OTU4MjUzfQ.TDKKu7EFdH9haJFJ4JoemgwLrVfPhctFLQtxKtp_gmm59Mbv8dMKPg-YjOWuBPXZTSGyevguLfAnB5ss0bxpbsO91izTJjGojFHOnLerjqoL1e4rZubZn3l-Jl7UA8vf3zndBabMX8rQpQhPr-HHh2SxzK-rsEYM7btWmRYD53k

A signed JWT is known as a JWS (JSON Web Signature). In fact a JWT does not exist itself — either it has to be a JWS or a JWE (JSON Web Encryption). Its like an abstract class — the JWS and JWE are the concrete implementations.

the alg and kid elements there, are not defined in the JWT specification, but in the JSON Web Signature (JWS) specification. The JWT specification only defines two elements (typ and cty) in the JOSE header and both the JWS and JWE specifications extend it to add more appropriate elements.

typ (type): The typ element is used to define the media type of the complete JWT. A media type is an identifier, which defines the format of the content, transmitted on the Internet. There are two types of components that process a JWT: JWT implementations and JWT applications. Nimbus is a JWT implementation in Java. The Nimbus library knows how to build and parse a JWT. A JWT application can be anything, which uses JWTs internally. A JWT application uses a JWT implementation to build or parse a JWT. In this case, the typ element is just another element for the JWT implementation. It will not try to interpret the value of it, but the JWT application would. The typ element helps JWT applications to differentiate the content of the JWT when the values that are not JWTs could also be present in an application data structure along with a JWT object. This is an optional element and if present for a JWT, it is recommended to use JWT as the media type.

cty (content type): The cty element is used to define the structural information about the JWT. It is only recommended to use this element in the case of a nested JWT.The JWS specification is not bound to any specific algorithm. All applicable algorithms for signing are defined under the JSON Web Algorithms (JWA) specification, which is the RFC 7518. The section 3.1 of RFC 7518 defines all possible alg element values for a JWS token. The value of the kid element provides an indication or a hint about the key, which is used to sign the message. Looking at the kid, the recipient of the message should know where and how-to lookup for the key and find it.

In a JWT, the members of the JSON object represented by the JOSE header describe the cryptographic operations applied to the JWT and optionally, additional properties of the JWT. Depending upon whether the JWT is a JWS or JWE, the corresponding rules for the JOSE header values apply. Both under the JWS and JWE, the JOSE header is a must — or in other words there exists no JWT without a JOSE header.

Unsecured JWT

A JWT can be either a JWS or a JWE object. Unsecured JWT is a JWS object where in the JOSE header the value of the alg element is set to none. In other words, an unsecured JWT is a JWS without a signature When propagating user identity and entitlement information over an unsecured JWT, it is expected that the underlying transport will provide a guarantee on the integrity and the confidentiality of the token

Claim Set: The second part of the JWT (when parted by the period (.)) is known as the JWT claim set. White-spaces can be explicitly retained while building the JWT claim set — no canonicalization is required before base64url-encoding or decoding. Canonicalization is the process of converting different forms of a message into a single standard form. This is used mostly before signing XML messages.

img

The JWT claim set represents a JSON object whose members are the claims asserted by the JWT issuer. Each claim name within a JWT must be unique. If there are duplicate claim names, then the JWT parser could either return a parsing error or just return back the claims set with the very last duplicate claim. JWT specification does not explicitly define what claims are mandatory and what are optional. It’s up to the each application of JWT to define mandatory and optional claims. For example, the OpenID Connect specification defines the mandatory and optional claims. According to the OpenID Connect core specification, iss, sub, aud, exp and iat are treated as mandatory elements, while auth_time, nonce, acr, amr and azp are optional elements. In addition to the mandatory and optional claims, which are defined in the specification, the identity provider can include additional elements into the JWT claim set.

Signature

The third part of the JWT (when parted by the period (.)), is the signature, which is also base64url-encoded. The cryptographic elements related to the signature are defined in the JOSE header.

Serialization

A signed or an encrypted message can be serialized in two ways by following the JWS or JWE specification: the JWS/JWE compact serialization and the JWS/JWE JSON serialization. In fact, the OpenID Connect specification mandates to use JWS compact serialization and JWE compact serialization whenever necessary.

Now we can further refine our definition of the JWT. So far we know that both the JWS and JWE tokens are instances of the JWT. But that is not 100% precise. We call a JWS or JWE, a JWT only if it follows the compact serialization. Any JWT must follow compact serialization. In other words a JWS or JWE token, which follows JSON serialization cannot be called as a JWT.

JWS Compact Serialization

JWS compact serialization represents a signed JWT as a compact URL-safe string. This compact string has three main elements separated by periods (.): the JOSE header, the JWS payload and the JWS signature. If you use compact serialization against a JSON payload (or any payload — even XML), then you can have only a single signature, which is computed over the complete JOSE header and JWS payload.

The structure of a JWS token formed by JWS compact serialization.

JWS Compact Serialization — Signing Process

Following lists out the signing process of a JWS under the compact serialization.

  • Build a JSON object including all the header elements, which express the cryptographic properties of the JWS token — this is known as the JOSE header. As discussed before, the token issuer should advertise in the JOSE header, the public key corresponding to the key used to sign the message. This can be expressed via any of these header elements: jku, jwk, kid, x5u, x5c, x5t.

  • Compute the base64url-encoded value against the UTF-8 encoded JOSE header from the 1st step, to produce the 1st element of the JWS token.

  • Construct the payload or the content to be signed — this is known as the JWS payload. The payload is not necessarily JSON — it can be any content. Yes, you read it correctly, the payload of a JWS necessarily need not to be JSON - if you’d like it can be XML too.

  • Compute the base64url-encoded value of the JWS payload from the previous step to produce the 2nd element of the JWS token.

  • Build the message to compute the digital signature or the Mac. The message is constructed as ASCII(BASE64URL-ENCODE(UTF8(JOSE Header)) ‘.’ BASE64URL-ENCODE(JWS Payload)).

  • Compute the signature over the message constructed in the previous step, following the signature algorithm defined by the JOSE header element alg. The message is signed using the private key corresponding to the public key advertised in the JOSE header.

  • Compute the base64url encoded value of the JWS signature produced in the previous step, which is the 3rd element of the serialized JWS token.

  • Now we have all the elements to build the JWS token in the following manner. The line breaks are introduced only for clarity.

BASE64URL(UTF8(JWS Protected Header)) ‘.’
BASE64URL(JWS Payload) ‘.’
BASE64URL(JWS Signature)

JWS JSON Serialization

In contrast to the JWS compact serialization, the JWS JSON serialization can produce multiple signatures over the same JWS payload along with multiple JOSE headers. The ultimate serialized form under JWS JSON serialization wraps the signed payload in a JSON object, with all the related metadata. This JSON object includes 2 top-level elements: payload and signatures (which is a JSON array), and three sub elements under each entry of the signatures array: protected, header and signature.

Following is an example of a JWS token, which is serialized under JWS JSON serialization. This is neither URL safe nor optimized for compactness. It carries two signatures over the same payload, and each signature and the metadata around it are stored as an element in the JSON array, represented by the signatures top-level element. Each signature uses a different key to sign, represented by the corresponding kid header element.

The payload top-level element of the JSON object includes the base64url-encoded value of the complete JWS payload. The JWS payload not necessarily needs to be a JSON payload, it can be of any content type. The payload is a required element in the serialized JWS token.

The JWS Protected Header

The JWS protected header is a JSON object that includes the header elements that has to be integrity protected by the signing or MAC algorithm. There can be multiple JWS protected headers in a JWS, serialized under JSON serialization, where each one of them carries the header elements that has to be signed differently. The JSON serialization is useful in selectively signing JOSE header elements, while in contrast JWS compact serialization signs the complete JOSE header.

Each protected element in the serialized JSON form represents the base64url-encoded value of a JWS protected header. The protected element is defined under each entry of the signatures JSON array and includes the base64url-encoded JSON object of header elements, which should be signed. If you base64url-decode the value of the first protected element in the above code snippet, you will see {“alg”:”RS256"}. The protected element must be present, if there are any JWS protected headers. There can be one protected element for each entry of the signatures JSON array.

JWS Unprotect Header

The JWS unprotected header is a JSON object, which includes the header elements that are not integrity protected by the signing or MAC algorithm. Each header element in the serialized JSON form represents the base64url-encoded value of a JWS unprotected header. The header element is defined under each entry in the signatures JSON array and includes unprotected header elements corresponding to this signature, which are not signed. Combining both the protected headers and unprotected headers ultimately derives the JOSE header corresponding to this signature (the metadata related to the signature can be either protected or unprotected). In the above code snippet, the complete JOSE header corresponding to the first entry in the signatures JSON array would be {“alg”:”RS256", “kid”:”2019–12–19"}, which aggregates both the protected and unprotected headers. The header element itself is represented as a JSON object and must be present if there are any unprotected header elements. There can be one header element for each entry of the signatures JSON array.

The signatures element of the JSON object includes an array of JSON objects, where each element includes a signature or MAC (over the JWS payload and JWS protected header) and the associated metadata. This is a required element. The signature element, which is inside each entry of the signatures array carries the base64url-encoded value of the signature computed over the protected header elements (represented by the protected element) and the JWS payload. Both the signatures and signature are required elements.

JWS JSON Serialization — Signing Process

Following lists out the signing process of a JWS under the JSON serialization.

  • Construct the payload or the content to be signed — this is known as the JWS payload. The payload is not necessarily JSON — it can be any content. The payload element in the serialized JWS token carries the base64url-encoded value of this.

  • Decide how many signatures you would need against the payload and for each case which header elements must be signed and which are not.

  • Build a JSON object including all the header elements that are to be integrity protected or to be signed. In other words construct the JWS protected header for each signature. The base64url-encoded value of the UTF-8 encoded JWS protected header will produce the value of the corresponding protected element inside each entry of the signatures JSON array.

  • Build a JSON object including all the header elements that need not to be integrity protected or not to be signed. In other words construct the JWS unprotected header for each signature. This will produce the corresponding header element inside each entry of the signatures JSON array.

  • Both the JWS protected header and the JWS unprotected header express the cryptographic properties of the corresponding signature — this is known as the JOSE header. As discussed before the token issuer should advertise in the JOSE header, the public key corresponding the key used to sign the message. This can be expressed via any of these header elements: jku, jwk, kid, x5u, x5c, x5t.

  • Build the message to compute the digital signature or the Mac against each entry in the signatures JSON array of the serialized JWS token. The message is constructed as ASCII(BASE64URL-ENCODE(UTF8(JWS Protected Header of the corresponding entry)) ‘.’ BASE64URL-ENCODE(JWS Payload)).

  • Compute the signature over the message constructed in the previous step, following the signature algorithm defined in the corresponding header element: alg. This element can be either inside the JWS protected header or the JWS unprotected header. The message is signed using the private key corresponding to the public key advertised in the header.

  • Compute the base64url encoded value of the JWS signature produced in the previous step, which will produce the value of the signature element inside the signatures JSON array of the serialized JWS token.

  • Once all the signatures are computed, the signatures JSON array can be constructed and will complete the JWS JSON serialization.

JWE (JSON Web Encryption)

The JWE (JSON Web Encryption) specification standardizes the way to represent an encrypted content in a JSON-based data structure. It defines two serialized forms to represent the encrypted payload: the JWE compact serialization and JWE JSON serialization. Both of these two serialization techniques are discussed in detail, in the sections to follow. Like in JWS, the message to be encrypted using JWE standard needs not to be a JSON payload, it can be any content.

JWE Compact Serialization

With the JWE compact serialization, a JWE token is built with five key components, each separated by a period (.): JOSE header, JWE Encrypted Key, JWE initialization vector, JWE Additional Authentication Data (AAD), JWE Ciphertext and JWE Authentication Tag.

The structure of a JWE token formed by JWE compact serialization.

The JOSE header is the very first element of the JWE token produced under compact serialization. The structure of the JOSE header is the same, as we discussed under JWS other than couple of exceptions. The JWE specification introduces two new elements (enc and zip), which are included in the JOSE header of the JWE token, in addition to what’s defined by the JSON Web Signature (JWS) specification.

To understand JWE Encrypted Key section of the JWE, we first need to understand how a JSON payload gets encrypted. The enc element of the JOSE header defines the content encryption algorithm and it should be a symmetric Authenticated Encryption with Associated Data (AEAD)algorithm. The alg element of the JOSE header defines the encryption algorithm to encrypt the Content Encryption Key (CEK). This algorithm can also be defined as the key wrapping algorithm, as it wraps the CEK.

Authenticated Encryption with Associated Data (AEAD) is a block cipher mode of operation which simultaneously provides confidentiality, integrity, and authenticity assurances on the data; decryption is combined in single step with integrity verification.

Let’s look at the following JOSE header. For content encryption, it uses A256GCM algorithm; and for key wrapping, RSA-OAEP:

{
“alg”:”RSA-OAEP”,”enc”:”A256GCM”
}

A256GCM is defined in the JWA specification. It uses the Advanced Encryption Standard (AES) in Galois/Counter Mode (GCM) algorithm with a 256-bit long key, and it’s a symmetric key algorithm used for AEAD. Symmetric keys are mostly used for content encryption and it is much faster than asymmetric-key encryption. At the same time, asymmetric-key encryption can’t be used to encrypt large messages.

RSA-OAEP is too defined in the JWA specification. During the encryption process, the token issuer generates a random key, which is 256 bits in size and encrypts the message using that key following the AES GCM algorithm. Next, the key used to encrypt the message is encrypted using RSA-OAEP, which is an asymmetric encryption scheme. The RSA-OAEP encryption scheme uses RSA algorithm with the Optimal Asymmetric Encryption Padding (OAEP)method. Finally the encrypted symmetric key is placed in the JWE Encrypted Header section of the JWE.

Some encryption algorithms, which are used for content encryption require an initialization vector, during the encryption process. Initialization vector is a randomly generated number, which is used along with a secret key to encrypt data. This will add randomness to the encrypted data, which will prevent repetition even the same data gets encrypted using the same secret key again and again. To decrypt the message at the token recipient end, it has to know the initialization vector, hence it is included in the JWE token, under the JWE Initialization Vector element. If the content encryption algorithm does not require an initialization vector, then the value of this element should be kept empty.

The fourth element of the JWE token is the base64url-encoded value of the JWE ciphertext. The JWE ciphertext is computed by encrypting the plaintext JSON payload using the Content Encryption Key (CEK), the JWE initialization vector and the Additional Authentication Data (AAD) value, with the encryption algorithm defined by the header element enc. The algorithm defined by the enc header element should be a symmetric Authenticated Encryption with Associated Data (AEAD) algorithm. The AEAD algorithm, which is used to encrypt the plaintext payload, also allows specifying Additional Authenticated Data (AAD).

The base64url-encoded value of the JWE Authenticated Tag is the final element of the JWE token. As discussed before the value of the authentication tag is produced during the AEAD encryption process, along with the ciphertext. The authentication tag ensures the integrity of the ciphertext and the Additional Authenticated Data (AAD).

JWE Compact Serialization — Signing Process

Following lists out the encryption process of a JWE under the compact serialization.

  • Figure out the key management mode by the algorithm used to determine the Content Encryption Key (CEK) value. This algorithm is defined by the alg element in the JOSE header. There is only one alg element per JWE token.

  • Compute the CEK and calculate the JWE Encrypted Key based on the key management mode, picked in the previous. The CEK is later used to encrypt the JSON payload. There is only one JWE Encrypted Key element in the JWE token.

  • Compute the base64url-encoded value of the JWE Encrypted Key, which is produced in the previous step. This is the 2nd element of the JWE token.

  • Generate a random value for the JWE Initialization Vector. Irrespective of the serialization technique, the JWE token will carry the value of the base64url-encoded value of the JWE Initialization Vector. This is the 3rd element of the JWT token.

  • If token compression is needed, the JSON payload in plaintext must be compressed following the compression algorithm defined under the zipheader element.

  • Construct the JSON representation of the JOSE header and find the base64url-encoded value of the JOSE header with UTF8 encoding. This is the 1st element of the JWE token.

  • To encrypt the JSON payload, we need the CEK (which we already have), the JWE Initialization Vector (which we already have), and the Additional Authenticated Data (AAD). Compute ASCII value of the encoded JOSE header from the previous step and use it as the AAD.

  • Encrypt the compressed JSON payload (from the previous step) using the CEK, the JWE Initialization Vector and the Additional Authenticated Data (AAD), following the content encryption algorithm defined by the header enc header element.

  • The algorithm defined by the enc header element is a AEAD algorithm and after the encryption process, it produce the ciphertext and the Authentication Tag.

  • Compute the base64url-encoded value of the ciphertext, which is produced by the step one before the previous. This is the 4th element of the JWE token.

  • Compute the base64url-encoded value of the Authentication Tag, which is produced by the step one before the previous. This is the 5th element of the JWE token.

  • Now we have all the elements to build the JWE token in the following manner. The line breaks are introduced only for clarity.

BASE64URL-ENCODE(UTF8(JWE Protected Header)) ‘.’
BASE64URL-ENCODE(JWE Encrypted Key) ‘.’
BASE64URL-ENCODE(JWE Initialization Vector) ‘.’
BASE64URL-ENCODE(JWE Ciphertext) ‘.’
BASE64URL-ENCODE(JWE Authentication Tag)

JWE JSON Serialization

Unlike the JWE compact serialization, the JWE JSON serialization can produce encrypted data targeting at multiple recipients over the same JSON payload. The ultimate serialized form under JWE JSON serialization represents an encrypted payload in a JSON object. This JSON object includes six top-level elements: protected, unprotected, recipients, iv, ciphertext and tag. Following is an example of a JWE token, which is serialized under JWE JSON serialization.

The JWE Protected Header

The JWE protected header is a JSON object that includes the header elements that has to be integrity protected by the authenticated encryption operation (AEAD). The elements inside the JWE protected header are applicable to all the recipients of the JWE token. The protected element in the serialized JSON form represents the base64url-encoded value of the JWE protected header. There can be only one protected element in a JWE token at the root level and any header elements that we discussed before under the JOSE header can also be used under the JWE protected header.

JWE Shared Unprotected Header

The JWE shared unprotected header is a JSON object that includes the header elements that are not integrity protected. The elements inside the JWE shared unprotected header are applicable to all the recipients of the JWE token. The unprotected element in the serialized JSON form represents the JWE shared unprotected header. There can be only one unprotected element in a JWE token, at the root level and any header element that we discussed before under the JOSE header can also be used under the JWE shared unprotected header.

Example:

JWT with RS256

Now that we’ve learnt the HMAC way, let’s look into the RSA (public/private key pair) way. Let’s create a public/private key pair first

Create

Now as far as our code is concerned we’ll have the same base64UrlEncode() and base64UrlDecode() helper functions along with another one that we’ll define now.

Also instead of a secret that is used as a crypto key to generate HMACs we’ll use the public/private keys that we just generated with openssl. So along with the helpers (and removing the $secret) this is what we’ll have

The values of $privateKeyFile and $publicKeyFile are basically the path (relative in this case, can also be absolute) to those files that we generated. Our new generateJWT() method will slightly change to use openssl_sign()with our private key.

Everything is similar to our HS256 method except instead of using hash_hmac(), we used openssl_sign() and it’s appropriate arguments to generate the signature.

Verify

As far as the verification goes that’ll also be very similar except we’ll now use openssl_verify() function to verify the signature with our public key

Due to the symmetric nature of HS256 we favor the use of RS256 for signing your JWTs, especially for APIs with 3rd party clients. However, this decision comes with some extra steps for verifying the signature of your JWTs. cidaas uses the JWK specification to represent the cryptographic keys used for signing tokens. This spec defines two high level data structures: JWKS and JWK.

JSON Web Key (JWK)

A JSON object that represents a cryptographic key. The members of the object represent properties of the key, including its value.

JWK Set

A JSON object that represents a set of JWKs. The JSON object MUST have a "keys" member, which is an array of JWKs.

At the most basic level, the JWKS is a set of keys containing the public keys that should be used to verify any JWT issued by the authorization server. cidaas exposes a JWKS endpoint for each tenant, The endpoint will contain the JWK used to sign all cidaas issued JWTs for this tenant. Here is an example of the JWKS used by a demo tenant.

{
"keys": [
{
"n": "l7tTKL_sCIOGRTMwHkq8WKfZjcYVslO_md5pNb7Vdh79prJP_E6-PkhQekfMIv-C3V01h-ilQVLLKXzo-acWsRWpfazjd1BnRxFbfsjAJuvueWg3ubEksEMTG1kZ16e9y3NkQKXc7Xr1pze0wAUlzlApwb1yqez0DdKrUz2ppsM",
"e": "AQAB",
"alg": "RS256",
"use": "sig",
"kid": "d63a18e4-b573-4be7-bc36-3b3db1535a12",
"kty": "RSA"
}
]
}
Note At the time of writing, cidaas only supports a single JWK for signing, however it is important to assume this endpoint could contain multiple JWKs. As an example, multiple keys can be found in the JWKS when rotating signing certificates.

The JWKS above contains a single key. Each property in the key is defined by the JWK specification RFC 7517 Section 4. We will use these properties to determine which key was used to sign the JWT. Here is a quick breakdown of what each property represents:

  • alg: is the algorithm for the key
  • kty: is the key type
  • use: is how the key was meant to be used. For the example above sig represents signature.
  • x5c: is the x509 certificate chain
  • e: is the exponent for a standard pem
  • n: is the moduluos for a standard pem
  • kid: is the unique identifier for the key

Verifying a JWT using the JWKS endpoint

Now that we understand JWKS and the specific properties of each JWK let's put this together and verify a JWT signed by cidaas with RS256. In our example we are going to build a first party API for our ficticious company c0der.io. The API will have a single endpoint returning metadata about our API, which requires a valid JWT. Ideally your resource server would also validate necessary scopes, however, that is beyond this topic. We will assume an API and client have been created and we will focus on what happens once our API recieves a request.

HS256 vs RS256

You might be wondering when to use which ? It’s simple I think. If your clients are first party trusted clients where you have control over them entirely (end to end), then you can use HS256 where the “secret” can be shared.

If that’s not the case (third party clients) then you can’t share the secret (especially what when you want to revoke or roll it?). In such cases you can use RS256 and provide your clients with public keys. You can additionally give them endpoints to fetch public keys periodically in case the private keys have been revoked/regenerated and there are new public keys extracted out of them.

Payload Verification

We just looked at signature verification but a JWT has the provision to contain some registered claims which are not mandatory but can help do a bunch of verification in terms of token expiration and issuance time checks. A claim is basically a key/value pair inside the payload. So for instance you can use iat (issued at), nbf (not before) and exp(expiration time) keys inside your payload with timestamp values which can be checked everytime against current time to see if the token should be used or not or whether it has been expired. Here’s a header and payload sample where the payload contains registered and private claims.

results matching ""

    No results matching ""