RS256 vs HS256:有什么区别?


254

我正在使用Auth0在我的Web应用程序中处理身份验证。我正在使用ASP.NET Core v1.0.0和Angular 2 rc5,而我一般对身份验证/安全性了解不多。

ASP.NET Core Web ApiAuth0文档中, JWT算法有两个选择:RS256和HS256。这可能是一个愚蠢的问题,但:

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


我发现了一个Angular2,智威汤逊与firbase / PHP的智威汤逊在服务器端应用程序的教程freakyjolly.com/...
代码间谍

Answers:


437

两种选择都涉及身份提供者用来签署 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,您将看到一个JSON对象,该对象带有对的引用https://{account domain}/.well-known/jwks.json,其中包含帐户的一个或多个公共密钥。

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


46
注意:使用rs256时- 许多库存在(或曾经存在)安全风险,使令牌可以确定要使用哪种算法。本质上,攻击者可以使用具有hs256编码的公共rs256密钥来假装它是秘密密钥。因此,请确保您的图书馆没有这种行为!
AlexFoxGill

7
一个小的修正,“ HS256(另一方面,具有SHA-256的HMAC)是一种对称算法”-HMAC不使用对称密钥算法(这将允许您通过其定义来加密和解密签名)。它利用HMAC下面的加密哈希函数和秘密加密密钥。这意味着使用附加的密钥计算消息上的哈希(单向函数)。
Kikoz

1
例如与谷歌:去accounts.google.com/.well-known/openid-configuration和看jwks_uri; 它会将您转发到googleapis.com/oauth2/v3/certs,您可以在其中找到密钥。然后,您只需要按孩子的方式检索好密钥即可。
Denis TRUFFAUT

值得一提的是,由于HS256在方之间共享密钥,这使得该算法无法在一个access_token中支持多个受众,而RS256可以支持许多受众。对于身份验证客户端(如Auth0)而言,这很重要,如果配置为RS256,则它们仅允许使用access_token到/ userinfo端点的请求,因为它们需要此令牌来将您的api域以及auth0域支持为aud。
jezpez

94

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

对称算法

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

非对称算法

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

HS256和RS256方案

这些算法不用于加密/解密数据。而是将它们用于验证数据的来源或真实性。当Mary需要向Jhon发送开放消息并且需要验证消息肯定来自Mary时,可以使用HS256或RS256。

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

RS256使用一对密钥来完成相同的操作。签名只能使用私钥生成。并且必须使用公钥来验证签名。在这种情况下,即使杰克找到了公钥,他也无法创建带有模仿玛丽的签名的欺骗消息。


38

性能有所不同。

简单地说HS256,比RS256验证快大约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)

不要挂在实际数字上,只需要相互尊重就可以考虑它们。

[Program.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 ms的时间。
马修·马克·米勒

1
@MatthewMarkMiller请记住,尽管它们的用法不相等。它们具有不同的特征。RS256是非对称的,因此在仅共享公钥的客户端/服务器风格的通信中,它是一个更好的选择。HS256需要共享既可以签名又可以验证的密钥-仅在您信任双方或不需要一方解密任何内容时才有用。
罗布·埃文斯

7
@RobEvans是的,不要挂在这里的性能数字。选择正确的解决方案来解决您的问题。这仅是一个观察,而不是建议使用HS256而不是RS256的建议,您必须根据您的上下文做出决定。
约翰·莱德格伦

1
当协议选择对延迟的影响与增加一公里的电缆具有相同的影响时,这是值得知道的,尤其是在当今长呼叫链和紧凑的TTL的今天。
马修·马克·米勒

0

针对OAuth2的简短回答,

  • HS256用户客户端密钥以生成令牌签名,并且需要相同的密钥来在后端验证令牌。因此,您应该在后端服务器中拥有该机密的副本,以验证签名。
  • RS256使用公钥加密对令牌进行签名,签名(哈希)将使用私钥创建,并且可以使用公钥进行验证。因此,不需要将私钥或客户端机密存储在后端服务器中,而是后端服务器将从租户中的openid配置网址(https:// [tenant] /.well-known/openid -configuration)以验证令牌。access_toekn中的KID参数将用于从openid-configuration中检测正确的密钥(公共)。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.