SSL 握手失败 - 客户端证书无效

您正在查看 Apigee Edge 文档。
转到 Apigee X 文档
info

问题

客户端应用收到 HTTP 状态代码 503,并显示消息 "Service Unavailable" 作为对 API 请求的响应。在界面跟踪记录中,您会观察到,对于失败的 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"
    }
 }
}

对于消息处理器日志 /opt/apigee/var/log/edge-message-processor/system.log 中的特定 API 请求 ,Private Cloud 用户会看到以下错误:

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 Private 和 Public Cloud 用户
证书授权机构不匹配 消息处理器的密钥库中叶证书(证书链中的第一个证书)的证书授权机构 与后端服务器接受的任何证书授权机构都不匹配。 Edge Private 和 Public Cloud 用户

常见诊断步骤

  1. 在 Edge 界面中启用跟踪记录,进行 API 调用并重现问题。
  2. 在界面跟踪记录结果中,浏览每个阶段并确定错误发生的位置。 错误应该发生在目标请求流中。
  3. 检查显示错误的流,您应该会看到如下示例跟踪记录中所示的错误:

    alt_text

  4. 如上图所示,error.cause "Received fatal alert: bad_certificate"
  5. 如果您是 Private Cloud 用户,请按照以下说明操作:
    1. 您可以通过确定跟踪记录中由 AX 指示的阶段中的错误标头“X-Apigee.Message-ID”的值,获取失败的 API 请求的消息 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. 如果您是 Private Cloud 用户,则可以在后端服务器或消息处理器上捕获 TCP/IP 数据包。 最好在后端服务器上捕获这些数据包,因为这些数据包会在后端服务器上解密。
    2. 如果您是 Public Cloud 用户,则在后端服务器上捕获 TCP/IP 数据包。
    3. 确定要在何处捕获 TCP/IP 数据包后,请使用以下 tcpdump 命令捕获 TCP/IP 数据包。
    4. tcpdump -i any -s 0 host <IP address> -w <File name>

      如果您要在消息处理器上捕获 TCP/IP 数据包,请在 `tcpdump`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. 消息处理器完成证书验证,并确认后端服务器的 ServerHello 消息在消息 #8 中。
  5. 消息处理器在消息 #9 中将其证书发送到后端服务器。
  6. 后端服务器在消息 #11 中确认收到了消息处理器的 证书。
  7. 但是,它会立即向 消息处理器发送致命提醒:证书错误 (消息 #12)。这表明消息处理器发送的证书有误,因此后端服务器上的证书验证失败。因此,SSL 握手失败,连接 将关闭。


    alt_text

  8. 现在,我们来看看消息 #9,检查消息处理器发送的证书的内容:


    alt_text

  9. 您会注意到,后端服务器未从客户端收到任何证书 (证书长度:0)。因此,后端服务器会发送致命提醒:证书错误。
  10. 通常,当客户端(即消息处理器,一个基于 Java 的进程)出现以下情况时,会发生这种情况:
    1. 其密钥库中没有任何客户端证书;或者
    2. 无法发送客户端证书。如果它找不到由后端服务器接受的证书授权机构之一颁发的证书,则可能会发生这种情况。也就是说,如果客户端的叶证书 (即链中的第一个证书)的证书授权机构与后端服务器 接受的任何证书授权机构都不匹配,则消息处理器不会发送该证书。

接下来,我们分别了解一下这些原因。

原因:无客户端证书

诊断

如果目标端点的 SSL 信息部分 或目标端点中使用的目标服务器中指定的密钥库中没有证书, 则会导致此错误。

请按照以下步骤确定是否是此原因:

  1. 按照以下步骤确定特定 API 代理的目标端点或目标服务器中使用的密钥库:
    1. 从目标端点或目标服务器的 SSLInfo 部分中的 Keystore 元素获取密钥库引用名称。

      我们来看一下目标端点配置中的示例 SSLInfo 部分:

      <SSLInfo>
        <Enabled>true</Enabled>
        <ClientAuthEnabled>true</ClientAuthEnabled>
        <KeyStore>ref://myKeystoreRef</KeyStore>
        <KeyAlias>myKey</KeyAlias>
        <TrustStore>ref://myTrustStoreRef</TrustStore>
      </SSLInfo>
    2. 在上面的示例中,密钥库引用名称为“myKeystoreRef”。
    3. 前往 Edge 界面,然后依次选择 API 代理 -> 环境配置

      选择引用 标签页,然后搜索密钥库引用名称。 记下特定密钥库引用的引用 列中的名称。 这将是您的密钥库名称。


      alt_text

    4. 在上面的示例中,您会注意到 myKeystoreRef 引用了“myKeystore”。因此,密钥库名称为 myKeystore。
  2. 检查此密钥库是否包含证书,可以使用 Edge 界面或 List certs for keystore API
  3. 如果密钥库确实包含证书,请转到 原因:证书授权机构不匹配
  4. 如果密钥库不包含任何证书,则这就是 消息处理器未发送客户端证书的原因。

解决方案

  1. 确保将正确且完整的客户端证书链上传到消息处理器中的特定密钥库。

原因:证书授权机构不匹配

通常,当服务器请求客户端发送其证书时,它 会指明一组接受的颁发者或证书授权机构。 如果消息处理器的密钥库中叶证书(即证书链中的第一个 证书)的颁发者/证书授权机构与后端服务器接受的任何证书授权机构都不匹配,则消息处理器(这是一个 基于 Java 的进程)不会将证书发送到后端服务器。

请按照以下步骤确认是否是这种情况:

  1. List certs for 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" 的部分,如下所示:

    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 步中获得的后端服务器接受的颁发者或证书授权机构列表匹配。 如果不匹配,则消息处理器不会将客户端证书 发送到后端服务器。

    在上面的示例中,您会注意到,消息处理器的密钥库中客户端的叶证书的颁发者 与后端服务器的 接受的证书授权机构 都不匹配。因此,消息处理器不会 将客户端证书发送到后端服务器。 这会导致 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" 的证书已添加到后端服务器的信任库中,以解决此问题。

如果问题仍然存在,请前往必须收集的诊断信息

必须收集的诊断信息

如果按照上述说明操作后问题仍然存在, 请收集以下诊断信息。与 Apigee Edge 支持团队联系并分享这些信息:

  1. 如果您是 Public Cloud 用户,请提供以下信息:
    1. 组织名称
    2. 环境名称
    3. API 代理名称
    4. 用于重现错误的完整 curl 命令
    5. 显示错误的跟踪文件
    6. 在后端服务器上捕获的 TCP/IP 数据包
  2. 如果您是 Private Cloud 用户,请提供以下信息:
    1. 观察到的完整错误消息
    2. API 代理软件包
    3. 显示错误的跟踪文件
    4. 消息处理器日志 /opt/apigee/var/log/edge-message-processor/logs/system.log
    5. 在后端服务器或消息处理器上捕获的 TCP/IP 数据包。
    6. Get cert for keystore API 的输出。
  3. 有关您尝试过的此 Playbook 中的哪些部分以及任何其他 有助于我们快速解决此问题的见解的详细信息。