JWT 簽名算法 HS256、RS256 及 ES256 及密鑰生成


個人筆記,不保證正確。

博客已遷移至:https://ryan4yin.space/posts/jwt-algorithm-key-generation/

簽名算法

介紹具體的 JWT 簽名算法前,先解釋一下簽名、摘要/指紋、加密這幾個名詞的含義:

  1. 數字簽名(Digital Signature):就和我們日常辦理各種手續時需要在文件上簽上自己的名字一樣,數字簽名的主要用途也是防止偽造簽名。
  2. 數字摘要(digest)/數字指紋(fingerprint): 指的都是數據的 Hash 值。
  3. 加密算法:這個應該不需要解釋,就是對數據進行加密。。

數字簽名的具體實現,通常是先對數據進行一次 Hash 摘要(SHA1/SHA256/SHA512 等),然后再使用非對稱加密算法(RSA/ECDSA 等)的私鑰對這個摘要進行加密,這樣得到的結果就是原始數據的一個簽名。

用戶在驗證數據時,只需要使用公鑰解密出 Hash 摘要,然后自己再對數據進行一次同樣的摘要,對比兩個摘要是否相同即可。

注意:簽名算法是使用私鑰加密,確保得到的簽名無法被偽造,同時所有人都可以使用公鑰解密來驗證簽名。這和正常的數據加密算法是相反的。

因為數字簽名多了非對稱加密這一步,就能保證只有擁有私鑰的人才能生成出正確的數字簽名,達到了防止偽造簽名的目的。
而數字摘要(Hash)則誰都可以計算出來,通常由可信方公布數據的 Hash 值,用戶下載數據后,可通過 Hash 值對比來判斷數據是否損壞,或者被人調包。

重點在於,Hash 摘要必須由可信方公布出來,否則不能保證安全性。而數字簽名可以隨數據一起提供,不需要擔心被偽造。

JWT 是簽名和數據一起提供的,因此必須使用簽名才能保證安全性。

P.S. 在 Android/IOS 開發中,經常會遇到各類 API 或者 APP 商店要求提供 APP 的簽名,還指明需要的是 MD5/SHA1 值。
這個地方需要填的 MD5/SHA1 值,實際上只是你「簽名證書(=公鑰+證書擁有者信息)」的「數字指紋/摘要」,和 JWT 的簽名不是一回事。

前言

JWT 規范的詳細說明請見「參考」部分的鏈接。這里主要說明一下 JWT 最常見的幾種簽名算法(JWA):HS256(HMAC-SHA256) 、RS256(RSA-SHA256) 還有 ES256(ECDSA-SHA256)。

這三個都是消息簽名算法,得到的都只是一段無法還原的簽名。區別在於消息簽名簽名驗證需要的 「key」不同。

  1. HS256 使用同一個「secret_key」進行簽名與驗證(對稱加密)。一旦 secret_key 泄漏,就毫無安全性可言了。
    • 因此 HS256 只適合集中式認證,簽名和驗證都必須由可信方進行。
    • 傳統的單體應用廣泛使用這種算法,但是請不要在任何分布式的架構中使用它!
  2. RS256 是使用 RSA 私鑰進行簽名,使用 RSA 公鑰進行驗證。公鑰即使泄漏也毫無影響,只要確保私鑰安全就行。
    • RS256 可以將驗證委托給其他應用,只要將公鑰給他們就行。
  3. ES256 和 RS256 一樣,都使用私鑰簽名,公鑰驗證。算法速度上差距也不大,但是它的簽名長度相對短很多(省流量),並且算法強度和 RS256 差不多。

對於單體應用而言,HS256 和 RS256 的安全性沒有多大差別。
而對於需要進行多方驗證的微服務架構而言,顯然只有 RS256/ES256 才能提供足夠的安全性。
在使用 RS256 時,只有「身份認證的微服務(auth)」需要用 RSA 私鑰生成 JWT,其他微服務使用公開的公鑰即可進行簽名驗證,私鑰得到了更好的保護。

更進一步,「JWT 生成」和「JWT 公鑰分發」都可以直接委托給第三方的通用工具,比如 hydra
甚至「JWT 驗證」也可以委托給「API 網關」來處理,應用自身可以把認證鑒權完全委托給外部的平台,而應用自身只需要專注於業務。這也是目前的發展趨勢。

RFC 7518 - JSON Web Algorithms (JWA) 中給出的 JWT 算法列表如下:

   +--------------+-------------------------------+--------------------+
   | "alg" Param  | Digital Signature or MAC      | Implementation     |
   | Value        | Algorithm                     | Requirements       |
   +--------------+-------------------------------+--------------------+
   | HS256        | HMAC using SHA-256            | Required           |
   | HS384        | HMAC using SHA-384            | Optional           |
   | HS512        | HMAC using SHA-512            | Optional           |
   | RS256        | RSASSA-PKCS1-v1_5 using       | Recommended        |
   |              | SHA-256                       |                    |
   | RS384        | RSASSA-PKCS1-v1_5 using       | Optional           |
   |              | SHA-384                       |                    |
   | RS512        | RSASSA-PKCS1-v1_5 using       | Optional           |
   |              | SHA-512                       |                    |
   | ES256        | ECDSA using P-256 and SHA-256 | Recommended+       |
   | ES384        | ECDSA using P-384 and SHA-384 | Optional           |
   | ES512        | ECDSA using P-521 and SHA-512 | Optional           |
   | PS256        | RSASSA-PSS using SHA-256 and  | Optional           |
   |              | MGF1 with SHA-256             |                    |
   | PS384        | RSASSA-PSS using SHA-384 and  | Optional           |
   |              | MGF1 with SHA-384             |                    |
   | PS512        | RSASSA-PSS using SHA-512 and  | Optional           |
   |              | MGF1 with SHA-512             |                    |
   | none         | No digital signature or MAC   | Optional           |
   |              | performed                     |                    |
   +--------------+-------------------------------+--------------------+

   The use of "+" in the Implementation Requirements column indicates
   that the requirement strength is likely to be increased in a future
   version of the specification.

可以看到被標記為 Recommended 的只有 RS256 和 ES256(糾正:最后一列只是對 JWA 具體實現的一些要求,和算法推薦沒有任何關系!)。

ES256 使用 ECDSA 進行簽名,它的安全性和運算速度目前和 RS256 差距不大,但是擁有更短的簽名長度。
對於需要頻繁發送的 JWT 而言,更短的長度長期下來可以節約大量流量。

因此更推薦使用 ES256 算法。

使用 OpenSSL 生成 RSA/ECC 公私鑰

RS256 使用 RSA 算法進行簽名,可通過如下命令生成 RSA 密鑰:

# 1. 生成 2048 位(不是 256 位)的 RSA 密鑰
openssl genrsa -out rsa-private-key.pem 2048

# 2. 通過密鑰生成公鑰
openssl rsa -in rsa-private-key.pem -pubout -out rsa-public-key.pem

ES256 使用 ECDSA 算法進行簽名,該算法使用 ECC 密鑰,生成命令如下:

# 1. 生成 ec 算法的私鑰,使用 prime256v1 算法,密鑰長度 256 位。(強度大於 2048 位的 RSA 密鑰)
openssl ecparam -genkey -name prime256v1 -out ecc-private-key.pem
# 2. 通過密鑰生成公鑰
openssl ec -in ecc-private-key.pem -pubout -out ecc-public-key.pem

密鑰的使用應該就不需要介紹了,各類語言都有對應 JWT 庫處理這些,請自行查看文檔。

如果是調試/學習 JWT,需要手動簽名與驗證的話,推薦使用 jwt 工具網站 - jwt.io

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM