【问题标题】:How to sign a JWT using RS256 with RSA private key如何使用带有 RSA 私钥的 RS256 签署 JWT
【发布时间】:2016-12-12 04:33:45
【问题描述】:

我正在使用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中列出的两个选项有什么区别,如何在两者之间进行选择?

-------------- 选项 1 ------------------- --------

RS-* 和 PS-* 系列

CLR:

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

-------------- 选项 2 ------------------- --------

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

【问题讨论】:

  • 只是为了检查...您帖子中的密钥...这是测试密钥,对吗?
  • @vcsjones 是的,我不想出示我的真钥匙。

标签: c# encryption rsa jwt sha256


【解决方案1】:

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

为了测试我使用 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<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/ 是一个很好的资源来测试你的令牌

【讨论】:

  • 谢谢@Roche,这就是答案。我浪费了好几天。谢谢!
  • 谢谢!在找到你的答案之前,我很绝望!
  • 如果我的私人令牌看起来像: -----BEGIN PRIVATE KEY----- {some string data} -----END PRIVATE KEY-----。能否请您说一下在这种情况下如何使用 RS512 生成令牌?
  • 我收到此错误:“无法读取 RSA 私钥”。这是一个演员问题:“pemReader.ReadObject() as Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair”... 所以,我改为: var privateRsaParams = pemReader.ReadObject() as Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters ;
  • 谢谢@Roche,真的很有帮助
【解决方案2】:

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

  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(csp, 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 私钥-----

【讨论】:

  • 在 .net 核心中寻找相同但无法做到的。您可以发布一些示例私人 Rsa 密钥,此代码可以从中工作吗?我有一个 -----BEGIN PUBLIC KEY----- -----END PUBLIC KEY----- 格式的 RSA 公钥/私钥。无论如何,我们可以从中工作吗?
【解决方案3】:

如果你想使用一个证书,你可以使用这个方法通过它的指纹来检索它

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;
}

【讨论】:

  • 你用过var凭证怎么声明?你测试过这个吗?
  • 你说得对,我搞砸了订单。但是你明白代码的意思......
【解决方案4】:

使用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;
}

【讨论】:

    【解决方案5】:
    【解决方案6】:

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

    在我的具体情况下,我试图为 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 代码,虽然效果不佳,但它有助于找出我做错了什么

    【讨论】:

      【解决方案7】:

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

      【讨论】:

        【解决方案8】:

        发布代码以使用服务帐户 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

        【讨论】:

          【解决方案9】:
          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);
            

          【讨论】:

            猜你喜欢
            • 2017-07-04
            • 2014-12-17
            • 2017-08-31
            • 2020-09-04
            • 2019-08-27
            • 2021-02-06
            • 2020-03-06
            • 2019-05-26
            • 2014-11-12
            相关资源
            最近更新 更多