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

时间:2016-08-05 17:17:39

标签: c# encryption rsa jwt sha256

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

var payload = new Dictionary<string, object>()
{
    { "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-----

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

  

-------------------------- OPTION 1 ------------------- -------

     

RS- *和PS- *家庭

     

CLR:

     

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

     

-------------------------- OPTION 2 ------------------- -------

     

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

9 个答案:

答案 0 :(得分:16)

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

测试我使用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/
  3. 测试代码

    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<Claim>();
            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<Claim> 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<string, object> 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/是测试令牌的绝佳资源

答案 1 :(得分:1)

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

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<X509Certificate2>().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;
}

答案 2 :(得分:1)

使用BouncyCastleJose nuget包,以下代码对我有用。

public static string CreateToken(Dictionary<string, object> payload)
{
  string jwt = string.Empty;
  RsaPrivateCrtKeyParameters keyPair;

  var cert = ConfigurationManager.AppSettings["cert"];
  /// cert begins -----BEGIN PRIVATE KEY----- and ends with -END PRIVATE KEY-----";

  using (var sr = new StringReader(cert))
  {
    PemReader pr = new PemReader(sr);
    keyPair = (RsaPrivateCrtKeyParameters)pr.ReadObject();
  }

  RSAParameters rsaParams = DotNetUtilities.ToRSAParameters(keyPair);

  using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
  {
    rsa.ImportParameters(rsaParams);

    jwt = Jose.JWT.Encode(payload, rsa, Jose.JwsAlgorithm.RS256);
  }

  return jwt;
}

答案 3 :(得分:1)

我想我会添加我的发现,因为我昨晚必须这样做。此页面中的示例确实有帮助,但它们并没有立即起作用。

在我的具体案例中,我试图为 DocuSign 生成 JWT 令牌,但由于其他一些原因我无法使用他们的 SDK,手动生成 JWT 令牌是我的用例的正确方法。

var privateKeybyteArray = Encoding.ASCII.GetBytes(@"-----BEGIN RSA PRIVATE KEY----- 
xxxxxxxxxxxxxxxxxx 
-----END RSA PRIVATE KEY-----");

var payload = new Dictionary<string, object>
{
    { "iss", "3a31fd58-xxxx-xxxx-xxxx-17639ade3c1b" },
    { "sub", "40a3a606-xxxx-xxxx-xxxx-762c6e7dadb6" },
    { "aud", "account-d.docusign.com" },
    { "iat", DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds },
    { "exp", DateTime.UtcNow.AddHours(1).Subtract(new DateTime(1970, 1, 1)).TotalSeconds }, 
    { "scope",  "signature" }
};

var rsaPrivateKey = new RSAParameters();
using (var ms = new MemoryStream(privateKeybyteArray))
{
    using (var sr = new StreamReader(ms))
    {
        var pemReader = new PemReader(sr);
        var keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
        rsaPrivateKey = DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters);
    }
}

var csprivate = new RSACryptoServiceProvider();
csprivate.ImportParameters(rsaPrivateKey);

var algorithm = new RS256Algorithm(csprivate, csprivate);
var serializer = new JsonNetSerializer();
var urlEncoder = new JwtBase64UrlEncoder();
var encoder = new JwtEncoder(algorithm, serializer, urlEncoder);

var token = encoder.Encode(payload, privateKeybyteArray);

我使用了 JWT-dotnet 包。我发现网站 jsonwebtoken.io 非常好,因为它生成了生成令牌所需的 .NET 代码,它不太好用,但它有助于找出我做错了什么

enter image description here

答案 4 :(得分:0)

答案 5 :(得分:0)

如果使用公共证书和.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);

答案 6 :(得分:0)

  1. RS256是一种签名算法而不是加密算法
  2. 使用公钥进行加密
  3. 以下是创建加密JWT的代码:

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

答案 7 :(得分:0)

这个问题的关键是使用JWT和Bouncy城​​堡库分别对令牌进行编码和签名。

  1. JWT用于编码和解码JWT令牌
  2. Bouncy Castle支持加密和解密,尤其是RS256 get it here

