SSL 握手失敗 - 用戶端憑證錯誤

您目前查看的是 Apigee Edge 說明文件。
前往 Apigee X 說明文件
info

問題

用戶端應用程式會收到 HTTP 狀態碼 503,以及「Service Unavailable」(服務無法使用) 訊息,做為 API 要求的回應。在 UI 追蹤記錄中,您會發現失敗的 API 要求在「目標要求流程」中,error.cause Received fatal alert: bad_certificate

如果您有權存取訊息處理器記錄,就會發現失敗的 API 要求會顯示 Received fatal alert: bad_certificate 錯誤訊息。在雙向 TLS 設定中,訊息處理器與後端伺服器之間的 SSL 交握程序會發生這項錯誤。

錯誤訊息

用戶端應用程式會取得下列回應代碼:

HTTP/1.1 503 Service Unavailable

此外,您可能會看到下列錯誤訊息:

{
 "fault": {
    "faultstring":"The Service is temporarily unavailable",
    "detail":{
        "errorcode":"messaging.adaptors.http.flow.ServiceUnavailable"
    }
 }
}

Private Cloud 使用者會在訊息處理器記錄 /opt/apigee/var/log/edge-message-processor/system.log 中,看到特定 API 要求的下列錯誤:

2017-10-23 05:28:57,813 org:org-name env:env-name api:apiproxy-name rev:revision-number messageid:message_id NIOThread@0 ERROR HTTP.CLIENT - HTTPClient$Context.handshakeFailed() : SSLClientChannel[C:IP address:port # Remote host:IP address:port #]@65461 useCount=1 bytesRead=0 bytesWritten=0 age=529ms lastIO=529ms handshake failed, message: Received fatal alert: bad_certificate

可能原因

這個問題的可能原因如下:

原因 說明 適用於以下裝置的疑難排解說明
沒有用戶端憑證 目標伺服器目標端點中使用的金鑰儲存區沒有任何用戶端憑證。 Edge 私有雲和公有雲使用者
憑證授權單位不符 訊息處理器的金鑰儲存區中,葉節點憑證 (憑證鏈中的第一個憑證) 的憑證授權單位,與後端伺服器接受的任何憑證授權單位都不相符。 Edge 私有雲和公有雲使用者

常見診斷步驟

  1. 在 Edge UI 中啟用追蹤功能、發出 API 呼叫,然後重現問題。
  2. 在 UI 追蹤結果中,逐一瀏覽每個階段,找出發生錯誤的位置。錯誤會發生在目標要求流程中。
  3. 檢查顯示錯誤的 Flow,您應該會看到如下方範例追蹤記錄所示的錯誤:

    alt_text

  4. 如上方的螢幕截圖所示,error.cause 為 「Received fatal alert: bad_certificate」。
  5. 如果您是私有雲使用者,請按照下列指示操作:
    1. 如要取得失敗 API 要求的訊息 ID,請在追蹤記錄中,找出 AX 指示的階段,並判斷錯誤標頭「X-Apigee.Message-ID」的值。
    2. 在訊息處理器記錄 /opt/apigee/var/log/edge-message-processor/system.log 中搜尋這個訊息 ID,判斷是否能找到錯誤的相關資訊:
      2017-10-23 05:28:57,813 org:org-name env:env-name api:apiproxy-name
      rev:revision-number messageid:message_id NIOThread@0 ERROR HTTP.CLIENT - HTTPClient$Context.handshakeFailed() :
      SSLClientChannel[C:IP address:port # Remote host:IP address:port #]@65461 useCount=1
      bytesRead=0 bytesWritten=0 age=529ms lastIO=529ms handshake failed, message: Received fatal alert: bad_certificate
      2017-10-23 05:28:57,813 org:org-name env:env-name api:apiproxy-name
      rev:revision-number messageid:message_id NIOThread@0 ERROR HTTP.CLIENT - HTTPClient$Context.handshakeFailed() : SSLInfo:
      KeyStore:java.security.KeyStore@52de60d9 KeyAlias:KeyAlias TrustStore:java.security.KeyStore@6ec45759
      2017-10-23 05:28:57,814 org:org-name env:env-name api:apiproxy-name
      rev:revision-number messageid:message_id NIOThread@0 ERROR ADAPTORS.HTTP.FLOW - RequestWriteListener.onException() :
      RequestWriteListener.onException(HTTPRequest@6071a73d)
      javax.net.ssl.SSLException: Received fatal alert: bad_certificate
      at sun.security.ssl.Alerts.getSSLException(Alerts.java:208) ~[na:1.8.0_101]
      at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1666) ~[na:1.8.0_101]
      at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1634) ~[na:1.8.0_101]
      at sun.security.ssl.SSLEngineImpl.recvAlert(SSLEngineImpl.java:1800) ~[na:1.8.0_101]
      at com.apigee.nio.NIOSelector$SelectedIterator.findNext(NIOSelector.java:496) [nio-1.0.0.jar:na]
      at com.apigee.nio.util.NonNullIterator.computeNext(NonNullIterator.java:21) [nio-1.0.0.jar:na]
      at com.apigee.nio.util.AbstractIterator.hasNext(AbstractIterator.java:47) [nio-1.0.0.jar:na]
      at com.apigee.nio.NIOSelector$2.findNext(NIOSelector.java:312) [nio-1.0.0.jar:na]
      at com.apigee.nio.NIOSelector$2.findNext(NIOSelector.java:302) [nio-1.0.0.jar:na]
      at com.apigee.nio.util.NonNullIterator.computeNext(NonNullIterator.java:21) [nio-1.0.0.jar:na]
      at com.apigee.nio.util.AbstractIterator.hasNext(AbstractIterator.java:47) [nio-1.0.0.jar:na]
      at com.apigee.nio.handlers.NIOThread.run(NIOThread.java:59) [nio-1.0.0.jar:na]

      訊息處理器記錄檔有錯誤的堆疊追蹤記錄 Received fatal alert: bad_certificate,但沒有任何其他資訊指出問題原因。

  6. 如要進一步調查這個問題,請使用 tcpdump 工具擷取 TCP/IP 封包。
    1. 如果您是私有雲使用者,則可以在後端伺服器或訊息處理器上擷取 TCP/IP 封包。最好在後端伺服器上擷取封包,因為封包會在後端伺服器上解密。
    2. 如果您是公有雲使用者,請擷取後端伺服器上的 TCP/IP 封包。
    3. 決定要擷取 TCP/IP 封包的位置後,請使用下列 tcpdump 指令擷取 TCP/IP 封包。
    4. tcpdump -i any -s 0 host <IP address> -w <File name>

      如果您要在訊息處理器上擷取 TCP/IP 封包,請在 tcpdump 指令中使用後端伺服器的公開 IP 位址。

      如果後端伺服器/訊息處理器有多個 IP 位址,則必須使用不同的 tcpdump 指令。如要進一步瞭解這項工具和這個指令的其他變體,請參閱 tcpdump

  7. 使用 Wireshark 工具或您熟悉的類似工具,分析 TCP/IP 封包。

