AzureAD / azure-activedirectory-identitymodel-extensions-for-dotnet

IdentityModel extensions for .Net
MIT License
1.03k stars 385 forks source link

[Bug] Decoding JWE ECDH-ES+A256KW A256GCM #2518

Open slav4ik51493 opened 4 months ago

slav4ik51493 commented 4 months ago

Which version of Microsoft.IdentityModel are you using? System.IdentityModel.Tokens.Jwt 7.4.0

Where is the issue?

Is this a new or an existing app? c. Experemental app for testing before implementing to existing app.

Repro

using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography;
using JwtHeaderParameterNames = System.IdentityModel.Tokens.Jwt.JwtHeaderParameterNames;

IdentityModelEventSource.ShowPII = true;
IdentityModelEventSource.LogCompleteSecurityArtifact = true;

//header.encryptedKey.iv.ciphertext.tag
string encryptedToken = "eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiRUNESC1FUytBMjU2S1ciLCJraWQiOiI3VGt5TWFqV0JYUlo3aVpmZ3lQUGZmMmdMMzloMlh0ZkpEemNzNXRjZXJNIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoibXo1c3FfQi1YbFNYTW1oQm9YWDdmR0ZfME5wOFJqS3F0VUF3cXphU3dMZyIsInkiOiJJUVFRYzhpQVpMbzhiY2NfNUxEWW5kVEJIVXY0Rlc3T3loUHdwakZISmlJIn19.p81T3WizOuNm1XvU-PjksrrDvSExL93EBiC3tUYyJnpPSECXrCdAag.rxpe3T3vPcqenJpF.l29eduC5ZO4MzDsGzXoU-qqNHrWbdZbQulHM43-a0OxRl2NaqVJcyEP3v4JVmNFWZbSHp5IWS1lvM4Sr68l17qSq-voCKrUnqm-7HgEC2QP7pjP3waftgHTyvFYSoc3yXkTSvdL_5OUPcauW1Gay62d-iJWoJqO014VLoSicboMfiCc2bEn1J8iNeC4uWo2yuUgBjo-WR13xRTVi8-8kIjssrIB6aIJpTCdX7cghZ6O3SZZ0Cq8RWx0jcFy41fvxxko-8Z-d16MHqtnzGn4t12Hkjv9ux3KdiWUCy8w7nRvPyClS87pk5C_tqXyaT5vEa2t45JJk1mFbJIuco5C2idHCfDfYQUoBh-8cqvi-MAr9oncQMr5FrZtKmW5HBKCdK6FXZcDZrK0q6YFZXz0t-kc7-pJN6EGZLoRaVuQQ1GzQjpKmimvZ2eqOicBm8nJjT1ppd0NS-ypM_aaPbTtASeSvGQBwgLqxWAccZXbWXRPiqWTKkgRre6oumQtRabfPkdTAjNs7G4wosLDs4K4Qxse5lwvUubOVd4hPTZ8uzUJy3KbaUug3xtJ1ovYIEiA2cbJOhNapSqo3Xu3SKq1orw7eqO6ZfHrSEUb7B2io-ubgYYhTtPzRw3u_2jJVHfLbeszMKWEM291GDaz9_jyuaqGGtglJmeHQ2AUn-tjCVyQdn-txsTzT8gxK4QgK-3J7jnsrecXsiI9gLj1xcG4PTtmJXUtqz07-jaNok-w1H05XbVME3QaFk3tMeA0OYjNhOXpOzhL4AlDxQWwNQ-ulZKZHs1zPUlVDv0nlcpAsOfUqXlBJncAMJ1mvyHxvNMOZsTeLQav2aKr6S2c6uFknXOKAAI-E1x5WHB4SBs79MrWk8ipcMFrjmWhczQtFd2IYI2r820B_4VbBRsD7i-2D-oTO9JjRinxmvCrcT15z8yWP9b9GSbycsR3jkVXvjYKIaOHzcirq6TdufjPAp_ynyoSMg4EEWelRtzpTqzV253ZCK8HstIbHJm185TZh6hJdAQJpyVfKTjMH014U5jmbZTT3ik43WTD3VF_Uf_VWdXdBN-p5KboiNiC0VoTv8f0jAgU_dvYGNPuUJzytZXGYaTrkaSHqVgVLceqjFB8Q6OUTU7nVJ0d0S_vlEtioKGV6uAsLSSp8q_7B3DI1tZlVklAWfyw-GYpCBF09VU755ZcTiSe2wALp_P7-aAH14-2btknjrMAlQQrF9ZpDqzGT_vgQyDRTORrOdTN26AMCznr58ygmiSCfwFV0dmEKEI6dt7WNvVEuaFk8sKicUQlQFlQpX-YYFhaYGbulCZCuTUqDzQifi_D1VNqedGzfgbDclgVtDF7xLH6Sz7cwMIYsuWPttierh82QdFro0jDuNnbPZwU7TQBC2x8k8tn5nSH8SHd8yjR5uyQXtNTkLKaM_-NU_gJrX6Atalku3dIFUUKMB692vPw4Y154Eh8gYoYwgkwzZUJFgnjU3p2M139DFMMJyvTbiaPtKXx1Go_j8yEkgZpqG7nr7V_AisPCOQxu7Ewk3zi4_e3TQxhU3BcXMZCo8TApGpcoFa2lJIRpDK-KMHuyLnQK930OjdpvujhzDFynI7M36hA8msRVx3G2G2LNpDU4Hqd9FmVOwsmhZLdrcMUVxdB-l_bl9aYdHGjOMcAP_7A_VYX8fH5NlI9wTlPKiqwa23vdBuiaOjlZHjhDifK66s0TrOj7fQYmkAN4qn1AfjH5DTTa52U6z2g.8qcaLnQtJAdbUWAO8NiPMA";
string encryptionPrivateKey = File.ReadAllText("encryption_private_key.pem");

