什麼是簽名密鑰 (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,簽名密鑰概念在各種情況下被廣泛使用,例如簽署電子郵件、文件和軟件包。關鍵原則保持不變:
- 對於對稱密鑰,相同的密鑰用於簽署和驗證。適合各方能安全共享密鑰或單一實體負責簽署和驗證的情況。
- 對於非對稱密鑰,使用私鑰簽署,並使用對應的公鑰驗證。當簽署和驗證方是不同實體時,這是合適的。