以下是使用 Wireshark 工具分析的 TCP/IP 封包資料範例:

alt_text

  1. 上述 tcpdump 中的訊息 #4 顯示,訊息處理器 (來源) 將「Client Hello」訊息傳送至後端伺服器 (目的地)。
  2. 訊息 #5 顯示後端伺服器確認來自訊息處理器的 Client Hello 訊息。
  3. 後端伺服器會傳送「Server Hello」訊息和憑證,然後要求用戶端在訊息 #7 中傳送憑證。
  4. 訊息處理器會完成憑證驗證,並在訊息 #8 中確認後端伺服器的 ServerHello 訊息。
  5. 訊息處理器會在訊息 #9 中將憑證傳送至後端伺服器。
  6. 後端伺服器會在訊息 #11 中確認收到訊息處理器的憑證。
  7. 不過,它會立即將「Fatal Alert: Bad Certificate」傳送至訊息處理器 (Message #12)。這表示訊息處理器傳送的憑證有問題,因此後端伺服器無法通過憑證驗證。因此,SSL 握手程序失敗,連線將會關閉。


    alt_text

  8. 現在來查看訊息 #9,檢查訊息處理器傳送的憑證內容:


    alt_text

  9. 如您所見,後端伺服器並未從用戶端取得任何憑證 (憑證長度:0)。因此後端伺服器會傳送嚴重警示:憑證無效。
  10. 通常發生這種情況時,用戶端 (即訊息處理器,以 Java 為基礎的程序) 會:
    1. 金鑰儲存庫中沒有任何用戶端憑證;或
    2. 無法傳送用戶端憑證。如果找不到由後端伺服器可接受的憑證授權單位核發的憑證,就可能發生這種情況。也就是說,如果用戶端葉子憑證的憑證授權單位 (即鏈結中的第一個憑證) 與任何後端伺服器可接受的憑證授權單位不符,訊息處理器就不會傳送憑證。

以下將分別說明這些原因。

原因:沒有用戶端憑證

診斷

如果目標端點的 SSL 資訊部分或目標端點中使用的目標伺服器,沒有在 Keystore 中指定憑證,就會導致這個錯誤。

請按照下列步驟判斷是否為這個原因:

  1. 請按照下列步驟,找出特定 API Proxy 的目標端點或目標伺服器所使用的金鑰儲存區:
    1. 從目標端點或目標伺服器中的「SSLInfo」部分,取得「Keystore」元素的 KeyStore 參照名稱。

      以下是目標端點設定中的 SSLInfo 範例:

      <SSLInfo>
        <Enabled>true</Enabled>
        <ClientAuthEnabled>true</ClientAuthEnabled>
        <KeyStore>ref://myKeystoreRef</KeyStore>
        <KeyAlias>myKey</KeyAlias>
        <TrustStore>ref://myTrustStoreRef</TrustStore>
      </SSLInfo>
    2. 在上述範例中,Keystore 參照名稱為「myKeystoreRef"
    3. 前往 Edge UI,然後選取「API Proxies」->「Environment Configurations」

      選取「參考資料」分頁標籤,然後搜尋 KeyStore 參考名稱。 記下特定金鑰儲存區參照「參照」欄中的名稱。 這會是您的金鑰儲存庫名稱。


      alt_text

    4. 在上述範例中,您會發現 myKeystoreRef 參照了「myKeystore」。因此金鑰儲存庫名稱為 myKeystore。
  2. 使用 Edge UI 或 List certs for keystore API,檢查這個 Keystore 是否包含憑證。
  3. 如果金鑰儲存庫確實包含憑證,請前往「原因:憑證授權單位不符」
  4. 如果 Keystore 不含任何憑證,這就是訊息處理器未傳送用戶端憑證的原因。

解析度

  1. 請確認已將正確且完整的用戶端憑證鏈結上傳至訊息處理器的特定 KeyStore。

原因:憑證授權單位不符

一般來說,當伺服器要求用戶端傳送憑證時,會指出接受的簽發者或憑證授權單位組合。如果訊息處理器 Keystore 中,葉子憑證 (即憑證鏈結中的第一個憑證) 的簽發者/憑證授權單位,與後端伺服器接受的憑證授權單位不符,訊息處理器 (即以 Java 為基礎的程序) 就不會將憑證傳送至後端伺服器。

請按照下列步驟確認是否為這種情況:

  1. 列出 Keystore API 的憑證
  2. 使用「Get cert for keystore API」,取得您在上述步驟 1 中取得的每個憑證詳細資料。
  3. 記下儲存在金鑰儲存區中的分葉憑證簽發者 (即憑證鏈結中的第一個憑證)

    葉節點憑證範例

    {
      "certInfo" : [ {
        "basicConstraints" : "CA:FALSE",
        "expiryDate" : 1578889324000,
        "isValid" : "Yes",
        "issuer" : "CN=MyCompany Test SHA2 CA G2, DC=testcore, DC=test, DC=dir, DC=mycompany, DC=com",
        "publicKey" : "RSA Public Key, 2048 bits",
        "serialNumber" : "65:00:00:00:d2:3e:12:d8:56:fa:e2:a9:69:00:06:00:00:00:d2",
        "sigAlgName" : "SHA256withRSA",
        "subject" : "CN=nonprod-api.mycompany.com, OU=ITS, O=MyCompany, L=MELBOURNE, ST=VIC, C=AU",
        "subjectAlternativeNames" : [ ],
        "validFrom" : 1484281324000,
        "version" : 3
      } ],
      "certName" : "nonprod-api.mycompany.com.key.pem-cert"
    }

    在上述範例中,簽發者/憑證授權單位為 "CN=MyCompany Test SHA2 CA G2, DC=testcore, DC=test, DC=dir, DC=mycompany, DC=com"

  4. 使用下列其中一種技術,判斷後端伺服器接受的簽發者或憑證授權單位清單:

    技巧 1:使用下列 openssl 指令:

    openssl s_client -host <backend server host name> -port <Backend port#> -cert <Client Certificate> -key <Client Private Key>
    

    請參閱這項指令輸出內容中,標題為「Acceptable Client Certificate CA names」(可接受的用戶端憑證 CA 名稱) 的部分,如下所示:

    Acceptable client certificate CA names
    /C=AU/ST=VIC/L=MELBOURNE/O=MyCompany/OU=ITS/CN=nonprod-api.mycompany.com
    /C=AU/ST=VIC/L=MELBOURNE/O=MyCompany/OU=ITS/CN=nonprod-api.mycompany.com

    方法 2:檢查 TCP/IP 封包中的 Certificate Request 封包,後端伺服器會要求用戶端傳送憑證:

    在上述 TCP/IP 封包範例中,Certificate Request封包是訊息 #7。請參閱「Distinguished Names」(識別名稱) 一節,其中包含後端伺服器的可接受憑證授權單位。

    alt_text

  5. 確認步驟 3 中取得的憑證授權單位,是否與步驟 4 中取得的後端伺服器接受的簽發者或憑證授權單位清單相符。如果兩者不符,訊息處理器就不會將用戶端憑證傳送至後端伺服器。

    在上述範例中,您會發現訊息處理器的 Keystore 中,用戶端葉子憑證的簽發者與後端伺服器的可接受憑證授權單位不符。因此,訊息處理器不會將用戶端憑證傳送至後端伺服器。這會導致 SSL 交握失敗,後端伺服器會傳送「Fatal alert: bad_certificate」訊息。

解析度

  1. 請確認發行者/憑證授權單位與用戶端葉子憑證 (鏈結中的第一個憑證) 的發行者/憑證授權單位相符的憑證,已儲存在後端伺服器的信任儲存區中。
  2. 在本 Playbook 所述的範例中,我們將發行者為 "issuer" : "CN=MyCompany Test SHA2 CA G2, DC=testcore, DC=test, DC=dir, DC=mycompany, DC=com" 的憑證新增至後端伺服器的 Truststore,解決了這個問題。

如果問題仍未解決,請參閱「必須收集的診斷資訊」。

必須收集診斷資訊

如果按照上述指示操作後問題仍未解決,請收集下列診斷資訊。請聯絡 Apigee Edge 支援團隊並提供下列資訊:

  1. 如果您是公有雲使用者,請提供下列資訊:
    1. 機構名稱
    2. 環境名稱
    3. API Proxy 名稱
    4. 完成 curl 指令,重現錯誤
    5. 顯示錯誤的追蹤記錄檔
    6. 在後端伺服器上擷取的 TCP/IP 封包
  2. 如果您是 Private Cloud 使用者,請提供下列資訊:
    1. 出現的完整錯誤訊息
    2. API Proxy 套裝組合
    3. 顯示錯誤的追蹤記錄檔
    4. 訊息處理器記錄 /opt/apigee/var/log/edge-message-processor/logs/system.log
    5. 在後端伺服器或訊息處理器上擷取的 TCP/IP 封包。
    6. Get cert for keystore API 的輸出內容。
  3. 您已嘗試使用本 Playbook 中的哪些章節,以及任何其他有助於我們加快解決問題的洞察資訊。