Skip to content

Creating and Validating JWEs (Json Web Encryptions)

kellyyangsong edited this page Apr 22, 2024 · 8 revisions

JWEs allow you to protect sensitive data in JWT tokens so that only the intended recipient is able to read it. There are multiple algorithms available to encrypt a JWE. One common way is for the sender to encrypt with a symmetric key and then the symmetric key is encrypted using the public key of the recipient. The receiver decrypts the symmetric key with corresponding private key and then decrypts the JWE. IdentityModel does all the work for you.

In order to create and validate JWEs, you will first need to install the Microsoft.IdentityModel.JsonWebTokens library.

Creating a JWE

The process of creating a JWE is very similar to the process of creating a JWS, except that it includes the additional step of setting the EncryptingCredentials property. As mentioned earlier, these EncryptingCredentials correspond to the public key of the recipient, and will be used in order to encrypt the inner token.

var payload = new JObject()
{
    { JwtRegisteredClaimNames.Email, "Bob@contoso.com" },
    { JwtRegisteredClaimNames.GivenName, "Bob" },
    { JwtRegisteredClaimNames.Iss, "http://Default.Issuer.com" },
    { JwtRegisteredClaimNames.Aud, "http://Default.Audience.com" },
    { JwtRegisteredClaimNames.Iat, EpochTime.GetIntDate(DateTime.Parse("2017-03-17T18:33:37.095Z")).ToString() },
    { JwtRegisteredClaimNames.Nbf, EpochTime.GetIntDate(DateTime.Parse("2017-03-17T18:33:37.080Z")).ToString() },
    { JwtRegisteredClaimNames.Exp, EpochTime.GetIntDate(DateTime.Parse("2021-03-17T18:33:37.080Z")).ToString() },
}.ToString();

var jsonWebTokenHandler = new JsonWebTokenHandler();
var signingCredentials = Default.SymmetricSigningCredentials;
var encryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256);
var jwe = jsonWebTokenHandler.CreateToken(payload, signingCredentials, encryptingCredentials);

The Default class mentioned above is used by our tests and can be found here.

Validating a JWE

var jsonWebTokenHandler = new JsonWebTokenHandler();
var tokenValidationParameters = new TokenValidationParameters
{
    TokenDecryptionKey = KeyingMaterial.RsaSecurityKey_2048,
    IssuerSigningKey = Default.SymmetricSigningKey256,
    ValidAudience = "http://Default.Audience.com",
    ValidIssuer = "http://Default.Issuer.com",
};
var tokenValidationResult = jsonWebTokenHandler.ValidateToken(jwe, tokenValidationParameters);

The TokenValidationResult class is a part of the Microsoft.IdentityModel.Tokens library and can be found here.

The KeyingMaterial class mentioned above is used by our tests and can be found here.

The code example above uses TokenDecryptionKey which is for the case of a single decryption key. Use TokenDecryptionKeys if there are multiple decryption keys that can be used or TokenDecryptionKeyResolver if the decryption key needs to be resolved dynamically

Special creation/validation cases

Creating a JWE with an inner token that has a different type

To change the 'typ' header value in either a JWS or JWE, you need to use one of the JsonWebTokenHandler.CreateToken(...) methods that contains the IDictionary<string, object> additionalHeaderClaims parameter and provide a new value for the 'typ' claim. However, this value is only added to the outer token header in the case of a JWE, so it cannot be used to create a JWE with an inner JWS that has a different type. In order to do this, you must first create a JWS with a different type, and then encrypt that token. The code below shows how this should be done:

var jsonWebTokenHandler = new JsonWebTokenHandler();
var type = "DifferentType";
var jwsWithDifferentType = jsonWebTokenHandler.CreateToken(payload, new Dictionary<string, object>() { { JwtHeaderParameterNames.Typ, type } });
var jwe = jsonWebTokenHandler.EncryptToken(jwsWithDifferentType, encryptingCredentials);

The JwtHeaderParameterNames class can be found here.

JWE Format

Unlike a JWS (which is composed of 3 parts), a JWE is composed of 5 parts. An example of a JWE in JWE Compact Serialization format can be found below (line breaks are for display purposes only):

eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiUnNhU2VjdXJpdHlLZXlfMjA0OCIsInR5cCI6IkpXVCJ9.
QkA3lz1JdtgurjXd_Qi0wZhIRicqF6Hy1tFcm8ASQaI379pD-dNPHpehxJfz5WqRrV7p0xfsBkTvXyqzHnQnCrDVY2hD50p1dEDGUsKw48In5kghWwXscWmmbNwKt1QG8XYX9H42pBiuIkOuWiwMhAh8gMLvTaKg-Mg6p0-DdbmQGjCU9Di2rZo-gdm7lguWiTVwj1tI6D1MGJbiTa8f5XLwb26_G-ShDcHhE4LykfRayK-ohxa2_YI1Eyj0HYmfEYhVZK2hnXJGzgFsR048HiUj9pnW_9l7aUlyc5ZPzd7APzvPLzEAGAbpUWrxlMp350bG6Hd_90HxGxdcqCMESw.
BAYsl8n-cwhuS6ykkGTJEQ.
frYtdApw1pTtK-budjPCd30RJqegqz1-B0my9zMfue5OrJLLxipxrSeZ3QiNWZ4bwW0FzfHXGi_EoeHiph6hFEln7wdwAOdnd-mXW2LylwMQH0QBitc4e77gZ1G77SbhY2glses57fWW_v4J4JobYFP0GQh18LK-g9spQFK6og2o5M3VV1s0f_dyOnjh0_WrLAWOOnuLzGUxK3-GWwPNCFSkECGoOjNvCxy5FeewwWKfMc_gc3xnVkieRWGP1bbHFDLzDy3_eDDdRzhI3kPGZnSbxx9Fi-JVzq4UncfIrl4hyBJz3fMooqIlg_AR1QUctl-a19yGX1p77stzvOu0aXIehju7V1STkXM31jYi4eBm0K50-1weCHAbKrBtuk8yFfuqPLefcMghHq3-ATAVGoaoY-KS6ODRJDL8jC4ALcoFySD3uxYvmg4bCAHTF7ezYWrhzWYddWRTK8SMf-tOPZgLZ-bA4rU5OvPsNC3yQpZFg8nxcUJ1JcakFD2eToZ4QNpXysOZwzSUvMT9Hx3uTHtlOC5M_uvZkp-zotuGMoJHmsV1lwnoSQ0CLFqUiIBA.
JEduKaavj1noos2cLuP9Ig

