使用C#验证JWT(ES256)令牌与公钥

5
我得到了一个JWT,它是由我的AWS ALB使用OpenID Connector创建的。我需要在我的c#应用程序中验证令牌。但我很难让它正常工作。
AWS在此处描述了令牌验证:https://docs.aws.amazon.com/elasticloadbalancing/latest/application/listener-authenticate-users.html?icmpid=docs_elbv2_console 它应该分为3步进行:
1. 从令牌中获取密钥ID 2. 从aws alb端点读取公钥 `(https://public-keys.auth.elb.' + region + '.amazonaws.com/' + key id)` 3. 使用密钥解密有效载荷
这是我得到的JWT:

eyJ0eXAiOiJKV1QiLCJraWQiOiJjODE4ZTcxNi01OTAxLTQzOWQtOWFlZC1lYmRmODAyYjZkYTkiLCJhbGciOiJFUzI1NiIsImlzcyI6Imh0dHBzOi8vc2llbWVucy1xYS0wMDA2OS5ldS5hdXRoMC5jb20vIiwiY2xpZW50IjoiMndsS3k0YlRXbGpZWm9KYXZRSVFqVTE3OUprVG4zNDAiLCJzaWduZXIiOiJhcm46YXdzOmVsYXN0aWNsb2FkYmFsYW5jaW5nOmV1LWNlbnRyYWwtMTo0ODU2ODM0ODcxOTY6bG9hZGJhbGFuY2VyL2FwcC9maW5kLXRlc3QtYWxiLzU3YzBmMWYzZjg0YzZjMjEiLCJleHAiOjE1NzU1NDMwMzN9.eyJzdWIiOiJvYXV0aDJ8bWFpbi10ZW5hbnQtb2lkY3xzYW1scHxTaWVtZW5zfFowMDJFSk5VIiwiZ2l2ZW5fbmFtZSI6IlJhcGhhZWwiLCJmYW1pbHlfbmFtZSI6IlNjaG5haXRsIiwibmlja25hbWUiOiJSYXBoYWVsIiwibmFtZSI6IlJhcGhhZWwgU2NobmFpdGwiLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvODkzNWVlY2QzMDc2ZTAyMTQ5ODE2MTZmZjBkZTRkZjI_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZyYS5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOS0xMi0wNVQxMDo0ODozMy4wNjhaIiwiZXhwIjoxNTc1NTQzMDMzLCJpc3MiOiJodHRwczovL3NpZW1lbnMtcWEtMDAwNjkuZXUuYXV0aDAuY29tLyJ9.M39aPefXmaDGzaDd0qHcQHMhvugTVN4i4pyvGJ-7fayewU9vZdtKvSzFF9rVal8GEz7HKTr_auqMw9HemOWyag==

因此,关键id为:c818e716-5901-439d-9aed-ebdf802b6da9 公钥:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENARdEGaEpfgHph3440UodVsQdqxi
PYz+l1aEcz+Bivr6emXDnor1nET94dbPqYxk+vtUHGkgOb44VPEZUe4ijQ==
-----END PUBLIC KEY-----

我尝试了以下几种方法来验证代码:

  • 使用 System.IdentityModel.Tokens.Jwt 命名空间。但我无法将公钥作为字符串提供给 ValidationParameters。我遵循了这个答案的类似方法 https://dev59.com/HFsW5IYBdhLWcg3w8rH2#51866939。它感觉复杂且易出错。由于我整整一天都没能让它正常工作,所以我决定转向一个提供相同功能的 NuGet 包。
  • 使用 JWT NuGet 包,但它不支持 AWS ALB 使用的算法。
  • 使用 Jose.JWT NuGet 包并使用以下代码:

Jose.JWT.Decode(__token, publicKeyPem, Jose.JwsAlgorithm.ES256)

会得到以下错误:

错误:脚本执行失败。[ArgumentException] EcdsaUsingSha 算法期望密钥是 CngKey 或 ECDsa 类型。

我该如何在 C# 中简单地验证 AWS ALB 的 JWT?


