在C#中使用RS256(非对称)验证JWT [英] Verify JWT with RS256 (asymmetric) in C#
问题描述
我有一些这样的代码,我相信它失败了,因为它使用的是非对称RS256,但是具有"SymmetricSecurityKey()".令牌是从 https://jwt.io/
I have some code like this which I believe is failing because it's using an Asymmetric RS256 but has "SymmetricSecurityKey()". The tokens were hand generated from https://jwt.io/
- 如何将其转换为使用我的非对称公钥?
- 此外,我是C#的新手,我想针对dotnet标准,所以我也想知道我使用的库是否错误? (我取决于预览版本)
λ cat Program.cs
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using System.Linq;
using Microsoft.IdentityModel.Tokens;
using System.Security.Cryptography;
namespace jwttest
{
class Program
{
static void Main(string[] args)
{
string jwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.POstGetfAytaZS82wHcjoTyoqhMyxXiWdR7Nn7A29DNSl0EiXLdwJ6xC6AfgZWF1bOsS_TuYI3OG85AmiExREkrS6tDfTQ2B3WXlrr-wp5AokiRbz3_oB4OxG-W9KcEEbDRcZc0nH3L7LzYptiy1PtAylQGxHTWZXtGz4ht0bAecBgmpdgXMguEIcoqPJ1n3pIWk_dUZegpqx0Lka21H6XxUTxiy8OcaarA8zdnPUnV6AmNP3ecFawIFYdvJB_cm-GvpCSbr8G8y_Mllj8f4x9nBH8pQux89_6gUY618iYv7tuPWBFfEbLxtF2pZS6YC1aSfLQxeNe8djT9YjpvRZA";
var pubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQAB";
var rawKey = Encoding.ASCII.GetBytes(pubKey);
var tokenHandler = new JwtSecurityTokenHandler();
// var rsa = ?
tokenHandler.ValidateToken(jwt, new TokenValidationParameters {
IssuerSigningKey = new SymmetricSecurityKey(rawKey)
},
out SecurityToken validatedToken);
}
}
}
C:\src\jwttest (cgt-test-5 -> origin)
λ dotnet run
[2020-08-18T23:41:05.7108585-07:00 Info] raw=System.Byte[] [392]
Unhandled exception. Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10503: Signature validation failed. Keys tried: 'System.Text.StringBuilder'.
Exceptions caught:
'System.Text.StringBuilder'.
token: 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken'.
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at jwttest.Program.Main(String[] args) in C:\src\jwttest\Program.cs:line 22
λ cat jwttest.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<!-- Using preview release because it only depends on dotnet standard. Prior versions need framework. -->
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.7.2-preview-10803222715" />
</ItemGroup>
</Project>
λ cat jwt.json
{
"alg": "RS256",
"typ": "JWT"
}
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022
}
推荐答案
-
关于您的第一个问题:
根据发布的堆栈跟踪,您似乎正在使用.NET Core 3.1.这使您可以轻松导入X.509/SPKI公用密钥,如下所示:Regarding your 1st question:
According to your posted stack trace, you seem to be using .NET Core 3.1. This allows you to easily import your public X.509/SPKI key as follows:var pubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQAB"; RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(pubKey), out _); // import the public X.509/SPKI DER encoded key
编辑开始: 在早期版本的.NET Core(3.0之前)或.NET Framework中,.NET标准2.1 是必需的.
Edit start: In earlier versions of .NET Core (before 3.0) or in the .NET Framework
ImportSubjectPublicKeyInfo()
is not available, so at least .NET Standard 2.1 is required.对于早期版本,例如在.NET Standard 2.0中,一种可能性是使用 BouncyCastle ,更确切地说是其
Org.BouncyCastle.OpenSsl.PemReader
类,它允许导入X509/SPKI格式的公钥(并且与您无关,也可以PKCS#1格式).在此答案中,您将找到有关如何使用PemReader
的示例.顾名思义,PemReader
按照ImportSubjectPublicKeyInfo()
<的要求处理PEM编码,即转换为DER编码(即,删除标头,页脚和换行符,以及对其余部分进行Base64解码) em>一定不能做.另请注意,PemReader
预计在标头(-----BEGIN PUBLIC KEY-----\n
)之后立即至少有一个换行符,而在页脚(\n-----END PUBLIC KEY-----
)之前立即有第二个换行符,对于每64个字符,Base64编码主体中的换行符对于PemReader
.For earlier versions, e.g. .NET Standard 2.0, one possibility is to use BouncyCastle, more precisely its
Org.BouncyCastle.OpenSsl.PemReader
class, which allows the import of public keys in X509/SPKI format (and, irrelevant for you, also in PKCS#1 format). In this answer you will find an example of how to usePemReader
.PemReader
processes, as the name suggests, a PEM encoding, i.e. the conversion to a DER encoding (i.e. the removal of header, footer and line breaks, as well as the Base64 decoding of the remainder) as required byImportSubjectPublicKeyInfo()
must not be done. Also note thatPemReader
expects at least one line break immediately after the header (-----BEGIN PUBLIC KEY-----\n
) and a second one immediately before the footer (\n-----END PUBLIC KEY-----
), the line breaks in the Base64 encoded body after every 64 characters are optional forPemReader
.另一种可能性是软件包 opensslkey 提供方法
opensslkey.DecodeX509PublicKey()
,该方法可以处理类似于ImportSubjectPublicKeyInfo
的DER编码中的X509/SPKI键. 编辑结束Another possibility is the package opensslkey providing the method
opensslkey.DecodeX509PublicKey()
, which can process an X509/SPKI key in DER encoding analogous toImportSubjectPublicKeyInfo
. Edit end关于第二个问题:
有几个 .NET标准版本,例如.NET Core 3.0实现了.NET Standard 2.1.软件包 System.IdentityModel.Tokens.JwtRegarding your 2nd question:
There are several .NET standard versions, e.g. .NET Core 3.0 implements .NET Standard 2.1. The package System.IdentityModel.Tokens.Jwt 6.7.2-preview-10803222715 you are using requires .NET Standard 2.0.System.IdentityModel.Tokens.Jwt
是一个支持JSON Web令牌(JWT)的创建和验证的软件包.对于已发布的令牌,可以按以下方式执行验证:System.IdentityModel.Tokens.Jwt
is a package that supports the creation and validation of JSON Web Tokens (JWT). In the case of the posted token, the validation could be implemented as follows:string jwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.POstGetfAytaZS82wHcjoTyoqhMyxXiWdR7Nn7A29DNSl0EiXLdwJ6xC6AfgZWF1bOsS_TuYI3OG85AmiExREkrS6tDfTQ2B3WXlrr-wp5AokiRbz3_oB4OxG-W9KcEEbDRcZc0nH3L7LzYptiy1PtAylQGxHTWZXtGz4ht0bAecBgmpdgXMguEIcoqPJ1n3pIWk_dUZegpqx0Lka21H6XxUTxiy8OcaarA8zdnPUnV6AmNP3ecFawIFYdvJB_cm-GvpCSbr8G8y_Mllj8f4x9nBH8pQux89_6gUY618iYv7tuPWBFfEbLxtF2pZS6YC1aSfLQxeNe8djT9YjpvRZA"; var tokenHandler = new JwtSecurityTokenHandler(); bool verified = false; try { tokenHandler.ValidateToken(jwt, new TokenValidationParameters { ValidateAudience = false, ValidateLifetime = false, ValidateIssuer = false, IssuerSigningKey = new RsaSecurityKey(rsa) }, out _); verified = true; } catch { verified = false; } Console.WriteLine("Verified: " + verified);
可以通过验证参数(即,通过声明 iss , aud 和 exp (例如,可以在 https:/上进行验证/jwt.io/),在我的示例中,它们不包含在验证中.
The validation can be controlled via the validation parameters, i.e. via the 2nd parameter of
ValidateToken()
. Since the posted token does not contain the claims iss, aud and exp (this can be verified e.g. on https://jwt.io/), they are excluded from the validation in my example.在教程 在ASP.NET Core中创建和验证JWT令牌 ,您会找到更详细的说明,尤其是在验证令牌一章中.
ValidateToken()
本质上封装了JWT签名的验证过程. JWT 是一种数据结构,它由三部分组成:标头,有效负载和签名,个体部分是通过Base64url编码的,并由一个点彼此分隔.
签名是使用各种算法创建的,例如在您的情况下 RS256 ,这意味着数据(Base64url编码的标头和包括分隔符的有效负载)使用带有PKCS#1 v1.5填充和摘要SHA256的RSA算法进行签名.
令牌的验证与签名的验证相对应,这也可以单独完成使用加密API(即无需 System.IdentityModel.Tokens.Jwt 参与),就像在在@zaitsman的评论中接受链接问题的答案.ValidateToken()
essentially encapsulates the verification process of the JWT signature. A JWT is a data structure that consists of three parts: header, payload and signature, the individual parts being Base64url encoded and separated from each other by a dot.
The signature is created using various algorithms, e.g. in your case RS256, which means that the data (Base64url encoded header and payload including separator) is signed using the algorithm RSA with PKCS#1 v1.5 padding and digest SHA256.
The verification of a token corresponds to the verification of the signature, which can also be done solely with cryptographic APIs (i.e. without participation of System.IdentityModel.Tokens.Jwt), as it is done in the accepted answer of the linked question in the comment of @zaitsman.这篇关于在C#中使用RS256(非对称)验证JWT的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!