The components of the JWE are as follows:

JWE Protected Header

eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiUnNhU2VjdXJpdHlLZXlfMjA0OCIsInR5cCI6IkpXVCJ9.

When base64 decoded, the value of the header above is as follows:

{{ "alg": "RSA1_5", "enc": "A128CBC-HS256", "kid": "RsaSecurityKey_2048", "typ": "JWT"}}

The 'alg' header parameter describes the key wrapping algorithm that was used to encrypt the content encryption key (CEK). This 'alg' was used along with the provided EncryptingCredentials to encrypt the CEK (generated key used for encrypting the JWS). The CEK was then used along with the 'enc' algorithm to encrypt the inner JWS.

The 'kid' is the key identifier that describes the public key (identifer of the key provided in the EncryptingCredentials).

If instead direct encryption was used, the header would look something like this:

{{ "alg": "dir", "enc": "A256CBC-HS512", "kid": "DefaultSymmetricSecurityKey_512", "typ": "JWT"}}

Because the 'alg' header parameter here is 'dir', it means that the provided EncryptingCredentials were actually used to encrypt the inner JWS directly. In this case, the token would contain no value for the encrypted key (see the JWE Encrypted Key section below).

For more information on the meaning of these header values, see Section 4 in the RFC.

JWE Encrypted Key

QkA3lz1JdtgurjXd_Qi0wZhIRicqF6Hy1tFcm8ASQaI379pD-dNPHpehxJfz5WqRrV7p0xfsBkTvXyqzHnQnCrDVY2hD50p1dEDGUsKw48In5kghWwXscWmmbNwKt1QG8XYX9H42pBiuIkOuWiwMhAh8gMLvTaKg-Mg6p0-DdbmQGjCU9Di2rZo-gdm7lguWiTVwj1tI6D1MGJbiTa8f5XLwb26_G-ShDcHhE4LykfRayK-ohxa2_YI1Eyj0HYmfEYhVZK2hnXJGzgFsR048HiUj9pnW_9l7aUlyc5ZPzd7APzvPLzEAGAbpUWrxlMp350bG6Hd_90HxGxdcqCMESw.

This is a symmetric key that was generated to encrypt the JWS. This key has been encrypted using the 'alg' found in the header and the public key provided in the EncryptingCredentials.

JWE Initialization Vector

BAYsl8n-cwhuS6ykkGTJEQ.

This is a randomly generated value that is required for some content encryption algorithms. In the key wrapping scenario outlined above, it is used along with the CEK to encrypt the inner JWS.

JWE Ciphertext

frYtdApw1pTtK-budjPCd30RJqegqz1-B0my9zMfue5OrJLLxipxrSeZ3QiNWZ4bwW0FzfHXGi_EoeHiph6hFEln7wdwAOdnd-mXW2LylwMQH0QBitc4e77gZ1G77SbhY2glses57fWW_v4J4JobYFP0GQh18LK-g9spQFK6og2o5M3VV1s0f_dyOnjh0_WrLAWOOnuLzGUxK3-GWwPNCFSkECGoOjNvCxy5FeewwWKfMc_gc3xnVkieRWGP1bbHFDLzDy3_eDDdRzhI3kPGZnSbxx9Fi-JVzq4UncfIrl4hyBJz3fMooqIlg_AR1QUctl-a19yGX1p77stzvOu0aXIehju7V1STkXM31jYi4eBm0K50-1weCHAbKrBtuk8yFfuqPLefcMghHq3-ATAVGoaoY-KS6ODRJDL8jC4ALcoFySD3uxYvmg4bCAHTF7ezYWrhzWYddWRTK8SMf-tOPZgLZ-bA4rU5OvPsNC3yQpZFg8nxcUJ1JcakFD2eToZ4QNpXysOZwzSUvMT9Hx3uTHtlOC5M_uvZkp-zotuGMoJHmsV1lwnoSQ0CLFqUiIBA.

After decryption (and base 64 decoding) this would look something like the following:

{{  "alg": "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",  "kid": "DefaultSymmetricSecurityKey_256",  "typ": "JWT"}}.{{  "email": "Bob@contoso.com",  "given_name": "Bob",  "iss": "http://Default.Issuer.com",  "aud": "http://Default.Audience.com",  "iat": "1489775617",  "nbf": "1489775617",  "exp": "1616006017"}}.Signature

JWE Authentication Tag

JEduKaavj1noos2cLuP9Ig

This value is generated along with the Ciphertext during the encryption process. It is then used during the decryption process.

To learn more about JWEs, you can read through this RFC.

Clone this wiki locally