-
Notifications
You must be signed in to change notification settings - Fork 401
Creating and Validating JWEs (Json Web Encryptions)
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.
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, "[email protected]" },
{ 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.
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
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.
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:
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.
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
.
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.
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": "[email protected]", "given_name": "Bob", "iss": "http://Default.Issuer.com", "aud": "http://Default.Audience.com", "iat": "1489775617", "nbf": "1489775617", "exp": "1616006017"}}.Signature
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.
Conceptual Documentation
- Using TokenValidationParameters.ValidateIssuerSigningKey
- Scenarios
- Validating tokens
- Outbound policy claim type mapping
- How ASP.NET Core uses Microsoft.IdentityModel extensions for .NET
- Using a custom CryptoProvider
- SignedHttpRequest aka PoP (Proof-of-Possession)
- Creating and Validating JWEs (Json Web Encryptions)
- Caching in Microsoft.IdentityModel
- Resiliency on metadata refresh
- Use KeyVault extensions
- Signing key roll over