本頁內容適用於 Apigee 和 Apigee Hybrid。
查看
Apigee Edge 說明文件。
本主題提供 JWT (JSON Web Token) 和 JWS (JSON Web Signature) 的一般資訊,以及 Apigee 代理程式開發人員可能感興趣的 Apigee JWS/JWT 政策。
簡介
JWS 和 JWT 通常用於在連線的應用程式之間共用聲明或判斷。Apigee API Proxy 可透過 JWS/JWT 政策執行下列操作:
- 產生 已簽署的 JWT 或 JWS,或加密的 JWT。
- 驗證 已簽署的 JWT 或JWS,或已加密的 JWT,並驗證 JWS/JWT 中的所選聲明。
- 解碼 已簽署或加密的 JWT,或JWS,但不驗證簽章或解密已加密的 JWT 或 JWS。如果是 JWS,DecodeJWS 政策只能解碼 JWS 標頭中的聲明。如果是加密的 JWT,DecodeJWT 政策只能解密未加密的標頭。
在後兩種情況下,這項政策也會設定流程變數。 這樣一來,Apigee 流程中的後續政策就能檢查 JWT 或 JWS 中的聲明,並根據這些聲明做出決策,或將該資訊傳播至後端服務。
使用 VerifyJWS 或 VerifyJWT 政策時,系統會拒絕無效的 JWS/JWT,並導致錯誤情況。同樣地,使用 DecodeJWS 或 DecodeJWT 政策時,如果 JWS/JWT 格式錯誤,就會導致錯誤狀況。
影片
觀看短片,快速瞭解 JWT。雖然這部影片專門介紹如何產生 JWT,但許多概念也適用於 JWS。
這部短片可協助您進一步瞭解 JWT 結構。
用途
您可以使用 JWS/JWT 政策執行下列操作:
- 在 Apigee Proxy 的 Proxy 或目標端點端產生新的 JWS/JWT。舉例來說,您可以建立產生 JWS/JWT 並傳回給用戶端的 Proxy 要求流程。或者,您也可以設計 Proxy,在目標要求流程中產生 JWS/JWT,並附加至傳送給目標的要求。JWS/JWT 及其聲明隨即會生效,後端服務可據此套用進一步的安全處理程序。
- 驗證並擷取從傳入用戶端要求、目標服務回應、服務呼叫政策回應或其他來源取得的 JWS/JWT 中的聲明。如果是已簽署的 JWS/JWT,無論 JWS/JWT 是由 Apigee 或第三方產生,Apigee 都會使用 RSA、ECDSA 或 HMAC 演算法驗證簽章。如果是加密的 JWT,Apigee 會使用其中一種支援的 JWA 加密演算法 (請參閱 IETF RFC 7518) 解密 JWT。
- 解碼 JWS/JWT。與驗證 JWS/JWT 政策搭配使用時,解碼功能最實用。在這種情況下,必須先瞭解 JWS/JWT 內憑證 (JWT) 或標頭 (JWS/JWT) 的值,才能驗證已簽署的 JWS/JWT 或解密已加密的 JWT。
JWS/JWT 的組成部分
簽署的 JWS/JWT 會將資訊編碼為三個部分,並以半形句號分隔:
Header.Payload.Signature
- 產生 JWS/JWT 政策會建立所有三個部分。
- 「驗證 JWS/JWT」政策會檢查所有三個部分。
- DecodeJWS 政策只會檢查標頭。DecodeJWT 政策只會檢查標頭和酬載。
JWS 也支援分離形式,可從 JWS 省略酬載:
Header..Signature
使用分離式 JWS 時,酬載會與 JWS 分開傳送。您可以使用「驗證 JWS」政策的 <DetachedContent>
元素,指定未編碼的原始 JWS 酬載。接著,Verify JWS 政策會使用 JWS 中的標頭和簽名,以及 <DetachedContent>
元素指定的酬載,驗證 JWS。
加密的 JWT 會將資訊編碼為五個部分,並以半形句號分隔:
Header.Key.InitializationVector.Payload.AuthenticationTag
GenerateJWT 和 VerifyJWT 政策會建立或檢查所有這些部分。 DecodeJWT 只能檢查未加密的標頭。
如要進一步瞭解權杖、編碼方式,以及簽署或加密方式,請參閱相關標準文件:
- JWT:IETF RFC7519
- JWS:IETF RFC7515
JWS 與 JWT 的差異
您可以使用 JWT 或 JWS,在已連結的應用程式之間分享聲明或判斷結果。 兩者之間的主要差異在於酬載的表示方式:
- JWT
- 酬載一律為 JSON 物件。
- 酬載一律會附加至 JWT。
- 憑證的
typ
標頭一律會設為JWT
。 - 酬載可簽署或加密。
- JWS
- 酬載可採用任何格式,例如 JSON 物件、位元組串流、八位元串流等。
- 酬載不必附加至 JWS。
- 系統一律會簽署標頭和酬載。JWS 不支援加密。
由於 JWT 格式一律會使用 JSON 物件代表酬載,因此 Apigee GenerateJWT 和 VerifyJWT 政策內建支援處理常見的已註冊聲明名稱,例如 aud、iss、sub 等。也就是說,您可以使用 GenerateJWT 政策的元素,在酬載中設定這些聲明,並使用 VerifyJWT 政策的元素驗證這些聲明的值。詳情請參閱 JWT 規格的「已註冊的宣告名稱」一節。
除了支援特定已註冊的聲明名稱,GenerateJWT 政策也直接支援將任意名稱的聲明新增至 JWT 的酬載或標頭。每項聲明都是簡單的名稱/值配對,值可以是 number
、boolean
、string
、map
或 array
類型。
使用 GenerateJWS 時,您會提供代表酬載的內容變數。由於 JWS 可使用任何資料表示法做為酬載,因此 JWS 中沒有「酬載聲明」的概念,且 GenerateJWS 政策不支援將任意名稱的聲明新增至酬載。您可以使用 GenerateJWS 政策,將任意名稱的宣告新增至 JWS 的標頭。此外,JWS 政策支援分離式酬載,也就是 JWS 省略酬載。分離式酬載可讓您分別傳送 JWS 和酬載。使用分離式酬載可更有效率地運用空間,特別是對於大型酬載而言,且多項安全標準都要求使用分離式酬載。
簽署與加密
Apigee 可以產生已簽署或加密的 JWT。如果酬載不需要保密,但必須向讀者提供完整性和不可否認性保證,請選擇已簽署的 JWT。簽署的 JWT 可向讀者保證,酬載自 JWT 簽署後未曾變更,且 JWT 是由私密金鑰持有人簽署。如果酬載應為機密資訊,請選擇加密的 JWT。加密的 JWT 可確保酬載內容的機密性,因為只有適當的金鑰持有者才能解密。
您可以同時使用加密和簽署的 JWT,特別是當加密的 JWT 使用非對稱式密碼編譯演算法 (RSA、ECDSA) 時。在這種情況下,由於加密金鑰是公開的,因此無法判斷該 JWT 的產生者身分。如要解決這個問題,請將簽署與加密功能合併。常見模式如下:
- 簽署酬載,產生 JWS 或已簽署的 JWT。
- 將簽署的結果加密,產生加密的 JWT。
使用這種方法在加密的 JWT 中嵌入已簽署的酬載,可同時確保不可否認性和機密性。Apigee 政策可以產生、解碼及驗證這類組合。
簽章演算法
如果是已簽署的 JWT,JWS/JWT 驗證和 JWS/JWT 產生政策支援 RSA、RSASSA-PSS、ECDSA 和 HMAC 演算法,使用位元強度為 256、384 或 512 的 SHA2 總和檢查碼。無論用來簽署 JWS/JWT 的演算法為何,DecodeJWS 和 DecodeJWT 政策都能正常運作。
HMAC 演算法
HMAC 演算法會使用共用密鑰 (又稱私密金鑰) 建立簽名 (也稱為簽署 JWS/JWT),並驗證簽名。
私密金鑰的長度下限取決於演算法的位元強度:
- HS256:金鑰長度下限為 32 個位元組
- HS384:金鑰長度下限為 48 個位元組
- HS512:金鑰長度下限為 64 個位元組
RSA 演算法
RSA 演算法會使用公開/私密金鑰組進行加密簽章。JWA 規格使用 RS256、RS384 和 RS512 這幾個代號來代表這些選項。使用 RSA 簽章時,簽署方會使用 RSA 私密金鑰簽署 JWS/JWT,驗證方則會使用相符的 RSA 公開金鑰驗證 JWS/JWT 上的簽章。金鑰大小沒有規定。
RSASSA-PSS 演算法
RSASSA-PSS 演算法是 RSA 演算法的更新版本,使用 PS256、PS384 和 PS512 等代號。與 RS* 變體一樣,RSASSA-PSS 會使用 RSA 公開/私密金鑰組進行加密簽章。金鑰的格式、機制和大小限制與 RS* 演算法相同。
ECDSA 演算法
橢圓曲線數位簽章演算法 (ECDSA) 演算法集是橢圓曲線密碼編譯演算法,具有 P-256、P-384 或 P-521 曲線。使用 ECDSA 演算法時,演算法會決定您必須指定的公開和私密金鑰類型:
演算法 | 曲線 | 重要條件 |
---|---|---|
ES256 | P-256 | 從 P-256 曲線產生的金鑰 (又稱 secp256r1 或 prime256v1) |
ES384 | P-384 | 從 P-384 曲線 (也稱為 secp384r1) 產生的金鑰 |
ES512 | P-521 | 從 P-521 曲線 (也稱為 secp521r1) 產生的金鑰 |
加密演算法
使用 GenerateJWT 和 VerifyJWT 處理加密的 JWT 時,政策支援下列演算法:
- dir
- RSA-OAEP-256
- A128KW、A192KW、A256KW
- A128GCMKW、A192GCMKW、A256GCMKW
- PBES2-HS256+A128KW、PBES2-HS384+A192KW、PBES2-HS512+A256KW
- ECDH-ES、ECDH-ES+A128KW、ECDH-ES+A192KW、ECDH-ES+A256KW
金鑰和金鑰表示法
JOSE 標準涵蓋 JWS、已簽署及加密的 JWT 等,說明如何使用加密金鑰簽署或加密資訊。任何加密編譯作業的基本要素都包括演算法和金鑰。不同演算法需要不同類型的金鑰,有時也需要不同大小的金鑰。
對稱演算法 (例如用於簽署的 HS* 系列,或用於加密的 A128KW 演算法) 需要對稱或共用金鑰:簽署和驗證使用相同的金鑰,或加密和解密使用相同的金鑰。非對稱演算法 (例如用於簽署的 RS*、PS* 和 ES* 演算法,或用於加密的 ECDH* 演算法) 會使用金鑰組,也就是一組相符的公開金鑰和私密金鑰。簽署時,簽署者會使用私密金鑰簽署,任何一方都能使用公開金鑰驗證簽章。加密者會使用公開金鑰加密,解密者則使用私密金鑰解密。顧名思義,公開金鑰可公開分享,不必保密。
您可以透過多種方式,將加密編譯金鑰序列化為文字格式。Apigee 政策接受以各種形式序列化的金鑰:PEM 編碼形式、JWKS 形式,或是共用金鑰的 UTF-8 編碼或 Base64 編碼形式。
PEM 格式
公開或私密金鑰通常會使用 PEM 編碼,這項編碼定義於 IETF RFC 7468。以下是 PEM 格式的私密金鑰範例:
-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgVcB/UNPxalR9zDYAjQIf
jojUDiQuGnSJrFEEzZPT/92hRANCAASc7UJtgnF/abqWM60T3XNJEzBv5ez9TdwK
H0M6xpM2q+53wmsN/eYLdgtjgBd3DBmHtPilCkiFICXyaA8z9LkJ
-----END PRIVATE KEY-----
公開金鑰或加密私密金鑰也有類似的 PEM 格式。
JWKS 格式
JSON Web Key (JWK) 是代表單一加密金鑰的 JSON 資料結構。JSON Web Key Set (JWKS) 是一種 JSON 結構,代表一組 JWK。JWK 和 JWKS 的說明請參閱 RFC7517。請參閱附錄 A 的 JWKS 範例。JSON Web Key 範例 Sets。
JWKS 的目的是讓任何一方都能以標準格式表示一組金鑰。主要用途是以標準方式分享公開金鑰,透過 HTTP 端點以 JWKS 格式傳送資料。當產生簽署 JWS 或 JWT 的公司或系統 (例如身分識別提供者) 發布公開金鑰時,任何可讀取公開金鑰的系統或應用程式,都能驗證簽署方產生的簽章。反之,任何想要加密資料的系統或應用程式,如果希望只有特定當事人或公司可以讀取資料,就能擷取該當事人或公司所屬的公開金鑰,並為此產生加密的 JWT。
RFC7517 說明各金鑰類型的 JWKS 金鑰元素,例如 RSA
或 EC
。這些元素一律須存在:
- kty - 金鑰類型,例如
RSA
或EC
。 - kid - 金鑰 ID。可以是任意不重複的字串值;單一鍵組內不得有重複值。如果傳入的 JWT 含有 JWKS 集合中的金鑰 ID,VerifyJWS 或 VerifyJWT 政策就會使用正確的公開金鑰驗證 JWS/JWT 簽章。
以下是選用元素及其值的範例:
- alg:金鑰演算法。必須與 JWS/JWT 中的簽署演算法相符。
- use:金鑰的預定用途。一般值為「sig」(簽署和驗證) 或「enc」(加密和解密)。
下列 JWKS (原先是從 https://www.googleapis.com/oauth2/v3/certs 擷取,但現在已過時) 包含必要元素和值,可供 Apigee 使用:
{ "keys":[ { "kty":"RSA", "alg":"RS256", "use":"sig", "kid":"ca04df587b5a7cead80abee9ea8dcf7586a78e01", "n":"iXn-WmrwLLBa-QDiToBozpu4Y4ThKdwORWFXQa9I75pKOvPUjUjE2Bk05TUSt7-V7KDjCq0_Nkd-X9rMRV5LKgCa0_F8YgI30QS3bUm9orFryrdOc65PUIVFVxIwMZuGDY1hj6HEJVWIr0CZdcgNIll06BasclckkUK4O-Eh7MaQrqb646ghFlG3zlgk9b2duHbDOq3s39ICPinRQWC6NqTYfqg7E8GN_NLY9srUCc_MswuUfMJ2cKT6edrhLuIwIj_74YGkpOwilr2VswKsvJ7dcoiJxheKYvKDKtZFkbKrWETTJSGX2Xeh0DFB0lqbKLVvqkM2lFU2Qx1OgtTnrw", "e":"AQAB" }, { "kty":"EC", "alg":"ES256", "use":"enc", "kid":"k05TUSt7-V7KDjCq0_N" "crv":"P-256", "x":"Xej56MungXuFZwmk_xccvsMpCtXmqhvEEMCmHyAmKF0", "y":"Bozpu4Y4ThKdwORWFXQa9I75pKOvPUjUjE2Bk05TUSt", } ] }
指定 JWS 和 JWT 政策的金鑰
無論是產生或驗證 JWS 或 JWT,您都需要提供金鑰,以供加密作業使用。
產生已簽署的 JWT 時,您需要提供可產生簽章的金鑰。
- 如果是 RS*、PS* 或 ES* 簽署演算法 (皆使用非對稱金鑰),您必須提供私密金鑰才能產生簽章。
- 如果是 HS* 演算法,您需要提供產生簽章時使用的對稱金鑰。
驗證已簽署的 JWS/JWT 時,您需要提供可驗證簽章的金鑰。
- 如果是 RS*、PS* 或 ES* 簽署演算法,您必須提供與原始用於簽署權杖的私密金鑰相關聯的公開金鑰。
- 如果是 HS* 演算法,您必須提供用於簽署 JWS 或 JWT 的相同對稱金鑰。
您可以透過下列兩種方式,將金鑰提供給 JWS 和 JWT 政策:
- 直接提供鍵值 (通常透過內容變數),或
- 透過
kid
和 JWKS 間接提供金鑰。您可以直接指定 JWKS,也可以透過 Apigee 可擷取 JWKS 的 HTTP 網址間接指定。
JWKS 網址選項通常只會做為可搭配非對稱演算法使用的公開金鑰來源,因為 JWKS 網址通常是公開的。
以下範例說明如何在各種情況下直接提供金鑰。
-
產生以 HS256 演算法簽署的 JWT。本例中,必要的金鑰是對稱金鑰。這項政策提供包含 base64url 編碼私密金鑰的內容變數。
<GenerateJWT name='gen-138'> <Algorithm>HS256</Algorithm> <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables> <SecretKey encoding='base64url'> <Value ref='private.secretkey'/> <Id ref='variable-containing-desired-keyid'/> </SecretKey> . . . <OutputVariable>output_variable_name</OutputVariable> </GenerateJWT>
-
驗證以 HS256 演算法簽署的 JWT。在這種情況下,必要金鑰是對稱金鑰。如上例所示,這項政策會提供包含 base64url 編碼私密金鑰的內容變數。
<VerifyJWT name='verify-138'> <Algorithm>HS256</Algorithm> <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables> <SecretKey encoding='base64url'> <Value ref='private.secretkey'/> </SecretKey> . . . <OutputVariable>output_variable_name</OutputVariable> </VerifyJWT>
-
驗證以 PS256 演算法簽署的 JWT。在這種情況下,必要金鑰為公開 RSA 金鑰。這項政策會提供包含 PEM 編碼公開金鑰的內容變數。
<VerifyJWT name='JWT-001'> <Algorithm>PS256</Algorithm> <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables> <PublicKey> <Value ref='variable-containing-pem-encoded-public-key'/> </PublicKey> . . . </VerifyJWT>
-
產生以 PS256 演算法簽署的 JWT。在這種情況下,必要金鑰為 RSA 私密金鑰。這項政策會提供含有 PEM 編碼私密金鑰的環境變數。
<GenerateJWT name='JWT-002'> <Algorithm>PS256</Algorithm> <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables> <PrivateKey> <Value ref='private.variable-containing-pem-encoded-private-key'/> </PrivateKey> . . . </GenerateJWT>
驗證 JWS 或已簽署的 JWT 時,將 JWKS 設為金鑰來源
系統或應用程式產生 JWS/JWT 時,通常會將金鑰 ID (kid
聲明) 插入 JWS/JWT 標頭。金鑰會告知 JWS/JWT 的任何讀取者,驗證已簽署 JWS/JWT 的簽章或解密已加密 JWT 時,需要使用哪一個金鑰。
舉例來說,假設核發者使用私密金鑰簽署 JWT。「金鑰 ID」會識別必須用來驗證 JWT 的相符公開金鑰。公開金鑰清單通常位於某些知名端點,例如 Google Identity 端點或 Firebase Authentication 端點。其他供應商會有自己的公開端點,以 JWKS 格式發布金鑰。
使用 Apigee 透過 JWKS 端點共用的公開金鑰驗證 JWS 或已簽署的 JWT 時,您可以採取下列做法:
選項 1:在政策設定中,於
<PublicKey/JWKS>
元素中指定 JWKS 端點 URI。舉例來說,如果是 VerifyJWT 政策:<VerifyJWT name="JWT-Verify-RS256"> <Algorithm>RS256</Algorithm> <Source>json.jwt</Source> <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables> <PublicKey> <JWKS uri="https://www.googleapis.com/oauth2/v3/certs"/> </PublicKey> . . . </VerifyJWT>
在這種情況下,Apigee 會:
- 檢查 JWS/JWT 標頭,找出簽署演算法 (alg),例如 RS256,如果該演算法與政策中設定的演算法不符,請拒絕傳入的 JWT。
- 從指定的 JWKS 端點或內部快取 (如果先前已使用這個 JWKS 端點),擷取金鑰清單及其 ID。
- 檢查 JWS/JWT 標頭,找出金鑰 ID (kid)。如果傳入的 JWT 標頭未包含金鑰 ID (kid),系統就無法將 kid 對應至驗證金鑰,Apigee 也會擲回錯誤。
- 從 JWKS 擷取 JWK,並在 JWS/JWT 標頭中註記金鑰 ID。 如果沒有該金鑰 ID 的金鑰,則擲回錯誤。
- 確認 JWK 的演算法與政策設定中指定的演算法相符。如果演算法不相符,請拒絕驗證並擲回錯誤。
- 使用該公開金鑰驗證 JWS/JWT 的簽名。如果簽章未通過驗證,請拒絕驗證並擲回錯誤。
選項 2:在政策設定中,於
<PublicKey/JWKS>
元素中指定保存 JWKS 端點 URI 的變數。舉例來說,如果是 VerifyJWT 政策:
<VerifyJWT name="JWT-Verify-RS256"> <Algorithm>RS256</Algorithm> <Source>json.jwt</Source> <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables> <PublicKey> <JWKS uriRef="variable-containing-a-uri"/> </PublicKey> . . . </VerifyJWT>
在這種情況下,Apigee 會執行上述相同步驟,但 Apigee 會從
uriRef
屬性參照的變數中指定的 URI 擷取 JWKS,而不是從硬式編碼的 URI 擷取。快取仍適用。選項 3:在政策設定中,指定
<PublicKey/JWKS>
元素中保存硬式編碼 JWKS 資料的變數。舉例來說,如果是 VerifyJWT 政策:
<VerifyJWT name="JWT-Verify-RS256"> <Algorithm>RS256</Algorithm> <Source>json.jwt</Source> <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables> <PublicKey> <JWKS ref="variable-that-holds-a-jwks"/> </PublicKey> . . . </VerifyJWT>
在這種情況下,Apigee 會執行與上述相同的步驟,但 Apigee 會從您在
ref
屬性中指定的內容變數擷取 JWKS,而不是從 URI 擷取。您通常會從 ServiceCallout、KVM 或與 Proxy 相關聯的屬性檔案載入這個內容變數。
產生加密 JWT 時,將 JWKS 設為金鑰來源
透過非對稱演算法 (RSA-OAEP-256 或任何 ECDH-* 變體) 產生加密 JWT 時,您會使用公開金鑰進行加密。您可以選擇將金鑰提供給 GenerateJWT 政策
一般做法是在政策設定的 <PublicKey/JWKS>
元素中指定 JWKS 端點 URI。例如:
<GenerateJWT name="GJWT-1"> <Algorithms> <Key>RSA-OAEP-256</Key> <Content>A128GCM</Content> </Algorithms> <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables> <PublicKey> <JWKS uri='https://www.example.com/.well-known/jwks.json'/> <Id ref='variable-containing-desired-keyid'/> </PublicKey> . . . </GenerateJWT>
在這種情況下,Apigee 會:
- 根據政策設定,組裝 JWT 的未編碼酬載和標頭。
- 從指定的 JWKS 端點或內部快取 (如果先前已使用這個 JWKS 端點),擷取金鑰清單及其 ID。目前快取存留時間為 5 分鐘。
- 從 JWKS 中,使用
PublicKey/Id
元素中記下的金鑰 ID 擷取 JWK。如果沒有該金鑰 ID 的金鑰,則擲回錯誤。 - 確認 JWK 的演算法與政策設定中指定的演算法相符。如果演算法不符,則擲回錯誤。
- 產生隨機序列,做為內容加密金鑰。
- 使用選取的公開金鑰加密內容加密金鑰。
- 使用內容加密金鑰加密酬載。
- 最後,將所有部分組合成序列化加密 JWT。
或者,您可以使用 uriRef
屬性指定變數,該變數會保留 JWKS 端點的 URI。例如:
<GenerateJWT name="GJWT-1"> <Algorithms> <Key>RSA-OAEP-256</Key> <Content>A128GCM</Content> </Algorithms> <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables> <PublicKey> <JWKS uriRef='variable-containing-jwks-uri'/> <Id ref='variable-containing-desired-keyid'/> </PublicKey> . . . </GenerateJWT>
在這種情況下,Apigee 會執行上述相同步驟,但 Apigee 會從 uriRef
屬性參照的變數中指定的 URI 擷取 JWKS,而不是從硬式編碼的 URI 擷取。如果先前使用過這個 JWKS 端點,Apigee 會從內部快取讀取 JWKS。