ChatGPT解决这个技术问题 Extra ChatGPT

RS256 与 HS256:有什么区别?

我正在使用 Auth0 处理我的网络应用程序中的身份验证。我正在使用 ASP.NET Core v1.0.0 和 Angular 2 rc5,但我一般对身份验证/安全性知之甚少。

Auth0 docs for ASP.NET Core Web Api中,JWT 算法有 RS256 和 HS256 两种选择。这可能是一个愚蠢的问题,但是:

RS256和HS256有什么区别?有哪些用例(如果适用)?

另请参阅有关安全堆栈交换的答案Recommended algorithms for JWT

u
user229044

这两种选择都指身份提供者用于签署 JWT 的算法。签名是一种加密操作,它生成一个“签名”(JWT 的一部分),令牌的接收者可以对其进行验证,以确保令牌没有被篡改。

RS256(带有 SHA-256 的 RSA 签名)是一种非对称算法,它使用公钥/私钥对:身份提供者拥有用于生成签名的私钥(秘密),而 JWT 的消费者获得公钥验证签名。由于与私钥相反,公钥不需要保持安全,因此大多数身份提供者都可以让消费者轻松获取和使用(通常通过元数据 URL)。

另一方面,HS256(带有 SHA-256 的 HMAC)涉及散列函数和一个(秘密)密钥的组合,该密钥在两方之间共享,用于生成用作签名的散列。由于生成签名和验证签名都使用相同的密钥,因此必须注意确保密钥不被泄露。

如果您将开发使用 JWT 的应用程序,则可以安全地使用 HS256,因为您可以控制谁使用密钥。另一方面,如果您无法控制客户端,或者您无法保护密钥,则 RS256 将更适合,因为消费者只需要知道公共(共享)密钥。

由于公钥通常可从元数据端点获得,因此可以对客户端进行编程以自动检索公钥。如果是这种情况(与 .Net Core 库一样),您在配置方面要做的工作就会减少(这些库将从服务器获取公钥)。另一方面,对称密钥需要在带外交换(确保安全的通信通道),并在签名密钥翻转时手动更新。

Auth0 为 OIDC、SAML 和 WS-Fed 协议提供元数据端点,可以在其中检索公钥。您可以在客户端的“高级设置”下看到这些端点。

例如,OIDC 元数据端点采用 https://{account domain}/.well-known/openid-configuration 的形式。如果浏览到该 URL,您将看到一个引用 https://{account domain}/.well-known/jwks.json 的 JSON 对象,其中包含帐户的公钥(或多个密钥),表示为 JSON Web Key Set

如果您查看 RS256 示例,您会发现您不需要在任何地方配置公钥:它由框架自动检索。


NB 使用 rs256 时 - 许多库中有(或曾经)有一个 security risk,它允许令牌确定要使用的算法。本质上,攻击者可以使用带有 hs256 编码的公共 rs256 密钥来假装它是密钥。因此,请确保您的图书馆没有这种行为!
另一方面,一个小的更正“HS256(HMAC 与 SHA-256)是一种对称算法” - HMAC 不使用对称密钥算法(这将允许您根据其定义加密和解密签名)。它在 HMAC 下使用加密散列函数和秘密加密密钥。这意味着在带有附加密钥的消息上计算散列(单向函数)。
Google 示例:转到 accounts.google.com/.well-known/openid-configuration 并查看 jwks_uri ;它会将您转发到 googleapis.com/oauth2/v3/certs,您可以在其中找到密钥。然后你只需要通过它的孩子取回好钥匙。
值得注意的是,由于 HS256 在两方之间共享一个密钥,这使得该算法无法在一个 access_token 中支持多个受众,而 RS256 可以支持多个受众。这对于像 Auth0 这样的身份客户端很重要,如果配置是 RS256,它们只允许使用 access_token 向 /userinfo 端点发出请求,因为他们需要这个令牌来支持您的 api 域作为 aud,以及他们的 auth0 域。
还值得注意的是,RSA 签名算法是vulnerable to quantum cryptography。因此,拥有足够大量子计算机的攻击者可以伪造签名。
C
Chris Redford

在密码学中,使用了两种类型的算法:

对称算法

单个密钥用于加密数据。当使用密钥加密时,可以使用相同的密钥对数据进行解密。例如,如果 Mary 使用密钥“my-secret”加密消息并将其发送给 John,他将能够使用相同的密钥“my-secret”正确解密消息。

非对称算法

两个密钥用于加密和解密消息。虽然一个密钥(公共)用于加密消息,但另一个密钥(私有)只能用于解密它。因此,John 可以生成公钥和私钥,然后只将公钥发送给 Mary 以加密她的消息。该消息只能使用私钥解密。

HS256 和 RS256 场景

这些算法不用于加密/解密数据。相反,它们用于验证数据的来源或真实性。当 Mary 需要向 Jhon 发送一条开放消息并且他需要验证该消息确实来自 Mary 时,可以使用 HS256 或 RS256。

HS256 可以使用单个密钥为给定的数据样本创建签名。当消息与签名一起传输时,接收方可以使用相同的密钥来验证签名是否与消息匹配。

RS256 使用一对钥匙来做同样的事情。只能使用私钥生成签名。并且必须使用公钥来验证签名。在这种情况下,即使 Jack 找到了公钥,他也无法创建带有签名的欺骗消息来冒充 Mary。


我认为您在描述非对称密钥时犯了一个错误。公钥用于解密,私钥用于加密。
任何人都可以访问公钥并用于加密。但是私钥由服务器保存以解密由公钥加密的内容。私钥永远不会被泄露。
P
Pang

性能上有区别。

