本文翻译自 How to select a JOSE / JWT cryptographic algorithm for your application。
JOSE 即 JavaScript Object Signing and Encryption,已采用了一系列标准加密算法,包括新的 Edwards 曲线算法(2017年新增)。那么应该使用哪种算法来保护 JWT 或应用程序中的其他对象呢?本指南将提供一些基本准则,以便做出明智的选择。 但是,也请咨询该领域的文章,文献和专家,以仔细检查您的假设,并确保没有遗漏任何重要问题。
一、常见的数据安全关注点
我们保护数据(例如令牌)的需求通常来自一个或多个关注的问题:
- 完整性
关注数据未被篡改 - 真实性
关注可以验证数据的来源 - 不可抵赖
数据的来源必须可由其他人验证 - 保密
对未经授权的各方和操作保密
选择合适的 JOSE 算法的第一步,是了解我们所关注的安全因素。
二、可用的 JOSE 算法类
JOSE 提供了三种截然不同的加密算法类,以解决四个安全问题,它们具有部分重叠的属性:
算法 | 完整性 | 真实性 | 不可抵赖性 | 保密性 |
---|---|---|---|---|
HMAC | ✔ | ✔ | ||
数字签名 | ✔ | ✔ | ✔ | |
认证加密 | ✔ | ✔ | ✔ |
HMAC 算法:一种特殊的超高效哈希(HMAC),用于确保数据的完整性和真实性。 为了计算 HMAC,您需要一个密钥。
数字签名:提供 HMAC 的特性,以及加密的不可否认性(使签名者以外的其他人可以检查签名的有效性)。 数字签名基于公钥/私钥密码术。 需要一对公钥/私钥(类型为 RSA,椭圆曲线(EC)或爱德华兹曲线八位密钥对(OKP))。
认证加密:在加密数据的同时,还确保其完整性和真实性(例如 HMAC)。 JOSE 通过公钥/私钥,秘密(共享)密钥和密码来提供加密。
1、基于哈希的消息认证码(HMAC)
- 功能:完整性、认证
- JOSE 格式:JSON Web Signature(JWS)
- 秘钥类型:密码
- 算法:HMAC with SHA-2
- HS256
- HS384
- HS512
- 用例:
- 存储在浏览器 Cookie 中的无状态会话
- 电子邮件验证码
- 会话 ID 能够区分过期 ID 和无效 ID
- Token 的颁发者和最终用户是==同一角色==
- 备注
- HMAC 不是数字签名!
- 使用更长的密钥/哈希(例如 HS512 )以提高安全性
- 长于哈希大小的密钥不能提供额外的安全性
对于令牌和其他需要向外发送或存储的信息,如果最终由颁发它的应用程序使用,那么 HMAC 算法(带有 JOSE alg 标识符 HS256,HS384 和 HS512)是理想的选择。
这里的主要关注点是确保:
1)当我们取回数据时数据的完整性,以及
2)数据实际上是由我们产生的。
示例 JWT 声明了无状态会话 Cookie:
{
"sub" : "user-12345",
"email" : "alice@wonderland.net",
"login_ip" : "172.16.254.1",
"exp" : 1471102267
}
一个常见的误解,是消息身份验证代码可以视为数字签名。 其实并不是! 这是因为验证的过程需要用到计算 HMAC 时使用的原始密钥,并且从本质上讲,该密钥不能与他人分享,因为不能让他人具备生成自己的 HMAC 的能力。 如果想要不可否认性,请使用 RSA,EC 或 EdDSA 签名。
2、数字签名
-
功能:完整性、认证、不可抵赖性
-
JOSE 格式:JSON Web Signature(JWS)
-
秘钥类型:公钥/私钥对
- RSA
- EC
- OKP
-
算法
-
RSA signature with PKCS #1 and SHA-2
- RS256
- RS384
- RS512
-
- PS256
- PS384
- PS512
-
- ES256
- ES384
- ES512
-
Edwards-curve DSA signature with SHA-2
- Ed25519
- Ed448
-
-
用例
- ID tokens (OpenID Connect)
- Self-contained access tokens (OAuth 2.0)
- 在域之间传递安全性断言和令牌
- 需要必须由其他人验证数据的完整性和真实性的场景
-
备注
- 私钥一定要保密!
- 推荐的 RSA 密钥大小为 2048 位
- EdDSA 签名提供最佳性能
数字签名适用于发行令牌、声明、断言和文档,且这些文档的完整性和真实性必须由其他各方进行验证的场景。
使用示例:
- 由 OpenID 提供者发行的身份令牌,依赖方需要对其进行验证才能登录用户。
- 由 OAuth 2.0 服务器发出的访问令牌,资源服务器 / Web API 必须在提供请求之前对其进行验证。
数字签名仅适用于公用/专用密钥:
- 颁发者需要先生成 公钥/私钥对,然后才能对 JWT 或其他对象进行签名。
- 签名是用私钥计算的,私钥必须始终保持安全,否则会有冒充的风险。
- JWT 或 JWS 对象的签名需要使用公钥验证。接收者发现和下载公钥的方法是特定于应用程序的。 例如,OpenID Connect 服务器在 JWK 格式的 URL 上发布其公钥。
选择哪种数字签名算法?
-
如果您需要广泛的支持,请选择 RS256。 该算法基于 RSA PKCS#1,它仍然是公钥/私钥密码术使用最广泛的标准。 任何足够好的 JWT 库都应支持它。 RSxxx 签名也花费很少的 CPU 时间进行验证(有助于确保在资源服务器上快速处理访问令牌)。 推荐的 RSA 密钥长度为 2048 位。
-
ESxxx 签名算法使用椭圆曲线(EC)加密。 它们需要较短的密钥,并且产生较小的签名(相当于 RSA 强度)。 但是,EC 签名有一个缺点:它们的验证速度很慢。
-
新的 Edxxx 算法提供了最佳的签名/验证性能组合。 特别是通过 P-265 曲线签名,比2048 位 RSA 高 62 倍,比 EC DSA 高 14 倍。
我们有 RSA,EC 和 EdDSA 签名的 JWT 的示例。
3、认证加密
-
功能:机密性、完整性、认证
-
JOSE 格式:JSON Web Encryption(JWE)
-
秘钥类型:
- Public / private key pair:
- RSA
- EC
- OKP
- Secret (shared) key
- Password
- Public / private key pair:
-
算法
-
公钥/私钥加密
- RSA
-
RSA OAEP
- RSA-OAEP
- RSA-OAEP-256
- RSA PKCS #1
-
RSA OAEP
- EC
- RSA
-
秘密(共享)密钥加密
-
Direct AES encryption
- dir
-
AES key wrap
- A128KW
- A192KW
- A256KW
-
AES GCM key encryption
- A128GCMKW
- A192GCMKW
- A256GCMKW
-
Direct AES encryption
-
基于密码加密
-
PBES2
- PBES2-HS256+A128KW
- PBES2-HS384+A192KW
- PBES2-HS512+A256KW
-
PBES2
-
-
用例
- 签名和加密的ID令牌(OpenID Connect)
- 签名并加密的自包含访问令牌(OAuth 2.0)
- 加密的文档和数据,具有完整性和真实性检查
-
备注
- 保持私钥和私钥的安全!
- 推荐的 RSA 密钥大小为 2048 位
JOSE 通过以下方式提供加密:
-
如果您想自己加密数据,请使用密钥。 如果秘密密钥是与其他方共享的(通过某种带外方式),则他们也可以使用它来加密数据/解密密文。 请查看上表,了解可用的密钥加密算法。
-
收件人提供的公共密钥(RSA,EC 或 OKP)。 例如,OpenID Connect 提供程序以可发现的 URL 以 JWK 格式发布其公共密钥。 接收者然后可以使用其匹配的私钥解密 JWT / JOSE 对象。 在不可能或不可行带外通信的情况下,公钥/私钥密码术是理想的选择。
-
如果您想使用可以记住的短语加密数据,则为密码。
JOSE 中的加密始终是经过身份验证的,这意味着密文的完整性受到保护,不会被篡改。 因此,经过身份验证的加密使 HMAC JWT 嵌套在 JSON Web 加密(JWE)中变得多余。 仅使用 JWE 加密。
JWE 加密是一个“两步”过程:
1、数据或内容始终使用 AES 密钥(称为内容加密密钥或 CEK)进行加密,并且每个 JWT / JWE 对象都使用不同的 CEK。 Nimbus 库将自动为您生成此 AES 密钥,并且其长度将取决于 enc(加密方法)标头参数(例如,"enc": "A128GCM"
将导致生成 128 位 AES 密钥)。 除了可以选择三种 AES 密钥长度(128、198 和 256)之外,JOSE 还支持两种内容加密模式:AxxxCBC-HSxxx 和 AxxxGCM。 通常,更广泛地支持 AxxxCBC-HSxxx 模式。
2、第二步是使用输入密钥(即您提供的秘密密钥,公共密钥或密码)对生成的 AES CEK 进行加密(也称为包装)。 这由 alg JWE 标头参数指定。 如果要跳过第二步,直接提供 AES CEK,请选择直接加密并指定 "alg": "dir"
JWE 标头参数。
示例 JWE 标头,其中的内容在 GCM 模式下使用 128 位 AES 加密,而 AES CEK 本身使用公共 RSA 密钥加密(使用 RSA OAEP 加密):
{
"alg" : "RSA-OAEP",
"enc" : "A128GCM"
}
您可以查看使用 RSA 公钥加密 JWT 的示例。
三、嵌套签名和加密
可以对签名的 JWT / JWS 对象进行附加加密,从而为数据提供完整性,真实性,不可否认性和机密性。
这是通过简单的嵌套实现的(请参见示例):
1、JWT 用专用的 RSA,EC 或 OKP 密钥签名。
2、然后,签名的 JWT 成为 JWE 对象的有效负载(纯文本),该JWE对象使用接收者的公钥(RSA,EC,OKP)或已在两方之间共享的秘密密钥进行加密。
处理嵌套的 JWT 向后工作:
1、使用适当的密钥(RSA,EC 或 OKP 的私钥或已建立的私钥)解密 JWE 对象。
2、然后将提取的有效负载(纯文本)解析为签名的 JWT,并使用发行者的公钥(RSA,EC 或 OKP)进行验证。
Nimbus JOSE + JWT 库提供了用于处理嵌套 JWT 的完整框架。
四、OpenID Connect 中的算法选择
通过以下规则,您可以了解在给定客户端注册后哪些 ID 令牌算法可行:
1、具有 client_secret 的客户端可以接收由 HMAC(其中 client_secret 充当 HMAC 秘密密钥)或签名(RSA,EC 或 EdDSA)保护的 ID 令牌。 为了验证 RSA,EC 或 EdDSA 签名的 ID 令牌,客户端仅需要(从其 JWK 设置 URL 中)检索 OpenID 提供程序的匹配公钥。
2、带有 client_secret 的客户端也可以接收加密的 ID 令牌,其中 client_secret 用于从中导出秘密 AES 密钥。
3、如果使用 HMAC,则要求的 client_secrets 必须足够长以适合所需的密钥长度。 例如,一个 256 位 client_secret 允许 HMAC 与 HS256 一起使用。
4、为了使客户端能够接收 RSA 或 ECDH 加密的 ID 令牌,它必须具备使用 OpenID 提供方注册的 RSA,EC 或 OKP 公钥。
5、OpenID Connect 还允许没有 client_secret 的客户端。 此类客户端需要向 OpenID 提供程序注册的 RSA,EC 或 OKP 公钥,并使用该密钥在令牌端点进行身份验证(通过 JWT)。 如上所述,可以将 ID 令牌加密为该公共密钥。
五、参考资料
(完)
网友评论