ES 512 jwt 令牌验证

Posted

技术标签:

【中文标题】ES 512 jwt 令牌验证【英文标题】:ES 512 jwt token verification 【发布时间】:2020-06-24 10:44:23 【问题描述】:

我正在尝试使用字符串格式的公钥验证 ES512 jwt 令牌:

我正在尝试模仿为 ES256 编写的示例 是这样的:

    // The code for ES256
    public static void VerifyES512Jwt(string token,string publicKey)
    
        byte[] publicKeyBytes = Convert.FromBase64String(publicKey);

        string[] parts = token.Split('.');

        string header = parts[0];
        string payload = parts[1];
        string signature = parts[2];

        var keyType = new byte[]  0x45, 0x43, 0x53, 0x31 ;
        var keyLength = new byte[]  0x20, 0x00, 0x00, 0x00 ;
        var key = keyType.Concat(keyLength).Concat(publicKeyBytes.Skip(publicKeyBytes.Length - 64)).ToArray(); 
        CngKey cngKey = CngKey.Import(key, CngKeyBlobFormat.EccPublicBlob);
        
        // the purpose is to get ECDsaCng and verify the payload data
         ECDsaCng eCDsaCng = new ECDsaCng(cngKey);
        
        bool result = eCDsaCng.VerifyData(payload, signatureBytes); 
    

我正在尝试将此代码用于 ES512,但无法获取密钥

        var keyType = new byte[]  0x45, 0x43, 0x53, 0x31 ;
        var keyLength = new byte[]  0x20, 0x00, 0x00, 0x00 ;
        var key = keyType.Concat(keyLength).Concat(publicKeyBytes.Skip(publicKeyBytes.Length - 64)).ToArray(); 

与上述一起使用时,在获取密钥时会出错:

CngKey cngKey = CngKey.Import(key, CngKeyBlobFormat.EccPublicBlob);

我使用的公钥和令牌如下: 公钥:

MIGbMBAGByqGSM49AgEGBSubBBAAjA4GGAAQBgc4HZz+/fBbC7lmEww0AO3NK9wVZ PDZ0VEnsaUFLEYpTzb90nITtJUcPUbvOsdZIZ1Q8fnbquAYgxXL5UgHMoywAib47 6MkyyYgPk0BXZq3mq4zImTRNuaU9slj9TVJ3ScT3L1bXwVuPJDzpr5GOFpaj+WwM Al8G7CqwoJOsW7Kddns=

代币:

eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1NjUwOTk2ODgsImV4cCI6MTU5ODE4OTg4NSwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDozNTg4Iiwic3ViIjoiaHR0cDovL2xvY2FsaG9zdDo1MDM3NiIsImZpcnN0bmFtZSI6IkFydmluZCIsImxhc3RuYW1lIjoiS3VtYXIiLCJFbWFpbCI6ImFydmluZC5rdW1hckBzdHJlYW1hbWcuY29tIiwiSWQiOiIxMDEifQ.AVwAJeY44lKrnywnDs7CdUOu3gli2cGafSJ6iP3zT7lkZpd2QnL0k54aVmPVxAGuN5dDnzbYmMTdRl5u2QE92ccOAHrcf5yA2gsvhhAGuDAAeh6Io4VU7v5TOTvwWGRb-ubgdjUvagA_HSJOyeXvFR16_M_MzGfDnXfg02sj4y9VFjDr P>

【问题讨论】:

【参考方案1】:

ES512 将 ECDSA 与 P-521 和 SHA-512 结合使用。

P-521 的公钥(secp521r1,第 2.6.1 章)的大小为未压缩形式的 2 x 66 = 132 字节(前字节 0x04)。 MS 将 P-521 的公钥指定为 0x35534345

因此,密钥必须按如下方式生成:

var keyType = new byte[]  0x45, 0x43, 0x53, 0x35 ;
var keyLength = new byte[]  0x42, 0x00, 0x00, 0x00 ;
var key = keyType.Concat(keyLength).Concat(publicKeyBytes.Skip(publicKeyBytes.Length - 132)).ToArray();

要签名的数据是标头和有效负载(均为 Base64url 编码),包括分隔符(.)。签名也是 Base64url 编码的。因此必须按如下方式进行验证:

byte[] headerPayloadBytes = Encoding.UTF8.GetBytes(header + "." + payload);
byte[] signatureBytes = Base64UrlDecode(signature);
bool verified = eCDsaCng.VerifyData(headerPayloadBytes, signatureBytes, HashAlgorithmName.SHA512);

Base64UrlDecode 完成 Base64url 解码,见 e.g. here 了解实现细节。

【讨论】:

非常感谢。生成密钥效果很好,但是 eCDsaCng.VerifyData(headerPayloadBytes, signatureBytes);没有工作,否则当我使用 var validationParameters = new TokenValidationParameters() ValidateAudience = false, ValidateIssuer = false, IssuerSigningKey = new ECDsaSecurityKey(pubKey) 时验证令牌; SecurityToken 代币; var claim = securityTokenHandler.ValidateToken(token, validationParameters, out stoken); @ArvindKrmar - 我无法重现您的问题。在我的机器上本地运行良好,也在dotnetfiddle 上运行良好。这两个代码在功能上似乎或多或少与我相同。我的代码(或者更确切地说是您在问题中发布的代码)更直接地验证,而您的答案中发布的代码将验证过程封装在一些 JWT 类中。 还是非常感谢。你的建议已经解决了我的问题,这是一个很大的帮助。我会再次尝试执行上面的代码 你建议的代码工作方式有点不同,我添加了这一行 eCDsaCng.HashAlgorithm = CngAlgorithm.Sha512;布尔验证 = eCDsaCng.VerifyData(headerPayloadBytes, signatureBytes, HashAlgorithmName.SHA512);对我来说 eCDsaCng.VerifyData();没有第三个参数。 HashAlgorithmName.SHA512 当我在 eCDsaCng 中添加算法时,它可以工作这个变化可能是因为我在 Dot Net V 4.5 上运行它,这可能与你的不同。 是的,没错。在 .NET Framework 3.5 到 4.6 中,必须应用您使用的 VerifyData 的 overload。我使用的overload另外从 .NET Framework 4.6.1(和 .NET Core 1)开始存在。问题中没有指定 .NET 版本,所以我假设当前版本(.NET Framework 4.8 或 .NET Core 3.1)。【参考方案2】:

我按照@Topaco 的建议生成了密钥,并将其与身份模型的 JwtSecurityTokenHandler 一起使用,并且可以正常工作。 由于某些原因,我试图使用的功能不起作用: 工作代码如下:

public static bool ValidateEcdsa512JwtToken(string token, string publicKey)

    ECDsa pubKey = LoadPublicKey(publicKey);

    var securityTokenHandler = new JwtSecurityTokenHandler();
    var validationParameters = new TokenValidationParameters()
    
        ValidateAudience = false,
        ValidateIssuer = false,
        IssuerSigningKey = new ECDsaSecurityKey(pubKey)
    ;

    SecurityToken stoken;
    var claims = securityTokenHandler.ValidateToken(token, validationParameters, out stoken);
    var verified=claims.Identity.IsAuthenticated;

    return verified;



private static ECDsa LoadPublicKey(string publicKey)

     byte[] publicKeyBytes = Convert.FromBase64String(publicKey);
     var keyType = new byte[]  0x45, 0x43, 0x53, 0x35 ;
     var keyLength = new byte[]  0x42, 0x00, 0x00, 0x00 ;
     var key = keyType.Concat(keyLength).Concat(publicKeyBytes.Skip(publicKeyBytes.Length - 132)).ToArray();

     CngKey cngKey = CngKey.Import(key, CngKeyBlobFormat.EccPublicBlob);
     ECDsaCng eCDsaCng = new ECDsaCng(cngKey);
     eCDsaCng.HashAlgorithm = CngAlgorithm.ECDsaP521;
     return eCDsaCng;

【讨论】:

以上是关于ES 512 jwt 令牌验证的主要内容,如果未能解决你的问题,请参考以下文章

如何在 .NET Framework 中读取 ES512 私钥

有啥方法可以创建密钥大小为 512 的 JWT 令牌?并更改 AsymmetricSignatureProvider 的默认最小尺寸要求

验证 JWT 令牌签名

JWT 令牌签名验证 javascript

验证时 JWT 令牌始终无效

如何基于使用 Oauth2 协议的身份验证改进 JWT 访问令牌和刷新令牌?