//encryption
var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
ecdsa.ImportFromPem(encryptionPrivateKey);
var privateSecurityKey = new ECDsaSecurityKey(ecdsa);

//signing
var ecdsaSigning = ECDsa.Create(new ECParameters()
{
    Curve = ECCurve.NamedCurves.nistP256,
    Q = new ECPoint()
    {
        X = Base64UrlDecode("L_GG9F-hIWXxUEWCB4Fco6zXJkbaU_aUMSbHVbwEwso"),
        Y = Base64UrlDecode("lNPEj7SHn5IFsO76Xel13d3NDlql8JyToZFylm5V-kU")
    },
});
var privateSigningSecurityKey = new ECDsaSecurityKey(ecdsaSigning)
{
    KeyId = "AFMnnKRWTaBYEhNfEB6iQ5ErC1yqGVyZchH8A7nl_yM"
};

var jwt = new JsonWebToken(encryptedToken);
var epk = new JsonWebKey(jwt.GetHeaderValue<string>(JwtHeaderParameterNames.Epk));
var ecdsaPublic = ECDsa.Create(new ECParameters()
{
    Curve = ECCurve.NamedCurves.nistP256,
    Q = new ECPoint()
    {
        X = Base64UrlDecode(epk.X),
        Y = Base64UrlDecode(epk.Y)
    }
});

var ecdsaSK = new ECDsaSecurityKey(ecdsaPublic);

var validationParameters = new TokenValidationParameters
{
    TokenDecryptionKeyResolver = (_, _, _, _) => new[] { privateSecurityKey },
    TokenDecryptionKey = ecdsaSK,
    ValidateIssuer = false, 
    ValidateAudience = false,
    ValidateLifetime = false,
    ValidateIssuerSigningKey = false,
    IssuerSigningKey = privateSigningSecurityKey
};

var handler = new JwtSecurityTokenHandler();

var jsonToken = handler.ReadToken(encryptedToken) as JwtSecurityToken;
var claims = handler.ValidateToken(encryptedToken, validationParameters, out var validatedToken);
Console.WriteLine(validatedToken);

static byte[] Base64UrlDecode(string base64Url)
{
    string base64 = base64Url.Replace('-', '+').Replace('_', '/');
    while (base64.Length % 4 != 0)
    {
        base64 += '=';
    }

    return Convert.FromBase64String(base64);
}

Edit 1: Here private encryption key encryption_private_key.pem

-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIDr6SLHbruSfQuLOxvc6nAL3w+/Dg9C3pge1aGuZ8rn2oAoGCCqGSM49
AwEHoUQDQgAENfIxJudBO1/XD4DnhkVKPonHux0LG12O3D+Z2NfsuEHYcbp7IlwQ
QFVlSnViUXnstYyUAibJK43nBBx4Suaj3w==
-----END EC PRIVATE KEY-----

