using System.Collections.Generic;
using System.Security.Cryptography;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
const string M11EJsonWebKeySet = "{\"keys\":[{\"d\":\"qXljkUVHDMgcib4tVjItXaFoNz08fkw2tHiP1YdHPtc1SrUo74z_IF8SXk60zWRfrfNUKrZTK2fgfeP9QPTpHgW38ZFPg4cvUu03-Hg9_JlZKGMoOqoplM0_lZg_DW45zwTsDFGEpd25hW9DurNde6WbRZjtfBDQoGQ5GYkdQ4Ad4UeuML9y4IAc-oC_2GOISmsn4i9mhS5hHONM0_dC__R_S6j8AVWdq-ujqPBegMrqyq-ml_FV2aMv6ZSOKFncIvgznTC-_VgvR72yeFIM5Eyt37VDUhcxwcI_1dmel0Bxgi0TqnCq-b1kyYe-rbooA_hfpUjI7VpF1N4UXiXcHGYVtfzpUsGz2E7tB_KIxfEbvELLGsU3aJfa527gMvJmXEwTJhxr3PEOsybi248zYygpwkA2hYeKrzZWFmuHGnIclnsVBhsnUsJ-vHS2ne5c3VZToYMd1-dToKXWXD3Av0rHrY8oKgOP-OpKqtRt-cqLIyxYwslvf7EkqTCodmQV\",\"dp\":\"UdJXHIBLTIdsLAayAATbSECS360Q-QGmx7_QyM1cnfWB5QnjdWouKOsatppr0EgVlZ0UQMrtv8h2LRFddilHd6g59LH7DN1ilmIH2QOQKZzV84KfwAL06fA18mXfzOk2RhnsXf7oleL_3LkwbiER9EbxmcpdIWX5p0xORZu6oWrlu_tYmY7E7dbpFCNoVMKVIjGMI3HDm5V7UuMxvK6Bk7k7TwBjj8pB3GXK6i29h5GgR-s2x7mIXnX0ooENS9M3\",\"dq\":\"MnaaYKHwvmAOhSTMes2HV1kat0iAI3s2CgwVN4odc0UZ-SZ38dlu7AdXpaokvmuffxgC8OoLvlbOxq3fYa3K4AtG0K926AKQVfkIzEt2zF30N3Dsnz7AXCF-fmpt7mFNPRP4hj37Rd7NKLj101WSHY2elWsfT1XjLlHZJY0DJfOMlkCHJQR8cLZho0GA-Ln7wLODL9Xb1oUn9ujWJyL7DNM18g3ZH337-jG4-C7SDnQ4RKJG993ATovEd9hj9qe5\",\"e\":\"AQAB\",\"kid\":\"EncryptionKeyId\",\"kty\":\"RSA\",\"n\":\"vW399aNyqxINEBeknAs12McoEibrKsc2xremZf94gG18Z3QOC2vpgvjWoMrP6EnK38udlB-efEPU-ZsPTrWLZKXIQjsqe1r0b0BtJLl8A-RGILijWqNxd5Z_jCOqXG_Sgp7ltUjhJohngkIv4z_Wkdny3zTven5QxSlFWuiLUUpdpHLlpvbMhMLgSFz92jrrcIsq8CkvPWVwmp2ZWUrYIRxR5F0RNVbplill3nWc2ZjlZ3LtQ1stX3GrqIEqs1FLGrXnPY8QQ9E2bP49A3QeFFrxauUJ70hLfE0otCJmwz2twx3JxzUo1NBK7ywbIa7Y__yDtNcyFCQAkGiL2694Uqkl1q_3uQMPC7j4IULR6gP7BT2hq_nxswQCzNj8j5xrJYBMe6rXTCJS_lIBKV4S4C5ZhEGgfSZaY6kNVWJnOKYPlBuY9G3QSR8Ix72nHtLZTCAwExfzuJWdnf_fwCVhFw1kPFBEO_4h2NRhO9vslh0w-L212AYaz61IK7CXbQ3x\",\"p\":\"_GGbL6VLZiViOhXhgEsgrXPEGLksVAkbNf9nbATWK6qMZhMRL2hTONM8AufGZCwSn62b5blN7Ys5Eb4HAbUyYxwCOmM02BBIh96IBNxQTp4-P4D_Rc6WTph2sAe82YQzgyEIAu5hMeOP2Mgq-1TDl3nMqDNEOrDXHBv4141lI5kwlELOKkiWbI6xRiRFvYB5glDHZRNsWL19Xy3s46DLN5c2WtHlbC8CkwPJfbZtP_Tp0BBe4hxdKTLYz5iq3KrL\",\"q\":\"wCVQmibt2_V7pSS7R3s-H8at2GzdQW7b2MQUSBiV4mkGOIZ4qESMP2TlXKhm7pLq65YhzlZ0IwUiDWVJ0xmXI_dMCC87TpzUZjrhUjlHVH3li9OPM_zmhSx7B-0lUHuuWdpdUYsO_Xj-vlnQOy_ar0N3r7WVqZLtNAM0Z5wuNgAoIBVg_0fUhzX-1aDBJg9qeTyQBhV45WvWxaYfDh3l8GMJxpX4dtq_zy5kdAJLRzAcc1YFgVlrznFwb80C5qaz\",\"qi\":\"EWjb7GCVbJJ-URu1ci0Z7-zSFoSv7Z_ArWkiYCv3Z9iV4MXnQ25lTPpAMotMjJ3VTXyOTCLfiuWk2ug6qpKc4l83t5YF14V9_PllF4zQlObaz6q4je6yakIW_VFkofxcS1Z83AvcV4cb4oAp3raeyKrqccBNc6bcLDYid5COXOVKsFiOyU2e46WG_UEk-LDSlgMXQyFwT1oKeVdxis-20n12VrPgUCuueWpwojfb8tRQOOIBZa8TaJDLcghlUqhu\",\"use\":\"enc\"},{\"e\":\"AQAB\",\"kid\":\"SigningKeyId\",\"kty\":\"RSA\",\"n\":\"2Ajascz8gjp33vimSUho_VTmyEv-FF7Mg17MLZSAKSJBr_suLC9pUUP3EJeCEmOZ55DJ6XP4x07ov-3mEIGmOYluF33SjKIeONutHaTjQajsX-68Vn0nbehpdWwWAFy5vXVASJPQlJ8KBYGO2xDnMK9cRo9__pUJTQN9WLZiRWEo9lzkBUjR_1vDzVD_barH_PIkzsW_jJQhEBvpuQr6YnnBvCJCDeGCAXI72R9ly34ICXxafYAKzEGpGubZx_O98dlFCPzJXhkeW4xd3VE3yM1oyZ2OO63lq9B86q6Y9DaUMkGPsXAP0hMh1Azp9YMDrEtWN9ZnhEDRgEakZRebINMQoe095na7-X0OKL_mt58HBhwAH_kTLzq7irceDi6gxHUFlBGm4Jj34CGWlo52ZbZBhTWPX0gYzoBIr5Ofc8x9apGffktqpQP4xRww-w1OEJNtmRf4VznygOtp6wicMPmVot3PLeHC7N-iTtqL9qfWC_P6-sQ0Y0-zCRwASHpp\",\"use\":\"sig\"}]}";
const string SavilleJsonWebKeySet = "{\"keys\":[{\"e\":\"AQAB\",\"kid\":\"EncryptionKeyId\",\"kty\":\"RSA\",\"n\":\"vW399aNyqxINEBeknAs12McoEibrKsc2xremZf94gG18Z3QOC2vpgvjWoMrP6EnK38udlB-efEPU-ZsPTrWLZKXIQjsqe1r0b0BtJLl8A-RGILijWqNxd5Z_jCOqXG_Sgp7ltUjhJohngkIv4z_Wkdny3zTven5QxSlFWuiLUUpdpHLlpvbMhMLgSFz92jrrcIsq8CkvPWVwmp2ZWUrYIRxR5F0RNVbplill3nWc2ZjlZ3LtQ1stX3GrqIEqs1FLGrXnPY8QQ9E2bP49A3QeFFrxauUJ70hLfE0otCJmwz2twx3JxzUo1NBK7ywbIa7Y__yDtNcyFCQAkGiL2694Uqkl1q_3uQMPC7j4IULR6gP7BT2hq_nxswQCzNj8j5xrJYBMe6rXTCJS_lIBKV4S4C5ZhEGgfSZaY6kNVWJnOKYPlBuY9G3QSR8Ix72nHtLZTCAwExfzuJWdnf_fwCVhFw1kPFBEO_4h2NRhO9vslh0w-L212AYaz61IK7CXbQ3x\",\"use\":\"enc\"},{\"d\":\"0hgyTw3SV_fykbZxSP2RlSUXV52H2FmlPPFZUc6EoPw-s51sdu4Y1z5orQsbJTUV_LyzkEUHoRUdh2IeBtl_RZ9goJntxHT5PURuZFDx7e4RCu7K3LXUPa_zt41JDEshG55GyQ5vn8nXXil0O5mBrLgt3jU6PeTE6c_F_BtLELv_sS__VSBPka61ihwGfKnE2jrHDwwDT_zS9q6eqyWBAXn1rjgG0ycHXs6wY2SEg9TqjgcQndKDLceBZLcy_qELxBiCdzj9__vBKuWgslWkewW89nxoLoogQDHn79QEOJ_VgSVrI7kNL8bx3xWfg4gaEnnN4xOox3I-qZsDpouBjlvMJelsgqQAZWXkRamqcMSM-ujOqfvGf4sHJm-IU_cu-ZUqD-ocrAEkTUxl6EHkaY87fgirqfDnls2KPf5OYpszCBqBBaiSiIuHJiyvuAuXSfqG0Z9ZSP3EuzEYz0ISfVZ9U7-T2XsDZnRfXanOLdy7HqU95-w6eWbUK5Z5ni1d\",\"dp\":\"lDejza4KFnTPBxB3CmrVpcjqyM9VHBo5D8u9_IusWltZZtBt7OVHnTF9TXQ06iyvSPGgk2xNYHNAMlVo7Gmj-wB8lZqOcaSFwn7eOeeiSaJhwt6nBviCJxlzyraW5uU5XF-HFalKFTRMf6nyv4sKcEEE68dEQ0L7tu-OCI1QiX0EPXTC-0Gwe9VJX8o92yRy__2wuec_4D2o8w2P46E63P-zILCIHdPasFurymxtwlZs2-xOVm9aDFZHFH5N5WDB\",\"dq\":\"VKmEkR1iR3Vwyh5y-U3vE4mqtmFal647Nw4JpU9XwH5NsSZNRiii1W_rtfQ6MGWuP3jEmKKVrOaRmZjhsfieEevZC9ZJ8-bVrZklsnenbcFVGDoi3apk57CWM5OiDfrNtwR0C37qv6nuiPfxG2hANt-5bLsojqA67Cfa5XegHKqX29HIAS_OLvfbqW3uAIGtZJr2Vwxkly0P0BEHlp14SikkWDn89GrFDvyFQFDZnMjkHTeyDMGm70H8v79ClezH\",\"e\":\"AQAB\",\"kid\":\"SigningKeyId\",\"kty\":\"RSA\",\"n\":\"2Ajascz8gjp33vimSUho_VTmyEv-FF7Mg17MLZSAKSJBr_suLC9pUUP3EJeCEmOZ55DJ6XP4x07ov-3mEIGmOYluF33SjKIeONutHaTjQajsX-68Vn0nbehpdWwWAFy5vXVASJPQlJ8KBYGO2xDnMK9cRo9__pUJTQN9WLZiRWEo9lzkBUjR_1vDzVD_barH_PIkzsW_jJQhEBvpuQr6YnnBvCJCDeGCAXI72R9ly34ICXxafYAKzEGpGubZx_O98dlFCPzJXhkeW4xd3VE3yM1oyZ2OO63lq9B86q6Y9DaUMkGPsXAP0hMh1Azp9YMDrEtWN9ZnhEDRgEakZRebINMQoe095na7-X0OKL_mt58HBhwAH_kTLzq7irceDi6gxHUFlBGm4Jj34CGWlo52ZbZBhTWPX0gYzoBIr5Ofc8x9apGffktqpQP4xRww-w1OEJNtmRf4VznygOtp6wicMPmVot3PLeHC7N-iTtqL9qfWC_P6-sQ0Y0-zCRwASHpp\",\"p\":\"69yLRYgBOK7gvsDu3Jrz-DvG0s9z1oXM8RyHtaq407DBwQGFdBXPFT2g6dSm4JTx0BFdUjsU9w2HsV11kYUslc8D6qlMqDLeOoJKvv38udAiC-_zGwyWkvgDAnOT-ZxYuYp24d38AywrSbyu5x9KkBVqTHVCTZ7squ-Fa8gQDgITD6up-HhqNCafD9YD0BOU0F1ky9XKvPOGkYt0q16sPzhkc_0SMLVchTYUhp19aA6ddB9LAcad6NQpPeUpZYlL\",\"q\":\"6nrvGO-wmcs6y1DsiOLvQgioVmtlTjdmm0KYjng_yNj1L7L0ZYrNYXN6phuWw32DZ3KJedzHL6MH0tk1LrqT38gI_2XVPz48F0_owe1C13MLCn8bxOai-NJge1oEc_wD9Sle9EAoJKkjyWokUl2esDDRECiP3PTRzEutL_FY2yudiKVDP3h1Qpa7MmvEvZTO9mLZR8oIdJlPWEcjpLPhJDQhHqGIvYna_3GWOZllVWsJtJerJNsNHHO3MCdRfM6b\",\"qi\":\"3gwTXz6ZlW6mYfhsllytJdCsxLvGYDwtvm8WrBsdwcnL7eYKBB9R6T3So4n_qsbdLhlIQx7OExyRH-2O-5juws2QmvrXIauVEh6d9HM8q7SIOOmqmkcd_aemui0CyZER0_wyz7uj2wQICllzc3T8hAUKSsO2lwFRYaUf5wGyrI2OHwXsQdTaLAIcxW_6KkIK86_7bT9f5bIiuPdiQ7T6a7cZHacsLAsTmXAnI_Mv4ppUnpHiTCnaGxLToP0GdAGn\",\"use\":\"sig\"}]}";
public static void Main()
var tokenGenerator = new JsonEncryptedWebTokenGenerator();
var additionalOrderIds = new Dictionary<string, object>
{ "projectId", "bf74a437-1d09-43c2-b547-d255dbe3830c" },
{ "workflowId", "e29121ee-d294-4b78-adb7-c73e2cecf173" },
var claims = new Dictionary<string, object>
{ "email", "john.doe@example.com" },
{ "telephone", "00000000001" },
{ "candidateId", "f7a4b360-7bd6-4815-9cbf-c0f3c70dc9a6" },
{ "formId", "f92cfe29-0cff-4ee5-a12d-d1cecd30c855" },
{ "instanceId", "b688e7b0-cebd-4db4-b134-5985e9f18f59" },
{ "additionalOrderIds", additionalOrderIds },
Console.WriteLine("Saville Json Web Key Set:");
Console.WriteLine(SavilleJsonWebKeySet);
var savilleKeySet = new JsonWebKeySet(SavilleJsonWebKeySet);
var token = tokenGenerator.GenerateJwe(claims, savilleKeySet);
Console.WriteLine("Encrypted token: ");
Console.WriteLine(token);
public class JsonEncryptedWebTokenGenerator
private const string EncryptionKid = "EncryptionKeyId";
private const string SigningKid = "SigningKeyId";
public JsonEncryptedWebTokenGenerator()
public string GenerateJwe(Dictionary<string, object> claims, JsonWebKeySet keySet)
var signingKey = GetRsaSecurityKey(keySet.Keys.FirstOrDefault(k => k.Kid == SigningKid), true);
var encryptionKey = GetRsaSecurityKey(keySet.Keys.FirstOrDefault(k => k.Kid == EncryptionKid), false);
var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.RsaSha256);
var encryptingCredentials = new EncryptingCredentials(encryptionKey, SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes256CbcHmacSha512);
var tokenDescriptor = new SecurityTokenDescriptor
Issuer = "https://idp.example.com",
SigningCredentials = signingCredentials,
EncryptingCredentials = encryptingCredentials
var handler = new JsonWebTokenHandler();
return handler.CreateToken(tokenDescriptor);
public TokenValidationResult DecryptJwe(string token, JsonWebKeySet keySet)
var handler = new JsonWebTokenHandler();
return handler.ValidateToken(
new TokenValidationParameters
ValidIssuer = "https://idp.example.com",
IssuerSigningKeyResolver = (tokenString, securityToken, keyIdentifier, parameters) =>
.Where(k => k.Use == "sig" && (string.IsNullOrEmpty(keyIdentifier) || k.Kid == keyIdentifier))
.Select(k => GetRsaSecurityKey(k, false));
TokenDecryptionKeyResolver = (tokenString, securityToken, keyIdentifier, parameters) =>
.Where(k => k.Use == "enc" && (string.IsNullOrEmpty(keyIdentifier) || k.Kid == keyIdentifier))
.Select(k => GetRsaSecurityKey(k, true));
private RsaSecurityKey GetRsaSecurityKey(JsonWebKey jsonWebKey, bool includePrivateKey)
if (jsonWebKey.Kty != "RSA")
throw new NotSupportedException();
var rsaParameters = new RSAParameters
Modulus = Base64UrlEncoder.DecodeBytes(jsonWebKey.N),
Exponent = Base64UrlEncoder.DecodeBytes(jsonWebKey.E),
D = includePrivateKey ? Base64UrlEncoder.DecodeBytes(jsonWebKey.D) : null,
DP = includePrivateKey ? Base64UrlEncoder.DecodeBytes(jsonWebKey.DP) : null,
DQ = includePrivateKey ? Base64UrlEncoder.DecodeBytes(jsonWebKey.DQ) : null,
P = includePrivateKey ? Base64UrlEncoder.DecodeBytes(jsonWebKey.P) : null,
Q = includePrivateKey ? Base64UrlEncoder.DecodeBytes(jsonWebKey.Q) : null,
InverseQ = includePrivateKey ? Base64UrlEncoder.DecodeBytes(jsonWebKey.QI) : null
var securityKey = new RsaSecurityKey(RSA.Create(rsaParameters));
securityKey.KeyId = jsonWebKey.Kid ?? jsonWebKey.KeyId;