JWS 和 JWT 政策總覽

本頁內容適用於 ApigeeApigee Hybrid

查看 Apigee Edge 說明文件。

本主題提供 JWT (JSON Web Token) 和 JWS (JSON Web Signature) 的一般資訊,以及 Apigee JWS/JWT 政策。

JWS 和 JWT 通常用於在連線的應用程式之間共用聲明或判斷。Apigee API Proxy 可透過 JWS/JWT 政策執行下列操作:

在後兩種情況下,政策也會設定流程變數。 這樣一來,Apigee 流程中的後續政策就能檢查 JWT 或 JWS 中的聲明,並根據這些聲明做出決策,或將該資訊傳播至後端服務。

使用 VerifyJWS 或 VerifyJWT 政策時,系統會拒絕無效的 JWS/JWT,並導致錯誤狀況。同樣地,使用 DecodeJWS 或 DecodeJWT 政策時,如果 JWS/JWT 格式錯誤,就會導致錯誤狀況。

用途

您可以使用 JWS/JWT 政策執行下列操作:

  • 在 Apigee Proxy 的 Proxy 或目標端點端產生新的 JWS/JWT。舉例來說,您可以建立產生 JWS/JWT 並傳回給用戶端的 Proxy 要求流程。或者,您也可以設計 Proxy,在目標要求流程中產生 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 只能檢查未加密的標頭。

如要進一步瞭解權杖、編碼方式,以及簽署或加密方式,請參閱相關標準文件:

JWS 和 JWT 的差異

您可以使用 JWT 或 JWS,在已連結的應用程式之間分享聲明或判斷結果。 兩者之間的主要差異在於酬載的表示方式:

  • JWT
    • 酬載一律為 JSON 物件。
    • 酬載一律會附加至 JWT。
    • 憑證的 typ 標頭一律會設為 JWT
    • 酬載可能經過簽署或加密。
  • JWS
    • 酬載可採用任何格式,例如 JSON 物件、位元組串流、八位元串流等。
    • 酬載不必附加至 JWS。
    • 系統一律會簽署標頭和酬載。JWS 不支援加密。

由於 JWT 格式一律會使用 JSON 物件代表酬載,因此 Apigee GenerateJWT 和 VerifyJWT 政策內建支援處理常見的已註冊聲明名稱,例如 audisssub 等。也就是說,您可以使用 GenerateJWT 政策的元素,在酬載中設定這些聲明,並使用 VerifyJWT 政策的元素驗證這些聲明的值。詳情請參閱 JWT 規格的「Registered Claim Names」一節。

除了支援特定已註冊的聲明名稱,GenerateJWT 政策也直接支援將任意名稱的聲明新增至 JWT 的酬載或標頭。每項聲明都是簡單的名稱/值配對,值可以是 numberbooleanstringmaparray 類型。

使用 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 曲線產生的金鑰 (又稱 secp256r1prime256v1)
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) 是代表一組 JWK 的 JSON 結構。JWK 和 JWKS 的說明請參閱 RFC7517。請參閱附錄 A 的 JWKS 範例。JSON Web Key Sets 範例

JWKS 的目的是讓任何一方都能以標準格式表示一組金鑰。主要用途是以標準方式分享公開金鑰,透過以 JWKS 格式傳送資料的 HTTP 端點分享。當產生簽署 JWS 或 JWT 的公司或系統 (例如身分識別提供者) 發布公開金鑰時,任何可讀取公開金鑰的系統或應用程式,都能驗證簽署方產生的簽章。反之,任何想要加密資料的系統或應用程式,如果希望只有特定當事人或公司可以讀取資料,就能擷取該當事人或公司所屬的公開金鑰,並為此產生加密的 JWT。

RFC7517 說明各金鑰類型的 JWKS 金鑰元素,例如 RSAEC。這些元素一律須存在:

  • kty - 金鑰類型,例如 RSAEC
  • 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 時,通常會在 JWS/JWT 標頭中插入金鑰 ID (kid 聲明)。金鑰會告知 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 會:

    1. 檢查 JWS/JWT 標頭,找出簽署演算法 (alg),例如 RS256,如果該演算法與政策中設定的演算法不符,請拒絕傳入的 JWT。
    2. 從指定的 JWKS 端點或內部快取 (如果先前已使用這個 JWKS 端點),擷取金鑰及其 ID 的清單。
    3. 檢查 JWS/JWT 標頭,找出金鑰 ID (kid)。如果傳入的 JWT 標頭未包含金鑰 ID (kid),系統就無法將 kid 對應至驗證金鑰,Apigee 會擲回錯誤。
    4. 從 JWKS 中,擷取 JWS/JWT 標頭中註明的金鑰 ID 所對應的 JWK。 如果沒有該金鑰 ID 的金鑰,則擲回錯誤。
    5. 確認 JWK 的演算法與政策設定中指定的演算法相符。如果演算法不相符,請拒絕驗證並擲回錯誤。
    6. 使用該公開金鑰驗證 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 會:

  1. 根據政策設定,組裝 JWT 的未編碼酬載和標頭。
  2. 從指定的 JWKS 端點,或從內部快取 (如果先前已使用這個 JWKS 端點),擷取金鑰清單及其 ID。目前快取存留時間為 5 分鐘。
  3. 從 JWKS 中,擷取金鑰 ID 位於 PublicKey/Id 元素中的 JWK。如果沒有該金鑰 ID 的金鑰,則擲回錯誤。
  4. 確認 JWK 的演算法與政策設定中指定的演算法相符。如果演算法不符,則擲回錯誤。
  5. 產生隨機序列,做為內容加密金鑰。
  6. 使用選取的公開金鑰加密內容加密金鑰。
  7. 使用內容加密金鑰加密酬載。
  8. 最後,將所有部分組合成序列化加密 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。