简单地说,HS256RS256 快约 1 个数量级用于验证,但比 RS256 快约 2 个数量级用于发布(签名)。

 640,251  91,464.3 ops/s
  86,123  12,303.3 ops/s (RS256 verify)
   7,046   1,006.5 ops/s (RS256 sign)

不要挂在实际数字上,只要考虑到他们彼此的尊重。

[程序.cs]

class Program
{
    static void Main(string[] args)
    {
        foreach (var duration in new[] { 1, 3, 5, 7 })
        {
            var t = TimeSpan.FromSeconds(duration);

            byte[] publicKey, privateKey;

            using (var rsa = new RSACryptoServiceProvider())
            {
                publicKey = rsa.ExportCspBlob(false);
                privateKey = rsa.ExportCspBlob(true);
            }

            byte[] key = new byte[64];

            using (var rng = new RNGCryptoServiceProvider())
            {
                rng.GetBytes(key);
            }

            var s1 = new Stopwatch();
            var n1 = 0;

            using (var hs256 = new HMACSHA256(key))
            {
                while (s1.Elapsed < t)
                {
                    s1.Start();
                    var hash = hs256.ComputeHash(privateKey);
                    s1.Stop();
                    n1++;
                }
            }

            byte[] sign;

            using (var rsa = new RSACryptoServiceProvider())
            {
                rsa.ImportCspBlob(privateKey);

                sign = rsa.SignData(privateKey, "SHA256");
            }

            var s2 = new Stopwatch();
            var n2 = 0;

            using (var rsa = new RSACryptoServiceProvider())
            {
                rsa.ImportCspBlob(publicKey);

                while (s2.Elapsed < t)
                {
                    s2.Start();
                    var success = rsa.VerifyData(privateKey, "SHA256", sign);
                    s2.Stop();
                    n2++;
                }
            }

            var s3 = new Stopwatch();
            var n3 = 0;

            using (var rsa = new RSACryptoServiceProvider())
            {
                rsa.ImportCspBlob(privateKey);

                while (s3.Elapsed < t)
                {
                    s3.Start();
                    rsa.SignData(privateKey, "SHA256");
                    s3.Stop();
                    n3++;
                }
            }

            Console.WriteLine($"{s1.Elapsed.TotalSeconds:0} {n1,7:N0} {n1 / s1.Elapsed.TotalSeconds,9:N1} ops/s");
            Console.WriteLine($"{s2.Elapsed.TotalSeconds:0} {n2,7:N0} {n2 / s2.Elapsed.TotalSeconds,9:N1} ops/s");
            Console.WriteLine($"{s3.Elapsed.TotalSeconds:0} {n3,7:N0} {n3 / s3.Elapsed.TotalSeconds,9:N1} ops/s");

            Console.WriteLine($"RS256 is {(n1 / s1.Elapsed.TotalSeconds) / (n2 / s2.Elapsed.TotalSeconds),9:N1}x slower (verify)");
            Console.WriteLine($"RS256 is {(n1 / s1.Elapsed.TotalSeconds) / (n3 / s3.Elapsed.TotalSeconds),9:N1}x slower (issue)");

            // RS256 is about 7.5x slower, but it can still do over 10K ops per sec.
        }
    }
}

这些是重要的数字。谢谢你。我倾向于将加密视为或多或少透明的吞吐量,但您的研究表明使用 R256 对机器间通信进行签名每跳增加 1 毫秒。
@MatthewMarkMiller 请记住,尽管它们在使用上并不相同。他们有不同的特点。 RS256 是非对称的,因此在您只共享公钥的客户端/服务器式通信中,它是一个更好的选择。 HS256 需要共享既可以签名又可以验证的密钥 - 仅在您信任双方或不需要其中一方解密任何内容时才有用。
@RobEvans 是的,不要挂断这里的性能数字。为您的问题选择正确的解决方案。这只是一个观察结果,而不是推荐 HS256 而不是 RS256,您必须根据您的上下文做出决定。
当协议选择对延迟的影响与额外一公里的电缆相同时,这是值得知道的,尤其是在调用链长且 TTL 紧张的今天。
H
Hiran

简短的回答,特定于 OAuth2,

生成令牌签名的 HS256 用户客户端密码,并且需要相同的密码才能在后端验证令牌。因此,您应该在后端服务器中拥有该密钥的副本以验证签名。

RS256 使用公钥加密对令牌进行签名。Signature(hash) 将使用私钥创建,并且可以使用公钥进行验证。因此,不需要将私钥或客户端密钥存储在后端服务器中,但后端服务器将从您租户中的 openid 配置 url 获取公钥(https://[tenant]/.well-known/openid -configuration) 来验证令牌。 access_toekn 中的 KID 参数将用于从 openid-configuration 中检测正确的密钥(公共)。


您仍然必须存储私钥,以生成新令牌
@DollarAkshay 私钥由身份验证服务器使用(它自己的私钥)。没有人会存储其他人的私钥
是的,这就是我的意思,生成令牌的服务器仍然需要存储私钥。但是您说不必将私钥存储在后端服务器上,这是错误的。
@DollarAkshay“您不必将私钥存储在后端服务器中”是的,这是正确的。如果您仔细阅读该问题,它与 Auth0 相关,您不必将 Auth0 的私钥存储在后端资源服务器中。但在您看来,曾经生成令牌的人需要他的私钥(在这种情况下是 Auth0)
@nickjag 公钥不应该在令牌内。因为令牌应该根据已知的公钥进行验证,而验证者应该知道该公钥。否则,中间的某个人会更改令牌并将他的公钥附加到令牌上。您可以为同一个令牌使用不同的公钥/私钥集,然后您需要替换新密钥的令牌签名。