首先,您需要将私钥转换为RSA参数的形式。然后,您需要将RSA参数作为私钥传递给RSA算法。最后,您使用JWT库对令牌进行编码和签名。

    public string GenerateJWTToken(string rsaPrivateKey)
    {
        var rsaParams = GetRsaParameters(rsaPrivateKey);
        var encoder = GetRS256JWTEncoder(rsaParams);

        // create the payload according to your need
        var payload = new Dictionary<string, object>
        {
            { "iss", ""},
            { "sub", "" },
            // and other key-values 
        };

        // add headers. 'alg' and 'typ' key-values are added automatically.
        var header = new Dictionary<string, object>
        {
            { "{header_key}", "{your_private_key_id}" },
        };

        var token = encoder.Encode(header,payload, new byte[0]);

        return token;
    }

    private static IJwtEncoder GetRS256JWTEncoder(RSAParameters rsaParams)
    {
        var csp = new RSACryptoServiceProvider();
        csp.ImportParameters(rsaParams);

        var algorithm = new RS256Algorithm(null, csp);
        var serializer = new JsonNetSerializer();
        var urlEncoder = new JwtBase64UrlEncoder();
        var encoder = new JwtEncoder(algorithm, serializer, urlEncoder);

        return encoder;
    }

    private static RSAParameters GetRsaParameters(string rsaPrivateKey)
    {
        var byteArray = Encoding.ASCII.GetBytes(rsaPrivateKey);
        using (var ms = new MemoryStream(byteArray))
        {
            using (var sr = new StreamReader(ms))
            {
                // use Bouncy Castle to convert the private key to RSA parameters
                var pemReader = new PemReader(sr);
                var keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
                return DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters);
            }
        }
    }

ps:RSA私钥应具有以下格式:

-----开始使用RSA私钥-----

{base64格式的值}

-----结束RSA私钥-----

答案 8 :(得分:0)

使用服务帐户JSON密钥-:发布代码以为GCP OAuth令牌API调用创建RS256 JWT令牌:

using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;

namespace GCP
{
    class JWTTokenGenerationForGCPOAuthTokenAPI
    {


        public static string GenerateJWTToken()
        {
            var rsaParams = ReadAsymmetricKeyParameter();
            var encoder = GetRS256JWTEncoder(rsaParams);
            var iat = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
            var exp = DateTimeOffset.UtcNow.AddMinutes(60).ToUnixTimeSeconds();

            // create the payload according to your need 
            // iss is the Service Account Email ID
            var payload = new Dictionary<string, object>
            {
                { "iss",   "<service-account>@<project-id>.iam.gserviceaccount.com"},
                { "scope", "https://www.googleapis.com/auth/cloud-platform" },
                { "aud",   "https://oauth2.googleapis.com/token" },
                { "exp",    exp},
                { "iat",    iat}
            };
            //Final token
            var token = encoder.Encode(payload, new byte[0]);

            return token;
        }

        private static IJwtEncoder GetRS256JWTEncoder(RSAParameters rsaParams)
        {
            var csp = new RSACryptoServiceProvider();
            csp.ImportParameters(rsaParams);
            var algorithm = new RS256Algorithm(csp, csp);
            var serializer = new JsonNetSerializer();
            var urlEncoder = new JwtBase64UrlEncoder();
            var encoder = new JwtEncoder(algorithm, serializer, urlEncoder);

            return encoder;
        }



        public static RSAParameters ReadAsymmetricKeyParameter()


        {

\\ This key is fetched from the GCP Service Account JSON File. 
\\"private_key": "-----BEGIN PRIVATE KEY-----\n<long-code>-----END PRIVATE KEY-----\n",
\\ pick <long-code> from above. Replace all \n with actual new line like shown below.

                string pkey = @"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDSoGKK/Dzb8MBy
################################################################
################################################################
################################################################
################################################################
twySMqKKWnIC/zZljrvp4w==";



            RsaPrivateCrtKeyParameters rsaPrivateCrtKeyParameters1;
            var keyBytes = Convert.FromBase64String(pkey);
            var asymmetricKeyParameter = PrivateKeyFactory.CreateKey(keyBytes);


            rsaPrivateCrtKeyParameters1 = (RsaPrivateCrtKeyParameters)asymmetricKeyParameter;

            RSAParameters r = DotNetUtilities.ToRSAParameters(rsaPrivateCrtKeyParameters1);

            return r;


        }
    }

}

在.NET Framework 4.6.1中完成的代码

Nuget软件包:

Bounty Castle-安装软件包BouncyCastle-版本1.8.6.1