JWS 和 JWT 政策總覽

本頁內容適用於 ApigeeApigee Hybrid

查看 Apigee Edge 說明文件。

本主題提供 JWT (JSON Web Token) 和 JWS (JSON Web Signature) 的一般資訊,以及 Apigee 代理程式開發人員可能感興趣的 Apigee JWS/JWT 政策。

簡介

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

在後兩種情況下,這項政策也會設定流程變數。 這樣一來,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 只能檢查未加密的標頭。

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

JWS 與 JWT 的差異

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

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

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

除了支援特定已註冊的聲明名稱,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) 是一種 JSON 結構,代表一組 JWK。JWK 和 JWKS 的說明請參閱 RFC7517。請參閱附錄 A 的 JWKS 範例。JSON Web Key 範例 Sets

JWKS 的目的是讓任何一方都能以標準格式表示一組金鑰。主要用途是以標準方式分享公開金鑰,透過 HTTP 端點以 JWKS 格式傳送資料。當產生簽署 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 時,通常會將金鑰 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 會:

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