Removed singing_public_key.pem, because it wasn't used. Public signin key creating manually by hardcode and it work perfect on windows.

Expected behavior I want to get decrypted token.

Actual behavior It's working perfect on Windows. But I'm getting next error in Docker (Linux). I've tried to use .NET 7 and .NET 8.

Possible solution

Additional context / logs / screenshots / links to code My task is decrypt JWE with ECDH-ES+A256KW A256GCM. I've tried to use jose-jwt and it's very simple for windows, but it's using CngKey.Import which doesn't supporting on Linux. I'm trying to use this library, but catch this problem. I've also created question to SO, but it's still without any answer https://stackoverflow.com/questions/78108216/decrypt-jwe-ecdh-esa256kw-a256gcm. I hope that someone could help how to fix with this package or some other workable way on linux

Stack trace:

IDX10603: Decryption failed. Keys tried: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey, KeyId: '', InternalId: 'YrTOhgtBdNIfFUY7kudowDhgQkhdO6agUXzrJ-ETjtE'.
'.
Exceptions caught:
 'System.TypeInitializationException: The type initializer for 'Microsoft.IdentityModel.Tokens.AesGcm' threw an exception.
 ---> System.DllNotFoundException: Unable to load shared library 'BCrypt.dll' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable: 
/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.2/BCrypt.dll.so: cannot open shared object file: No such file or directory
/app/bin/Debug/net8.0/BCrypt.dll.so: cannot open shared object file: No such file or directory
/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.2/libBCrypt.dll.so: cannot open shared object file: No such file or directory
/app/bin/Debug/net8.0/libBCrypt.dll.so: cannot open shared object file: No such file or directory
/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.2/BCrypt.dll: cannot open shared object file: No such file or directory
/app/bin/Debug/net8.0/BCrypt.dll: cannot open shared object file: No such file or directory
/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.2/libBCrypt.dll: cannot open shared object file: No such file or directory
/app/bin/Debug/net8.0/libBCrypt.dll: cannot open shared object file: No such file or directory

   at Microsoft.IdentityModel.Tokens.Interop.BCrypt.BCryptOpenAlgorithmProvider(SafeAlgorithmHandle& phAlgorithm, String pszAlgId, String pszImplementation, Int32 dwFlags)
   at Microsoft.IdentityModel.Tokens.Cng.BCryptOpenAlgorithmProvider(String pszAlgId, String pszImplementation, OpenAlgorithmProviderFlags dwFlags)
   at Microsoft.IdentityModel.Tokens.AesBCryptModes.<>c__DisplayClass0_0.<OpenAesAlgorithm>b__0()
   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
   at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
   at System.Lazy`1.CreateValue()
   at Microsoft.IdentityModel.Tokens.AesGcm..cctor()
   --- End of inner exception stack trace ---
   at Microsoft.IdentityModel.Tokens.AesGcm.ImportKey(Byte[] key)
   at Microsoft.IdentityModel.Tokens.AesGcm..ctor(Byte[] key)
   at Microsoft.IdentityModel.Tokens.AuthenticatedEncryptionProvider.CreateAesGcmInstance()
   at Microsoft.IdentityModel.Tokens.DisposableObjectPool`1.CreateInstance()
   at Microsoft.IdentityModel.Tokens.DisposableObjectPool`1.Allocate()
   at Microsoft.IdentityModel.Tokens.AuthenticatedEncryptionProvider.DecryptWithAesGcm(Byte[] ciphertext, Byte[] authenticatedData, Byte[] iv, Byte[] authenticationTag)
   at Microsoft.IdentityModel.Tokens.AuthenticatedEncryptionProvider.Decrypt(Byte[] ciphertext, Byte[] authenticatedData, Byte[] iv, Byte[] authenticationTag)
   at Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities.DecryptToken(CryptoProviderFactory cryptoProviderFactory, SecurityKey key, String encAlg, Byte[] ciphertext, Byte[] headerAscii, Byte[] initializationVector, Byte[] authenticationTag)
   at Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities.DecryptJwtToken(SecurityToken securityToken, TokenValidationParameters validationParameters, JwtTokenDecryptionParameters decryptionParameters)
'.
token: '*my token here*'
user163 commented 4 months ago

Please complete your test data. Without the keys in encryption_private_key.pem and signing_public_key.pem no repro is possible.

slav4ik51493 commented 4 months ago

@user163 thanks. I've added it.