Logo Logo
GitHub Designed by Logto

什麼是簽名密鑰 (Signing key)?

OpenID Connect (OIDC) 的背景下,簽名密鑰 (Signing key),通常是一對非對稱密鑰,用於簽署和驗證 JSON Web Tokens (JWTs) 。OpenID 提供者使用簽名密鑰來簽署像 ID tokens access tokens 這類的 token,以確保它們的完整性和真實性。

儘管簽名密鑰的概念可以更廣泛應用,我們將重點介紹它們在 OIDC 中如何用於保護 token。其他用例如簽署電子郵件、文件和軟件包也可以從中衍生出同樣的原則。

範例:ID token 簽名

當用戶與 OpenID 提供者進行身份驗證 (Authentication) 時,提供者會發出一個包含用戶信息 ( claims ) 的 ID token 並由提供者簽名密鑰簽署。由於 ID token 是一個 JWT,它由三個部分組成:標頭、有效載荷和簽名。

1. 標頭

假設標頭如下:

{
  "alg": "ES384",
  "typ": "JWT"
}

JSON 指示 ID token 是使用 ECDSA 演算法和 P-384 曲線進行簽名的。typ 欄位指定 token 的類型是 JWT。

2. 有效載荷

有效載荷包含基本用戶信息:

{
  "sub": "1234567890",
  "name": "Alice"
}

sub claim 是用戶的唯一標識符,而 name 是他們的顯示名稱。

3. 簽署 token

根據 JWT 格式,應將標頭和有效載荷進行 Base64URL-編碼並使用 . 連接在一起以進行簽署:

{{header}}.{{payload}}

在這個例子中,值將是:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIn0

假設 OpenID 提供者使用以下私鑰對 token 進行簽署:

-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBW9PDXInlNT2hjOtQr
g4pkVkyJsKia33dHrsbOG4Z77pfYN7SYZCHh9YdLXTTKinehZANiAAQX/FB1s6Gj
YnDSGCY08PRUAQ8CCRCt8Ph/VDHfLj1xSbrjp8wFf0NjH7jcfNebpV1fvu4XKbP3
Ro7h0G6elN1TMsVECJPv4ieDNkYOsgT4UboJypC5E/rmvrlJTMM6Y/k=
-----END PRIVATE KEY-----

要對 token 進行簽署,OpenID 提供者需要使用私鑰生成簽名:

signature = sign(header + '.' + payload, private_key)

然後 Base64URL-編碼的簽名是:

Cjy6A_FHnwQBP0hRawoGTkRy8m8o0Ncc1q4BeyxYr0fxhKYmJJinIWZPXJdaAXRO9wOFuH2-UML2yWHjot_LnCPO6362asMvgNkEJMZ6UtqyOPlsCOJ7voTPOCT6sYu2

4. 組裝 JWT

最後,OpenID 提供者通過將標頭、有效載荷和簽名用 . 連接來組裝 JWT:

{{header}}.{{payload}}.{{signature}}

在這個例子中,ID token 將是:

eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIn0.Cjy6A_FHnwQBP0hRawoGTkRy8m8o0Ncc1q4BeyxYr0fxhKYmJJinIWZPXJdaAXRO9wOFuH2-UML2yWHjot_LnCPO6362asMvgNkEJMZ6UtqyOPlsCOJ7voTPOCT6sYu2

ID token 現在準備好發送給 客戶端 (Client) 進一步處理。

5. 驗證 token

當 client 收到 ID token 時,可使用 OpenID 提供者的公鑰來驗證簽名。通常,公鑰是通過 OpenID Connect (OIDC) 發現 (Discovery) 端點 (jwks_uri) 以 JSON Web Key Set (JWKS) 格式提供。

對於此例,公鑰為:

-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEF/xQdbOho2Jw0hgmNPD0VAEPAgkQrfD4
f1Qx3y49cUm646fMBX9DYx-43HzXm6VdX77uFymz90aO4dBunpTdUzLFRAiT7+In
gzZGDrIE+FG6CcqQuRP65r65SUzDOmP5
-----END PUBLIC KEY-----

對應的 JWK 值是:

{
  "kty": "EC",
  "crv": "P-384",
  "x": "F_xQdbOho2Jw0hgmNPD0VAEPAgkQrfD4f1Qx3y49cUm646fMBX9DYx-43HzXm6Vd",
  "y": "X77uFymz90aO4dBunpTdUzLFRAiT7-IngzZGDrIE-FG6CcqQuRP65r65SUzDOmP5"
}

現在,client 可以使用公鑰驗證簽名。

選擇正確的演算法

有多種演算法可用於簽署 JWT:

  • 對稱演算法:HMAC 與 SHA 系列(如 HS256、HS384、HS512)是一種使用相同密鑰進行簽署和驗證的對稱演算法。由於需要在各方之間共享密鑰,通常不推薦使用。
  • 非對稱演算法:RSA(如 RS256、RS384、RS512)和 ECDSA(如 ES256、ES384、ES512)是使用一對密鑰(私鑰簽署和公鑰驗證)的非對稱演算法。
    • RSA 廣泛使用並受到許多庫和平台的支持。然而,與 ECDSA 相比,它的密鑰和簽名尺寸較大。
    • ECDSA 性能更佳並且生成的簽名較小,適合受限的環境。由於較少見,確保你的平台支持它。

ECDSA 應成為新應用的首選,因為它具備性能和安全性上的優勢。

其他簽名密鑰場合

儘管上述示例專注於 OIDC 中的 ID token,簽名密鑰概念在各種情況下被廣泛使用,例如簽署電子郵件、文件和軟件包。關鍵原則保持不變:

  • 對於對稱密鑰,相同的密鑰用於簽署和驗證。適合各方能安全共享密鑰或單一實體負責簽署和驗證的情況。
  • 對於非對稱密鑰,使用私鑰簽署,並使用對應的公鑰驗證。當簽署和驗證方是不同實體時,這是合適的。

另見