当前位置: 首页 > 科技观察

JWT:我应该使用哪种签名算法?_0

时间:2023-03-13 06:56:26 科技观察

本文转载自微信公众号“DotNET技术圈”,作者ScottBrady。转载本文请联系DotNET技术圈公众号。JWT:我应该使用哪种签名算法?JSONWebToken(JWT)可以使用许多不同的算法进行签名:RS256、PS512、ES384、HS1;当被问到他们想使用哪一个时,您可以看到为什么有些开发人员会挠头。根据我的经验,许多主流身份提供商历来只提供RS256或至少默认使用它。然而,由于开放银行等举措,这些身份提供商现在正在扩大他们的支持以涵盖更多签名算法,这意味着您需要开始了解要使用哪些算法。我不是密码学家,但通过使用OpenIDConnect和FIDO2,我获得了从业者对各种签名算法的理解以及密码学社区对每种算法的总体感觉。在本文中,我将为您提供一些知识,以便您了解每个“alg”值的含义并选择可用的最佳签名算法。TL;DR:EdDSA>ECDSA或RSASSA-PSS>RSASSA-PKCS1-v1_5并知道会发生什么。算法(alg)值在我们查看每个签名算法系列之前,让我们首先阐明“alg”值(例如RS256)的含义。这些是JSONWeb算法(JWA),它们是JavaScript对象签名和加密(JOSE)系列的一部分。您将在JWT标头中看到一个“alg”值,告诉您JWT是如何签名的,在JSONWebKeys(JWK)中,告诉您密钥用于什么算法。作为一般的经验法则,“alg”值可以分解为:RS256签名算法哈希算法签名算法系列:在这种情况下,RS表示RSASSA-PKCS1-v1_5。签名算法使用的哈希算法。在这种情况下,256表示SHA-256。大多数签名算法都有SHA-256、SHA-384和SHA-512的变体。在某些情况下,您甚至可以使用诸如“RS1”之类的东西,它使用SHA-1?并且是FIDO2一致性所必需的。这些算法通常在RFC7518[1]中定义,但您可以在JOSEIANA注册表[2]中找到受支持算法的完整列表。我应该使用哪种哈希算法?SHA-256、SHA-384和SHA-512都是同一系列哈希算法的变体:SHA-2。根据经验,算法中的数字是指它将生成的哈希值的大小。例如,SHA-256将产生256位散列,而SHA-512将产生512位散列。每个都为您提供其输出大小50%的安全级别,因此SHA-256将为您提供128位的安全性,而SHA-512将为您提供256位的安全性。这意味着攻击者必须在开始寻找冲突之前生成2^128哈希值,这要归功于生日绑定[3]。这就是我们使用至少128位安全性的原因。很快您就不需要比SHA-256更好的算法了。只是不要使用SHA-1。验证:了解你的算法每个验证JWT签名的应用程序都应该事先知道预期的算法以及确切使用哪个密钥。您可以通过将每个公钥分配给一个算法来做到这一点(例如,这个密钥用于RS384,这个密钥用于ES256)。当单个算法有多个密钥时,您可以使用kidJWT标头中的密钥ID()来了解使用哪一个。基本上,你要确保JWT中的kid和alg值是你所期望的。如果他们不匹配,则有人是坏人。{"typ":"JWT","kid":"123",//isthiskey..."alg":"RS256"//...allowedtobeusedforthisalgorithm?}OpenIDConnect等协议使用发现文档和TLS保护端点上可用的JSONWeb密钥集(JWKS)有助于实现这一点。现在,您不应该只信任JWT标头中的“alg”值,也不应该接受具有“无”算法的JWT或标头中嵌入了公钥的JWT。RSASSA-PKCS1-v1_5(例如RS256)RS256=RSASSA-PKCS1-v1_5使用SHA-256虽然RSAES-PKCS1-v1_5不再安全用于加密,但RSASSA-PKCS1-v1_5仍然适用于数字签名。正如我之前提到的,根据我的经验,RS256历来是大多数JWT实现的默认设置,许多SaaS身份提供商只提供这种签名算法。很难找到不支持使用RS256签名的JWT的系统。使用RSASSA-PKCS1-v1_5签名的JWT具有确定性签名,这意味着相同的JWT标头和有效负载将始终生成相同的签名。RSASSA-PKCS1-v1_5已经存在很长时间了,但是现在,您通常应该更喜欢RSASSA-PSS(具有概率签名的RSA)。并不是说RSASSA-PKCS1-v1_5坏了,而是RSASSA-PSS只是具有其他人没有的理想功能。事实上,RFC8017现在将RSASSA-PSS视为使用RSA进行签名时的一项要求:尽管没有针对RSASSA-PKCS1-v1_5的已知攻击,但新应用程序需要RSASSA-PSS以实现稳健性。话虽如此,[7]Real-WordCryptography的DavidWong在讨论Bleichenbacher[5]对RSAPKCS#1加密和签名标准[6]的攻击时分享了一个有趣的统计数据:不同于第一次攻击,它完全破坏了加密算法,第二次攻击是实施攻击[用于签名验证]。这意味着如果签名方案被正确实现(根据规范),攻击将不会起作用。然而,在2019年[8]表明,许多用于签名的RSAPKCS#1v1.5的开源实现实际上落入了这个陷阱并且错误地实现了标准,这使得Bleichenbacher伪造攻击的不同变体起作用!RealWorldCryptography[9]由于攻击是针对签名验证的,因此您必须确信所有验证您的JWT的收件人都在使用不易受到Bleichenbacher攻击的库。如果您要与很多第3方打交道,那将很困难。解决OpenBanking(例如OpenID的金融级API(FAPI))不允许使用RSASSA-PKCS1-v1_5。对于我的普通读者,这是IdentityServer4版本3之前IdentityServer中唯一可用的算法。进一步阅读以了解如何使用OpenSSL为JWT签名生成RSA密钥[10]RSASSA-PSS(例如PS256)PS256=RSASSA-PSS使用SHA-256和MGF1以及SHA-256RSASSA-PSS是RSA的概率版本,其中相同的JWT标头和有效负载每次都会生成不同的签名。与其他算法不同,这是一种很好的概率方法;虽然在签名生成期间可以使用随机值,但它对安全性并不重要。一般来说,它更容易实现,因此更难出错。如果您想使用RSA密钥,那么建议您使用RSASSA-PSS而不是RSASSA-PKCS1-v1_5,但幸运的是,RSA密钥可以与任一签名方案一起使用。两者之间的签名长度也是一样的。英国的OpenBanking最初强制要求PS256,但后来向ES256开放。进一步阅读了解有关RSASSA-PSS的更多信息以及如何在.NETCore中使用它[11]了解如何使用OpenSSL为JWT签名生成RSA密钥[12]ECDSA(例如ES256)ES256=ECDSA使用P-256和SHA-256在椭圆曲线数字签名算法(ECDSA)的情况下,ES256指的是也与曲线关联的哈希算法的数量。ES256使用P-256(secp256r1,又名prime256v1),ES384使用P-384(secp384r1),奇怪的是,ES512使用P-521(secp521r1)。是的,521。是的,即使是Microsoft[13]也弄错了。椭圆曲线密码术(ECC)比RSA更难破解(或者我们可能真的很擅长破解RSA)。因此,ECDSA可以使用比RSA短得多的密钥和短得多的签名。大约256位的短椭圆曲线(EC)密钥提供与3072位RSA密钥相同的安全性。您经常会看到ECDSA被列为比RSA中的等效项更快,但这仅用于签名生成;使用RSA的签名验证通常仍然更快。使用JWT,您很可能会签名一次并验证多次。使用ECDSA签名的JWT具有概率签名,这意味着相同的JWT标头和有效负载将始终生成不同的签名。但不幸的是,ECDSA的概率很糟糕,随机生成对于签名的安全性至关重要。ECDSA使用每个签名生成的随机数(不超过一次)。不使用nonce一次使得私钥很容易恢复,就像索尼的Playstation3和比特币[14]所做的那样。在Playstation3中,私钥由于静态随机数而被恢复,而在比特币中,Android用户受到Android上Java的SecureRandom类中的错误的影响。如果概率签名的安全性需要随机值,那么您应该更喜欢不需要的确定性签名。RSASSA-PKCS1-v1_5有签名验证的问题,而ECDSA有签名生成的问题,当你是令牌发行者时更容易处理。ECDSA越来越受欢迎,但由于椭圆曲线密码学的实现方式,密码学家似乎普遍反对它,并担心由于使用随机值而导致实现困难。它的声誉比RSA好,但密码学家仍然主张转向EdDSA。JOSE最初使用的曲线由NIST定义。如果您担心使用NIST定义的曲线但想使用ECDSA,一个流行的替代方法是使用Koblitz曲线,例如secp256k1(与secp256r1相反)。Kobiltz曲线稍微弱一些,但如果您担心NIST曲线中使用的无法解释的随机数表示另一个NSA后门,Kobiltz曲线提供了一个越来越受欢迎的替代方案。您可以在比特币、以太坊和FIDO2中找到这些曲线的用法。但是,如果您想使用非NIST曲线,您应该使用EdDSA。在JOSE中,使用Kobiltz的算法以K结尾,比如ES256K。进一步阅读了解如何在.NETCore中使用ECDSA[15]以及如何在IdentityServer4中使用ECDSA签署令牌[16]了解如何使用OpenSSL生成用于JWT签名的EC密钥[17]在.NETCore签名算法中使用自定义JWT,以及使用Kobiltz曲线的例子[18]EdDSAEdDSA=EdDSA签名算法EdDSA使用单一的alg值,与以往算法的趋势相反。相反,它依赖于在crv的预先约定的密钥中定义的曲线()。例如,包含EdDSA公钥的JWK看起来像这样:{"kty":"OKP","alg":"EdDSA","crv":"Ed25519","x":"60mR98SQlHUSeLeIu7TeJBTLRG10qlcDLU4AJjQdqMQ"}这强制现代行为使用分配给键的曲线,而不是JWT,并消除了各种与算法相关的攻击。EdDSA是一种利用扭曲的爱德华兹曲线的椭圆曲线密码学形式。它是Schnorr签名系统(而不是DSA)的变体。EdDSA在签名和验证方面都很快,签名很短,并且避免了所有类别的安全漏洞。RFC8037定义了JOSE对以下EdDSA变体的支持:Ed25519:255位曲线Curve25519(32字节私钥,32字节公钥,64字节签名)。签名使用SHA-512。提供128位安全Ed448:448位Curve448-Goldilocks(57字节私钥,57字节公钥,114字节签名)。签名使用SHAKE256。提供224位安全性,使用EdDSA签名的JWT具有确定性签名,这意味着相同的JWT标头和有效负载将始终生成相同的签名。这是解决依赖随机nonce值保护私钥问题的确定性好方法。EdDSA仅在私钥创建期间使用随机值。这是JOSE和JWT[19]的评论家推荐的算法。JWT库对EdDSA的支持有点参差不齐,但希望很快能看到更多支持。进一步阅读了解有关EdDSA的更多信息以及如何在.NETCore中使用它[20]EdDSA入门[21]JWT在.NETCore中使用ScottBrady.IdentityModel[22]在cr.yp.to上签名[23]阅读更多关于EdDSAHMAC的设计优势(例如HS256)HS256=HMAC使用SHA-256可用于验证签名。例如,身份提供者拥有私钥,而依赖方使用公钥。在极少数情况下,您将是唯一一个发布和验证令牌的人,那么您可能会考虑使用对称加密和HS256之类的东西。这使用相同的密钥来创建和验证签名。在我看来,如果您发现自己处于这个位置,那么我认为JWT不是适合您的解决方案。如果同一实体同时进行读取和写入,那么JWT中往返结构化明文数据的要求是什么?我建议将数据存储在数据库中并传递引用或使用Branca令牌或JSONWeb加密(JWE)之类的东西来确保只有您可以读取数据。通常,使用HMAC进行JWT签名被认为是一种反模式。进一步阅读了解有关JWE的更多信息以及如何在.NETCore[24]中使用它们了解如何将Branca令牌[25]与ScottBrady.IdentityModel一起使用不使用任何加密?!None=base64加密对不起我忍不住了。请不要使用它。建议尽可能使用EdDSA,否则使用E??CDSA。如果您被迫使用RSA,请首选RSASSA-PSS而不是RSASSA-PKCS1-v1_5。我不认为说RSA正在慢慢退出是一个有争议的声明。目前,提供ECDSA是一个不错的选择,但理想情况下,您会希望尽可能使用EdDSA。但是,无论您使用哪种算法,请确保您提前知道期望使用哪种算法以及使用哪个密钥进行身份验证。参考资料[1]在RFC7518中:https://tools.ietf.org/html/rfc7518[2]在JOSEIANA注册表中:https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms[3]生日边界:https://en.wikipedia.org/wiki/Birthday_attack[4]RFC8017:https://tools.ietf.org/html/rfc8017[5]Bleichenbacher:https:攻击//en.wikipedia.org/wiki/Daniel_Bleichenbacher[6]:https://en.wikipedia.org/wiki/Daniel_Bleichenbacher[7]实字密码学:https://www.manning.com/books/真实世界密码学[8]2019:https://www.cs.purdue.edu/homes/schau/files/pkcs1v1_5-ndss19.pdf[9]真实世界密码学:https://www.manning.com/books/real-world-cryptography[10]生成RSA密钥:https://www.scottbrady91.com/OpenSSL/Creating-RSA-Keys-using-OpenSSL[11]RSASSA-PSS以及.NETCore:https://www.scottbrady91.com/C-Sharp/JWT-Signing-using-RSASSA-PSS-in-dotnet-Core[12]生成RSA密钥:https://www.scottbrady91.com/OpenSSL/Creating-RSA-Keys-using-OpenSSL[13]甚至微软:https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/master/src/Microsoft.IdentityModel.Tokens/JsonWebKeyECTypes.cs#L40[14]SonyPlaystation3和比特币:https://medium.com/securitysite-when-bob-met-alice/not-playing-randomly-the-sony-ps3-and-bitcoin-crypto-hacks-c1fe92bea9bc[15]如何在.NETCore中使用ECDSA:https://www.scottbrady91。com/C-Sharp/JWT-Signing-using-ECDSA-in-dotnet-Core[16]使用ECDSA在IdentityServer4中签署令牌:https://www.scottbrady91.com/Identity-Server/Using-ECDSA-in-IdentityServer4[17]生成EC密钥:https://www.scottbrady91.com/OpenSSL/Creating-Elliptical-Curve-Keys-using-OpenSSL[18]以及使用Kobiltz曲线的示例:https://www.scottbrady91.com/C-Sharp/Supporting-Custom-JWT-Signing-Algorithms-in-dotnet-Core[19]由JOSE和JWT的评论家推荐:https://www.scottbrady91.com/JOSE/Alternatives-to-JWTs[20]EdDSA以及如何在.NETCore中使用它:https://www.scottbrady91.com/C-Sharp/EdDSA-for-JWT-Signing-in-dotnet-Core[21]EdDSA入门:httpshttps://github.com/scottbrady91/身份用于JWT签名的模型[22]ScottBrady.IdentityModel:https://github.com/scottbrady91/IdentityModel[23]oncr.yp.to:https://ed25519.cr.yp.to/[24]JWE以及如何在.NETCore中使用它们:https://www.scottbrady91.com/C-Sharp/JSON-Web-Encryption-JWE-in-dotnet-Core[25]如何标记Branca:https://www.scottbrady91.com/C-Sharp/替换-JWTs-with-Branca-and-PASETO-in-dotnet-Core