你的JWT是由https://siemens-qa-00069.eu.auth0.com/颁发的。为什么你要试图使用AWS密钥来验证它? - Vlad DX
1
因为JWT是由AWS负载均衡器生成的。 - quadroid
你如何解释JWT将 https://siemens-qa-00069.eu.auth0.com/ 作为发行者? - Vlad DX
2
我想验证签名者,签名者在 JWT 中被称为 "signer": "arn:aws:elasticloadbalancing:eu-central-1:485683487196:loadbalancer/app/find-test-alb/57c0f1f3f84c6c21"。这就是为什么我试图将其与 AWS 密钥进行验证。如果您尝试在 jwt.io 上使用公钥,则会发现它显示为有效。(您需要删除签名中的尾随 = - quadroid
1个回答

5
公钥在https://siemens-qa-00069.eu.auth0.com/.well-known/openid-configuration上不可用。您可以尝试手动方法:
    private static ECDsa LoadPublicKey(byte[] key)
    {
        byte[] pubKeyX = key.Skip(27).Take(32).ToArray();
        byte[] pubKeyY = key.Skip(59).Take(32).ToArray();
        return ECDsa.Create(new ECParameters
        {
            Curve = ECCurve.NamedCurves.nistP256,
            Q = new ECPoint
            {
                X = pubKeyX,
                Y = pubKeyY
            }
        });
    }

        string key = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENARdEGaEpfgHph3440UodVsQdqxiPYz+l1aEcz+Bivr6emXDnor1nET94dbPqYxk+vtUHGkgOb44VPEZUe4ijQ==";
        ECDsa ecdsa = LoadPublicKey(Convert.FromBase64String(key));
        string authorizationDomain = "https://siemens-qa-00069.eu.auth0.com/";
        string jwt = "eyJ0eXAiOiJKV1QiLCJraWQiOiJjODE4ZTcxNi01OTAxLTQzOWQtOWFlZC1lYmRmODAyYjZkYTkiLCJhbGciOiJFUzI1NiIsImlzcyI6Imh0dHBzOi8vc2llbWVucy1xYS0wMDA2OS5ldS5hdXRoMC5jb20vIiwiY2xpZW50IjoiMndsS3k0YlRXbGpZWm9KYXZRSVFqVTE3OUprVG4zNDAiLCJzaWduZXIiOiJhcm46YXdzOmVsYXN0aWNsb2FkYmFsYW5jaW5nOmV1LWNlbnRyYWwtMTo0ODU2ODM0ODcxOTY6bG9hZGJhbGFuY2VyL2FwcC9maW5kLXRlc3QtYWxiLzU3YzBmMWYzZjg0YzZjMjEiLCJleHAiOjE1NzU1NDMwMzN9.eyJzdWIiOiJvYXV0aDJ8bWFpbi10ZW5hbnQtb2lkY3xzYW1scHxTaWVtZW5zfFowMDJFSk5VIiwiZ2l2ZW5fbmFtZSI6IlJhcGhhZWwiLCJmYW1pbHlfbmFtZSI6IlNjaG5haXRsIiwibmlja25hbWUiOiJSYXBoYWVsIiwibmFtZSI6IlJhcGhhZWwgU2NobmFpdGwiLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvODkzNWVlY2QzMDc2ZTAyMTQ5ODE2MTZmZjBkZTRkZjI_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZyYS5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOS0xMi0wNVQxMDo0ODozMy4wNjhaIiwiZXhwIjoxNTc1NTQzMDMzLCJpc3MiOiJodHRwczovL3NpZW1lbnMtcWEtMDAwNjkuZXUuYXV0aDAuY29tLyJ9.M39aPefXmaDGzaDd0qHcQHMhvugTVN4i4pyvGJ-7fayewU9vZdtKvSzFF9rVal8GEz7HKTr_auqMw9HemOWyag==";
        TokenValidationParameters tokenValidationParameters = new TokenValidationParameters
        {
            ValidIssuer = authorizationDomain,
            ValidateAudience = false,
            IssuerSigningKey = new ECDsaSecurityKey(ecdsa)
        };
        try
        {
            JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
            jwtSecurityTokenHandler.ValidateToken(jwt, tokenValidationParameters, out _);
            return true;
        }
        catch (SecurityTokenException)
        {
            return false;
        }

问题在于我无法测试它,因为在我的机器上(Windows 10 + .NET Core 2.1),我收到了这个错误:
System.PlatformNotSupportedException:指定的曲线'nistP256'或其参数对于此平台无效。---> Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException:参数不正确

你的代码返回了false,你尝试验证我提供的令牌了吗? - quadroid
1
ECDSA的创建指引我朝着正确的方向前进,但我使用了Bouncy Castle的PemReader来获取X/Y坐标(它们是32字节长,而不是45),这对我很有效。 - quadroid
以下坐标对我有效: Q = new ECPoint { X = bytes.Skip(27).Take(32).ToArray(),//ecPublicKeyParameters.Q.XCoord.GetEncoded(), Y = bytes.Skip(59).Take(32).ToArray()// ecPublicKeyParameters.Q.YCoord.GetEncoded(), }); - quadroid
1
我根据你的发现修改了答案。感谢你的反馈。 - Danut Radoaica
在使用 .NET Core 的 Windows 10 上,如果您的曲线参数不正确,则会收到 PlatformNotSupportedException。 - Ryan Williams

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接