JWT, JWS and JWE!
Omar Ismail
Senior Software Engineer @ Digitinary | Java & Spring Expert ? | AWS & Microservices Architect ? | FinTech & Open Banking Innovator ?? | Digital Payments Expert ?? | Top 200 IT Content Creator in Jordan ?? | 40K+ ??
Thanks to the original writer and article :
https://medium.facilelogin.com/jwt-jws-and-jwe-for-not-so-dummies-b63310d201a3
JSON Web Token (JWT) defines a container to transport data between interested parties. It became an IETF standard in May 2015 with the RFC 7519. There are multiple applications of JWT. The OpenID Connect is one of them. In OpenID Connect the?id_token?is represented as a JWT. Both in securing APIs and Microservices, the JWT is used as a way to propagate and verify end-user identity.
A JWT can be used to:
Following is a sample JWT, which is returned back from the Google OpenID Connect provider. Here the Google, which is the identity provider, asserts the identity of an end-user and passes the JWT to a service provider (a web or native mobile application).
eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc4YjRjZjIzNjU2ZGMzOTUzNjRmMWI2YzAyOTA3NjkxZjJjZGZmZTEifQ.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTEwNTAyMjUxMTU4OTIwMTQ3NzMyIiwiYXpwIjoiODI1MjQ5ODM1NjU5LXRlOHFnbDcwMWtnb25ub21ucDRzcXY3ZXJodTEyMTFzLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiZW1haWwiOiJwcmFiYXRoQHdzbzIuY29tIiwiYXRfaGFzaCI6InpmODZ2TnVsc0xCOGdGYXFSd2R6WWciLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXVkIjoiODI1MjQ5ODM1NjU5LXRlOHFnbDcwMWtnb25ub21ucDRzcXY3ZXJodTEyMTFzLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiaGQiOiJ3c28yLmNvbSIsImlhdCI6MTQwMTkwODI3MSwiZXhwIjoxNDAxOTEyMTcxfQ.TVKv-pdyvk2gW8sGsCbsnkqsrS0T-H00xnY6ETkIfgIxfotvFn5IwKm3xyBMpy0FFe0Rb5Ht8AEJV6PdWyxz8rMgX2HROWqSo_RfEfUpBb4iOsq4W28KftW5H0IA44VmNZ6zU4YTqPSt4TPhyFC9fP2D_Hg7JQozpQRUfbWTJI
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”:”78b4cf23656dc395364f1b6c02907691f2cdffe1"}
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.
Login to a service provider via Google OpenID Connect. In step-5 and step-7 user information is returned back to the service provider in a JWT, signed by Google.
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.
Going back to the JOSE header returned back from Google, both 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 (sounds bit weird though :-)). 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; use TLS.
Claims Set
Focus back on the sample JWT returned back from Google. More precisely now we know its a JWS. Following shows the base64url-decoded claims set returned back from Google. The second part of the JWT (when parted by the period (.)) is known as the JWT claims set. White-spaces can be explicitly retained while building the JWT claims 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.
The JWT claims 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 claims set.
Signature
Once again, focus back on the sample JWT returned back from Google. 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. In this particular example, Google uses RSASSA-PKCS1-V1_5 with the SHA-256 hashing algorithm, which is expressed by the value of the?alg?element in the JOSE header:?RS256.
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. The Google OpenID Connect response discussed before uses the JWS compact 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.
JWS Compact Serialization — Signing Process
Following lists out the signing process of a JWS under the compact serialization.
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”:”2010–12–29"}, 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.
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 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.
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.
JWE Per-Recipient Unprotected Header
The JWE per-recipient unprotected header is a JSON object that includes the header elements that are not integrity protected. The elements inside the JWE per-recipient unprotected header?are applicable only to a particular recipient of the JWE token. In the JWE token, these header elements are grouped under the element,?recipients. The?recipients?element represents an array of recipients of the JWE token. Each member consists of a?header?element and an?encrypted_key?element.
Any header element that we discussed before under the JOSE header can also be used under the JWE per-recipient unprotected header.
JWE Initialization Vector
This carries the same meaning as explained under JWE compact serialization, previously. The?iv?element in the JWE token represents the value of the initialization vector used for encryption.
JWE Ciphertext
This carries the same meaning as explained under JWE compact serialization, previously. The?ciphertext?element in the JWE token carries the base64url-encoded value of the JWE ciphertext
JWE Authentication Tag
This carries the same meaning as explained under JWE compact serialization, previously. The?tag?element in the JWE token carries the base64url-encoded value of the JWE authenticated tag, which is an outcome of the encryption process using an?AEAD?algorithm.
JWE JSON Serialization — Signing Process
Following lists out the encryption process of a JWE under the JSON serialization.
JWT Bindings
A binding for a given token defines how to transport the token from one place to another. For example, SAML HTTP redirect binding defines how to transport a SAML request/response over HTTP redirect. The SAML SOAP binding defines how to transport a SAML request/response wrapped in a SOAP enveloper.
JWT does not have a standard binding. But in most of the cases, the JWT is transported over HTTP under the Authorization Bearer header (like in OAuth 2.0).
Authorization: Bearer <jwt-token>
Everything we discussed so far assumed JWT is a bearer token. A bearer token means, who ever owns the token can use it without proving the ownership of the token. Its like cash. If you steal 100 bucks from someone, you do not need to prove how you got it when you want to spend it. If someone steals a bearer token he can just use it as the legitimate owner of it. Whenever you use a bearer token — or transport it from one place to another, it has to be done over a secured medium; user TLS.
JWT with Proof of Possession
The RFC 7800 describes how a JWT can declare that the presenter of the JWT possesses a particular proof-of-possession (PoP) key and how the recipient can cryptographically confirm proof of possession of the key by the presenter. Proof of possession of a key is also sometimes described as the presenter being a holder-of-key. This is analogous to a credit card protected with the owner’s signature. Even if someone steals a credit card he cannot use it without proving the ownership. The ownership is proved by the signature. The JWTs issued under holder-of-key confirmation is out-0f-the-scope of this post and possibly we can discuss this in detail in a later post.
Summary
Software Engineer || SMNSE || Senior CSE at OAU || 3x AWS Community Builder || DSF Individual Member ?? || DjangoCon Europe 2025 Volunteer ??
2 年Thanks to you too?? Now I understand JWT??