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

您正在查看的是 Apigee Edge 文档。
转到 Apigee X 文档
信息

问题

客户端应用收到 HTTP 状态代码 503 及消息"服务不可用",作为对 API 请求的响应。在界面跟踪记录中,您会发现,对于失败的 API 请求,目标请求流中的 error.cause Received fatal alert: bad_certificate

如果您有权访问消息处理器日志,则会发现 API 请求失败时显示的错误消息为 Received fatal alert: bad_certificate。在消息处理器和后端服务器之间的 SSL 握手过程中,采用双向 TLS 设置时会出现此错误。

错误消息

客户端应用会获得以下响应代码:

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 请求的以下错误:

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 界面中启用跟踪,进行 API 调用,并重现问题。
  2. 在界面轨迹结果中,浏览每个阶段并确定发生错误的位置。目标请求流中会出现此错误。
  3. 检查显示错误的流程,您应该会发现错误,如以下示例所示:

    alt_text

  4. 如上面的屏幕截图所示,error.cause error.cause
  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. 如果您是公有云用户,请捕获后端服务器上的 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 显示后端服务器确认来自消息处理器的客户端 Hello 消息。
  3. 后端服务器发送“服务器 Hello”消息及其证书,然后在消息 7 中请求客户端发送其证书。
  4. 消息处理器完成证书验证,并在消息 8 中确认后端服务器的 ServerHello 消息。
  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 代理 -> 环境配置

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


      alt_text

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

分辨率

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

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

通常,当服务器请求客户端发送其证书时,它会显示所接受的颁发者或证书授权机构集。 如果消息处理器的密钥库中的叶证书(即证书链中的第一个证书)的颁发者/证书授权机构与后端服务器接受的任何证书授权机构都不匹配,则消息处理器(这是一个基于 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>
    

    请参阅此命令输出中标题为“可接受的客户端证书 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。请参阅“可分辨名称”部分,其中包含后端服务器可接受的证书授权机构。

    alt_text

  5. 验证第 3 步中获得的证书授权机构是否与第 4 步获得的后端服务器接受的颁发者或证书授权机构列表一致。 如果不一致,消息处理器将不会将客户端证书发送到后端服务器。

    在上面的示例中,您可以注意到消息处理器的密钥库中客户端的叶证书颁发者与后端服务器的任何接受的证书授权机构都不匹配。因此,消息处理器不会将客户端证书发送到后端服务器。 这会导致 SSL 握手失败,且后端服务器会发送“Fatal alert: bad_certificate”消息。

分辨率

  1. 确保由与客户端叶证书(链中的第一个证书)的颁发者/证书授权机构匹配的证书存储在后端服务器的 Truststore 中。
  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 代理名称
    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. 详细说明您尝试了本手册中的哪些部分,以及其他任何有助于我们快速解决此问题的数据分析。