如何使用带有RSA私钥的RS256在C#中创建加密的JWT

我正在使用jose-jwt库,并希望使用RS256算法在C#中创建加密的JWT进行加密。 我没有密码学的经验,所以请原谅我的无知。 我在文档中看到以下示例:

var payload = new Dictionary() { { "sub", "mr.x@contoso.com" }, { "exp", 1300819380 } }; var privateKey=new X509Certificate2("my-key.p12", "password", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet).PrivateKey as RSACryptoServiceProvider; string token=Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.RS256); 

它显示了使用p12文件,但是如何使用下面表格的RSA密钥文件? 我正在查看X509Certificate2的文档,但我看不到RSA私钥的选项。 它似乎只接受PKCS7 ,我理解为公钥。

 -----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5 1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh 3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2 pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ 37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0= -----END RSA PRIVATE KEY----- 

最后, 文档中列出的两个选项有什么区别,我如何在两者之间做出选择?

– – – – – – – – – – – – – 选项1 – – – – – – – – – – – —-

RS- *和PS- *系列

CLR:

RS256,RS384,RS512和PS256,PS384,PS512签名需要相应长度的RSACryptoServiceProvider(通常是私有)密钥。 需要强制CSP使用Microsoft增强RSA和AES加密提供程序。 通常可以重新导入RSAParameters。 有关详细信息,请参见http://clrsecurity.codeplex.com/discussions/243156 。

————————–选项2 ———————- —-

CORECLR:RS256,RS384,RS512签名需要相应长度的RSA(通常是私有)密钥。

我知道这篇文章很老了,但我花了很长时间才想到这一点,所以我想我会分享。

要测试我使用OpenSSL创建了RSA密钥:

 openssl genrsa -out privateKey.pem 512 openssl rsa -in privateKey.pem -pubout -out publicKey.pem 

您将需要以下2个nuget包:

  1. https://github.com/dvsekhvalnov/jose-jwt
  2. http://www.bouncycastle.org/csharp/

测试代码

 public static void Test() { string publicKey = File.ReadAllText(@"W:\Dev\Temp\rsa_keys\publicKey.pem"); string privateKey = File.ReadAllText(@"W:\Dev\Temp\rsa_keys\privateKey.pem"); var claims = new List(); claims.Add(new Claim("claim1", "value1")); claims.Add(new Claim("claim2", "value2")); claims.Add(new Claim("claim3", "value3")); var token = CreateToken(claims, privateKey); var payload = DecodeToken(token, publicKey); } 

创建令牌

  public static string CreateToken(List claims, string privateRsaKey) { RSAParameters rsaParams; using (var tr = new StringReader(privateRsaKey)) { var pemReader = new PemReader(tr); var keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair; if (keyPair == null) { throw new Exception("Could not read RSA private key"); } var privateRsaParams = keyPair.Private as RsaPrivateCrtKeyParameters; rsaParams = DotNetUtilities.ToRSAParameters(privateRsaParams); } using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.ImportParameters(rsaParams); Dictionary payload = claims.ToDictionary(k => k.Type, v => (object)v.Value); return Jose.JWT.Encode(payload, rsa, Jose.JwsAlgorithm.RS256); } } 

解码令牌

  public static string DecodeToken(string token, string publicRsaKey) { RSAParameters rsaParams; using (var tr = new StringReader(publicRsaKey)) { var pemReader = new PemReader(tr); var publicKeyParams = pemReader.ReadObject() as RsaKeyParameters; if (publicKeyParams == null) { throw new Exception("Could not read RSA public key"); } rsaParams = DotNetUtilities.ToRSAParameters(publicKeyParams); } using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.ImportParameters(rsaParams); // This will throw if the signature is invalid return Jose.JWT.Decode(token, rsa, Jose.JwsAlgorithm.RS256); } } 

我发现https://jwt.io/是一个很好的资源来测试你的令牌

如果要使用证书,可以使用此方法通过指纹检索它

 private X509Certificate2 GetByThumbprint(string Thumbprint) { var localStore = new X509Store(StoreName.My, StoreLocation.LocalMachine); localStore.Open(OpenFlags.ReadOnly); return localStore.Certificates//.Find(X509FindType.FindByKeyUsage, X509KeyUsageFlags.DigitalSignature, false) .Find(X509FindType.FindByThumbprint, Thumbprint, false) .OfType().First(); } 

然后:

 private JwtSecurityToken GenerateJWT() { var securityKey = new Microsoft.IdentityModel.Tokens.X509SecurityKey(GetByThumbprint("YOUR-CERT-THUMBPRINT-HERE")); var credentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, "RS256"); var JWTHeader = new JwtHeader(credentials); var payload = new JwtPayload { { "iss", "Issuer-here"}, { "exp", (Int32)(DateTime.UtcNow.AddHours(1).Subtract(new DateTime(1970, 1, 1))).TotalSeconds}, { "iat", (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds} }; var token = new JwtSecurityToken(JWTHeader, payload); return token; } 

如果使用公共证书和.NET 4.6,则可以使用以下方法进行解码:

 string token = "eyJhbGciOiJSUzI1NiIsInR...."; string certificate = "MIICnzCCAYcCBgFd2yEPx...."; var publicKey = new X509Certificate2(Convert.FromBase64String(certificate )).GetRSAPublicKey(); string decoded = JWT.Decode(token, publicKey, JwsAlgorithm.RS256); 
  1. RS256是一种签名算法,而不是加密算法
  2. 使用公钥进行加密
  3. 以下是创建加密JWT的代码:

     var cert = new X509Certificate2(".\\key.cer"); var rsa = (RSACryptoServiceProvider) cert.PublicKey.Key; var payload = new Dictionary() { {"sub", "mr.x@contoso.com"}, {"exp", 1300819380} }; var encryptedToken = JWT.Encode( payload, rsa, JweAlgorithm.RSA_OAEP, JweEncryption.A256CBC_HS512, null);