TLS/SSL 握手失败

您正在查看 Apigee Edge 文档。
查看 Apigee X 文档。

问题

当客户端和服务器无法使用 TLS/SSL 协议建立通信时,就会发生 TLS/SSL 握手失败。当 Apigee Edge 中发生此错误时,客户端应用会收到 HTTP 状态 503 以及消息 Service Unavailable。在发生 TLS/SSL 握手失败的任何 API 调用后,您都会看到此错误。

错误消息

HTTP/1.1 503 Service Unavailable

当 TLS/SSL 握手失败时,您还会看到以下错误消息:

Received fatal alert: handshake_failure

可能的原因

TLS(传输层安全协议,其前身是 SSL)是一种在 Web 服务器与 Web 客户端(例如浏览器或应用)之间建立加密链接的标准安全技术。握手是一个机制,可让 TLS/SSL 客户端和服务器建立一组能够与之通信的 Secret 密钥。在此过程中,客户端和服务器会:

  1. 同意要使用的协议版本。
  2. 选择要使用的加密算法。
  3. 通过交换和验证数字证书来进行身份验证。

如果 TLS/SSL 握手成功,则 TLS/SSL 客户端和服务器会安全地相互传输数据。否则,如果 TLS/SSL 握手失败,连接将终止,客户端会收到 503 Service Unavailable 错误。

TLS/SSL 握手失败的可能原因包括:

原因 说明 谁可以执行问题排查步骤
协议不匹配 服务器不支持该客户端使用的协议。 私有云和公有云用户
加密套件不匹配 服务器不支持客户端使用的加密套件。 私有云和公有云用户
证书不正确 客户端所使用的网址中的主机名与服务器端存储的证书中的主机名不匹配。 私有云和公有云用户
不完整或无效的证书链存储在客户端或服务器端。 私有云和公有云用户
客户端将错误或已过期的证书发送到服务器或从服务器发送到客户端。 私有云和公有云用户
已启用 SNI 的服务器 后端服务器启用了服务器名称指示 (SNI);但是,客户端无法与 SNI 服务器通信。 仅限私有云用户

协议不匹配

如果服务器在传入(北向)或传出(南向)连接不支持客户端使用的协议,则会出现 TLS/SSL 握手失败。另请参阅 了解北向和南向连接

诊断

  1. 确定错误是在 northbound 连接还是 southbound 连接上发生的。如需进一步了解如何做出此类决定,请参阅 确定问题的根源
  2. 运行 tcpdump 实用程序以收集更多信息:
    • 如果您是私有云用户,则可以在相关客户端或服务器上收集 tcpdump 数据。客户端可以是客户端应用(针对传入或北向连接),也可以是消息处理器(针对传出或南向连接)。根据您在第 1 步中确定的结果,服务器可以是边缘路由器(针对传入或北向连接),也可以是后端服务器(针对传出或南向连接)。
    • 如果您是公有云用户,则只能在客户端应用(针对传入或北向连接)或后端服务器(针对传出或南向连接)收集 tcpdump 数据,因为您无权访问边缘路由器或消息处理器。
    tcpdump -i any -s 0 host IP address -w File name
    
    请参阅 tcpdump 数据,详细了解如何使用 tcpdump 命令。
  3. 使用 Wireshark 工具或类似工具分析 tcpdump 数据。
  4. 以下是使用 Wireshark 的 tcpdump 的示例分析:
    • 在此示例中,消息处理器和后端服务器(传出或南向连接)之间会发生 TLS/SSL 握手失败。
    • 下面的 tcpdump 输出中的消息 4 表明消息处理器(来源)向后端服务器(目的地)发送了“客户端 Hello”消息。

    • 如果您选择 Client Hello 消息,系统会显示消息处理器正在使用 TLSv1.2 协议,如下所示:

    • 消息 5 显示后端服务器确认来自消息处理器的“Client Hello”消息。
    • 后端服务器立即将严重提醒:关闭通知发送至消息处理器(消息 6)。这意味着 TLS/SSL 握手失败,连接也将关闭。
    • 如果查看消息 6,可以看到 TLS/SSL 握手失败的原因在于后端服务器仅支持 TLSv1.0 协议,如下所示:

    • 由于消息处理器和后端服务器使用的协议不匹配,因此后端服务器发送了消息:严重提醒消息:关闭通知

分辨率

消息处理器默认使用 Java 8 运行,并会使用 TLSv1.2 协议。如果后端服务器不支持 TLSv1.2 协议,您可以执行以下任一步骤来解决此问题:

  1. 升级后端服务器以支持 TLSv1.2 协议。我们推荐您采用这种方法,因为协议 TLSv1.2 更安全。
  2. 如果您因某种原因无法立即升级后端服务器,则可以强制消息处理器使用 TLSv1.0 协议与后端服务器通信,具体步骤如下:
    1. 如果您未在代理的 TargetEndpoint 定义中指定目标服务器,请将 Protocol 元素设置为 TLSv1.0,如下所示:
      <TargetEndpoint name="default">
       …
       <HTTPTargetConnection>
         <SSLInfo>
             <Enabled>true</Enabled>
             <Protocols>
                 <Protocol>TLSv1.0</Protocol>
             </Protocols>
         </SSLInfo>
         <URL>https://myservice.com</URL>
       </HTTPTargetConnection>
       …
      </TargetEndpoint>
      
    2. 如果您为代理配置了 目标服务器,请使用 此管理 API 在特定目标服务器配置中将协议设置为 TLSv1.0。

加密不匹配

如果服务器在 Apigee Edge 中的传入(北向)或传出(南向)连接不支持客户端使用的加密套件算法,则您可能会看到 TLS/SSL 握手失败。另请参阅 了解北向和南向连接

诊断

  1. 确定错误是在 northbound 还是 southbound 连接上发生的。如需进一步了解如何做出此类决定,请参阅确定问题的根源
  2. 运行 tcpdump 实用程序以收集更多信息:
    • 如果您是私有云用户,则可以在相关客户端或服务器上收集 tcpdump 数据。客户端可以是客户端应用(针对传入或北向连接),也可以是消息处理器(针对传出或南向连接)。根据您在第 1 步中确定的结果,服务器可以是边缘路由器(针对传入或北向连接),也可以是后端服务器(针对传出或南向连接)。
    • 如果您是公有云用户,则只能在客户端应用(针对传入或北向连接)或后端服务器(针对传出或南向连接)收集 tcpdump 数据,因为您无权访问边缘路由器或消息处理器。
    tcpdump -i any -s 0 host IP address -w File name
    
    请参阅 tcpdump 数据,详细了解如何使用 tcpdump 命令。
  3. 使用 Wireshark 工具或您熟悉的任何其他工具分析 tcpdump 数据。
  4. 以下是使用 Wireshark 的 tcpdump 输出的示例分析:
    • 在此示例中,客户端应用与边缘路由器(北向连接)发生 TLS/SSL 握手失败。边缘路由器上已收集 tcpdump 输出。
    • 下面的 tcpdump 输出中的消息 4 显示客户端应用(来源)向边缘路由器(目的地)发送了“客户端 Hello”消息。

    • 选择 Client Hello 消息会显示客户端应用使用的是 TLSv1.2 协议。

    • 消息 5 显示边缘路由器确认来自客户端应用的“Client Hello”消息。
    • 边缘路由器会立即向客户端应用发送严重提醒:握手失败(消息 6)。这意味着 TLS/SSL 握手失败,连接也将关闭。
    • 通过查看第 6 封邮件,我们可以看到以下信息:
      • 边缘路由器支持 TLSv1.2 协议。这意味着,客户端应用与边缘路由器之间的协议一致。
      • 不过,边缘路由器仍会向客户端应用发送严重提醒:握手失败,如以下屏幕截图所示:

    • 错误可能是由以下某个问题导致的:
      • 客户端应用未使用边缘路由器支持的加密套件算法。
      • 边缘路由器已启用 SNI,但客户端应用未发送服务器名称。
    • tcpdump 输出中的消息 4 列出了客户端应用支持的加密套件算法,如下所示:

    • /opt/nginx/conf.d/0-default.conf 文件中列出了边缘路由器支持的加密套件算法列表。在此示例中,Edge 路由器仅支持高加密加密套件算法。
    • 客户端应用不使用任何高加密加密套件算法。 这种不匹配是 TLS/SSL 握手失败的原因。
    • 由于边缘路由器已启用 SNI,请向下滚动到 tcpdump 输出中的消息 4,并确认客户端应用是否正确发送了服务器名称,如下图所示:


    • 如果此名称有效,就可以推断出 TLS/SSL 握手失败是因为 Edge 路由器不支持客户端应用使用的加密套件算法。

分辨率

您必须确保客户端使用服务器支持的加密套件算法。如需解决上一部分“诊断”部分中所述的问题,请下载并安装 Java 加密扩展 (JCE) 软件包,并将其包含在 Java 安装中,以支持高加密加密套件算法。

证书不正确

如果您的密钥库/信任存储区中有证书不正确(无论是在 Apigee Edge 的传入(北向)还是传出(向南)连接中),则 TLS/SSL 握手失败。另请参阅 了解北向和南向连接

如果问题属于“北”,则您可能会看到不同的错误消息,具体取决于根本原因。

以下部分列出了错误消息示例以及诊断和解决此问题的步骤。

错误消息

根据 TLS/SSL 握手失败的原因,您可能会看到不同的错误消息。 以下是您在调用 API 代理时可能会看到的错误消息示例:

* SSL certificate problem: Invalid certificate chain
* Closing connection 0
curl: (60) SSL certificate problem: Invalid certificate chain
More details here: http://curl.haxx.se/docs/sslcerts.html

可能的原因

此问题的典型原因如下:

原因 说明 谁可以执行问题排查步骤
主机名不匹配 网址中使用的主机名与路由器密钥库中的证书不匹配。例如,如果网址中使用的主机名是 myorg.domain.com,而证书的 CN 中的主机名是 CN=something.domain.com.,则会出现不匹配的情况

Edge Private 和公有云用户
证书链不完整或不正确 证书链不完整或不正确。 仅限 Edge Private 和公有云用户
服务器或客户端发送的证书已过期或未知 服务器或客户端会在北方或南向连接发送过期或未知的证书。 Edge Private Cloud 和 Edge 公有云用户

主机名不匹配

诊断

  1. 记下以下 Edge Management API 调用返回的网址中使用的主机名:
    curl -v https://myorg.domain.com/v1/getinfo
    例如:
    curl -v https://api.enterprise.apigee.com/v1/getinfo
  2. 获取存储在特定密钥库中的证书所用的 CN。您可以使用以下 Edge Management API 获取证书的详细信息:
    1. 获取密钥库中的证书名称

      如果您是 私有云用户,请使用 Management API,如下所示:
      curl -v https://management-server-ip:port/v1/organizations/org-name/environments/env-name/keystores/keystore-name/certs
      如果您是公有云用户,请按如下方式使用 Management API:
      curl -v https://api.enterprise.apigee.com/v1/organizations/org-name/environments/env-name/keystores/keystore-name/certs
      
    2. 使用 Edge Management API 获取密钥库中证书的详细信息。

      如果您是私有云用户
      curl -v https://management-server-ip:port/v1/organizations/org-name/environments/env-name/keystores/keystore-name/certs/cert-name
      
      如果您是公有云用户
      curl -v https://api.enterprise.apigee.com/v1/organizations/org-name/environments/env-name/keystores/keystore-name/certs/cert-name
      

      证书示例:

      "certInfo": [
          {
            "basicConstraints": "CA:FALSE",
            "expiryDate": 1456258950000,
            "isValid": "No",
            "issuer": "SERIALNUMBER=07969287, CN=Go Daddy Secure Certification Authority, OU=http://certificates.godaddy.com/repository, O=\"GoDaddy.com, Inc.\", L=Scottsdale, ST=Arizona, C=US",
            "publicKey": "RSA Public Key, 2048 bits",
            "serialNumber": "07:bc:a7:39:03:f1:56",
            "sigAlgName": "SHA1withRSA",
            "subject": "CN=something.domain.com, OU=Domain Control Validated, O=something.domain.com",
            "validFrom": 1358287055000,
            "version": 3
          },
      

      主证书中的主题名称的 CN 为 something.domain.com.

      由于 API 请求网址中使用的主机名(请参阅上文中的第 1 步)与证书中的主题名称不一致,因此会出现 TLS/SSL 握手失败。

分辨率

此问题可通过以下两种方式之一得到解决:

  • 获取正文 CN 具有通配符证书的证书(如果您还没有该证书),然后将新的完整证书链上传到密钥库。例如:
    "subject": "CN=*.domain.com, OU=Domain Control Validated, O=*.domain.com",
  • 获取现有主题 CN 的证书(如果尚未获得),但请使用 your-org。将 your-domain 作为主题备用名称,然后将完整的证书链上传到密钥库。

参考编号

密钥库和信任存储区

证书链不完整或不正确

诊断

  1. 获取存储在特定密钥库中的证书所用的 CN。您可以使用以下 Edge Management API 获取证书的详细信息:
    1. 获取密钥库中的证书名称

      如果您是私有云用户
      curl -v https://management-server-ip:port/v1/organizations/org-name/environments/env-name/keystores/keystore-name/certs
      
      如果您是公有云用户
      curl -v https://api.enterprise.apigee.com/v1/organizations/org-name/environments/env-name/keystores/keystore-name/certs
      
    2. 获取密钥库中证书的详细信息

      如果您是私有云用户
      curl -v https://management-server-ip:port/v1/organizations/org-name/environments/env-name/keystores/keystore-name/certs/cert-name
      
      如果您是公有云用户
      curl -v https://api.enterprise.apigee.com/v1/organizations/org-name/environments/env-name/keystores/keystore-name/certs/cert-name
      
    3. 验证证书及其链,并验证其是否遵循 证书链的工作原理一文中提供的准则,以确保其有效且完整。如果密钥库中存储的证书链不完整或无效,您会看到 TLS/SSL 握手失败。
    4. 以下图表显示了一个示例证书,其中证书链无效,中间证书和根证书不匹配:
    5. 发放者与主题不匹配的中间证书和根证书示例


分辨率

  1. 获取包含完整有效证书链的证书(如果您还没有这样的证书)。
  2. 运行以下 openssl 命令,以验证证书链是否正确且完整:
    openssl verify -CAfile root-cert -untrusted intermediate-cert main-cert
  3. 将经过验证的证书链上传到密钥库。

服务器或客户端发送的证书已过期或未知

如果服务器/客户端在北端或南端连接发送了错误/过期的证书,另一端(服务器/客户端)会拒绝该证书,从而导致 TLS/SSL 握手失败。

诊断

  1. 确定错误是在 northbound 连接还是 southbound 连接上发生的。如需进一步了解如何做出此类决定,请参阅 确定问题的根源
  2. 运行 tcpdump 实用程序以收集更多信息:
    • 如果您是私有云用户,则可以在相关客户端或服务器上收集 tcpdump 数据。客户端可以是客户端应用(针对传入或北向连接),也可以是消息处理器(针对传出或南向连接)。根据您在第 1 步中确定的结果,服务器可以是边缘路由器(针对传入或北向连接),也可以是后端服务器(针对传出或南向连接)。
    • 如果您是公有云用户,则只能在客户端应用(针对传入或北向连接)或后端服务器(针对传出或南向连接)收集 tcpdump 数据,因为您无权访问边缘路由器或消息处理器。
    tcpdump -i any -s 0 host IP address -w File name
    
    请参阅 tcpdump 数据,详细了解如何使用 tcpdump 命令。
  3. 使用 Wireshark 或类似工具分析 tcpdump 数据。
  4. tcpdump 输出中,确定在验证步骤中拒绝证书的主机(客户端或服务器)。
  5. 您可以从 tcpdump 输出中检索另一端发送的证书,但前提是数据未加密。这有助于比较此证书是否与 Truststore 中提供的证书匹配。
  6. 查看示例 tcpdump,了解消息处理器和后端服务器之间的 SSL 通信。

    示例 tcpdump 中显示证书未知错误


    1. 消息处理器(客户端)将向客户端服务器(服务器)发送“客户端问候”来发送消息 #59。
    2. 后端服务器将“Server Hello”发送至消息 61 中的消息处理器。
    3. 它们会相互验证所用的协议和加密套件算法。
    4. 后端服务器将消息和服务器 Hello Done 消息发送至消息处理器,位于消息 #68 中。
    5. 消息处理器在消息 #70 中发送严重提醒“Description: Certificate Unknown”
    6. 查看消息 70 后,除了提醒消息之外,没有其他详细信息(如下所示):


    7. 查看消息 #68 以获取后端服务器发送的证书的详细信息,如下图所示:

    8. “证书”部分下提供了后端服务器的证书及其完整链,如上图所示。
  7. 如果发现路由器(北向)或消息处理器(南向)发现未知证书(如上例所示),请按以下步骤操作:
    1. 获取存储在特定信任证书中的证书及其链。(请参阅路由器的虚拟主机配置和消息处理器的目标端点配置。)您可以使用以下 API 来获取证书的详细信息:
      1. 在信任存储区中获取证书名称
        curl -v https://management-server-ip:port/v1/organizations/org-name/environments/env-name/keystores/truststore-name/certs
      2. 在可信存储区中获取证书的详细信息
        curl -v https://management-server-ip:port/v1/organizations/org-name/environments/env-name/keystores/truststore-name/certs/cert-name
    2. 检查存储在路由器(北向)或消息处理程序(南向)中的信任证书是否与客户端应用(北向)或目标服务器(南向)的密钥库中存储的证书,或者从 tcpdump 输出获取的证书是否一致。如果不匹配,则是 TLS/SSL 握手失败的原因。
  8. 如果客户端应用(北向)或目标服务器(南向)发现证书未知,请按以下步骤操作:
    1. 获取存储在特定密钥库中的证书的完整证书链。(请参阅消息处理程序的虚拟主机配置和消息处理器的目标端点配置。)您可以使用以下 API 来获取证书的详细信息:
      1. 在密钥库中获取证书名称
        curl -v https://management-server-ip:port/v1/organizations/org-name/environments/env-name/keystores/keystore-name/certs
      2. 在密钥库中获取证书的详细信息
        curl -v https://management-server-ip:port/v1/organizations/org-name/environments/env-name/keystores/keystore-name/certs/cert-name
        
    2. 检查存储在路由器(北向)或消息处理器(南向)的密钥库中的证书,是否与存储在客户端应用(北向)或目标服务器(南向)中的证书一致,或者从 tcpdump 输出获取的证书。如果不匹配,这就是 SSL 握手失败的原因。
  9. 如果发现服务器/客户端发送的证书已过期,接收方客户端/服务器会拒绝该证书,而您会在 tcpdump 中看到以下提醒消息:

    提醒(级别:严重;说明:证书已过期)

  10. 确认相应主机的密钥库中的证书已过期。

分辨率

要解决上例中指出的问题,请将有效的后端服务器的证书上传到消息处理器上的 Trustore。

下表总结了解决问题的步骤,具体取决于问题的原因。

原因 说明 分辨率
证书已过期 北边界
  • 存储在路由器密钥库上的证书已过期。
  • 存储在客户端应用密钥库中的证书已过期(双向 SSL)。
将新证书及其完整链上传到相应主机上的密钥库。
南边界
  • 存储在目标服务器的密钥库上的证书已过期。
  • 消息处理程序的密钥库中存储的证书已过期(双向 SSL)。
将新证书及其完整链上传到相应主机上的密钥库。
未知证书 北边界
  • 存储在客户端应用的信任库中的证书与路由器的证书不匹配。
  • 存储在路由器信任库上的证书与客户端应用的证书(双向 SSL)不匹配。
将有效证书上传到相应主机上的信任库。
南边界
  • 存储在目标服务器的信任库上的证书与消息处理器的证书不匹配。
  • 消息处理器信任库中存储的证书与目标服务器的证书(双向 SSL)不匹配。
将有效证书上传到相应主机上的信任库。

已启用 SNI 的服务器

如果客户端与已启用服务器名称指示 (SNI) 的服务器通信,但客户端未启用 SNI,就可能会发生 TLS/SSL 握手失败。这可能会发生在边缘的北方连接或南向连接。

首先,您需要确定正在使用的服务器的主机名和端口号,并检查其是否启用了 SNI。

识别已启用 SNI 的服务器

  1. 执行 openssl 命令并尝试连接到相关的服务器主机名(边缘路由器或后端服务器),而不传递服务器名称,如下所示:
    openssl s_client -connect hostname:port
    
    您可能会获得证书,有时可能会在 openssl 命令中看到握手失败情况,如下所示:
    CONNECTED(00000003)
    9362:error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-64.50.6/src/ssl/s23_clnt.c:593
    
  2. 执行 openssl 命令,并尝试通过传递服务器名称来连接到相关的服务器主机名(边缘路由器或后端服务器),如下所示:
    openssl s_client -connect hostname:port -servername hostname
    
  3. 如果您在步骤 1 中握手失败,或在步骤 1 和步骤 2 中获得了不同的证书,则表示指定的服务器已启用 SNI。

确定服务器已启用 SNI,您便可以按照以下步骤检查 TLS/SSL 握手失败是否是由客户端无法与 SNI 服务器通信造成的。

诊断

  1. 确定错误是在 northbound 连接还是 southbound 连接上发生的。如需进一步了解如何做出此类决定,请参阅 确定问题的根源
  2. 运行 tcpdump 实用程序以收集更多信息:
    • 如果您是私有云用户,则可以在相关客户端或服务器上收集 tcpdump 数据。客户端可以是客户端应用(针对传入或北向连接),也可以是消息处理器(针对传出或南向连接)。根据您在第 1 步中确定的结果,服务器可以是边缘路由器(针对传入或北向连接),也可以是后端服务器(针对传出或南向连接)。
    • 如果您是公有云用户,则只能在客户端应用(针对传入或北向连接)或后端服务器(针对传出或南向连接)收集 tcpdump 数据,因为您无权访问边缘路由器或消息处理器。
    tcpdump -i any -s 0 host IP address -w File name
    
    请参阅 tcpdump 数据,详细了解如何使用 tcpdump 命令。
  3. 使用 Wireshark 或类似工具分析 tcpdump 输出。
  4. 以下是使用 Wireshark 的 tcpdump 示例分析:
    1. 在此示例中,边缘消息处理器和后端服务器(南向连接)发生 TLS/SSL 握手失败问题。
    2. 下面的 tcpdump 输出中的消息 4 表明消息处理器(来源)向后端服务器(目的地)发送了“客户端 Hello”消息。

    3. 选择“Client Hello”消息表明消息处理器正在使用 TLSv1.2 协议。

    4. 消息 4 显示后端服务器确认来自消息处理器的“Client Hello”消息。
    5. 后端服务器立即向消息处理器发送严重提醒:握手故障(消息 5)。这意味着 TLS/SSL 握手失败,连接也将关闭。
    6. 查看第 6 条消息,发现以下信息
      • 后端服务器支持 TLSv1.2 协议。这意味着消息处理器和后端服务器之间匹配的协议。
      • 但是,后端服务器仍会向消息处理器发送严重提醒:握手故障,如下图所示:

    7. 此错误可能是由以下原因之一导致的:
      • 消息处理器未使用后端服务器支持的加密套件算法。
      • 后端服务器已启用 SNI,但客户端应用未发送服务器名称。
    8. 详细查看 tcpdump 输出中的消息 3 (Client Hello)。请注意,Extension: server_name 缺失,如下所示:

    9. 这将确认消息处理器没有将 server_name 发送到已启用 SNI 的后端服务器。
    10. 这是 TLS/SSL 握手失败的原因,以及后端服务器将严重提醒:握手失败发送到消息处理器的原因。
  5. 验证 system.properties 中的 jsse.enableSNIExtension property 是否在消息处理器上设置为 false,以确认消息处理器未启用,因此无法与启用 SNI 的服务器通信。

分辨率

通过执行以下步骤,消息处理器能够与已启用 SNI 的服务器通信:

  1. 创建 /opt/apigee/customer/application/message-processor.properties 文件(如果该文件尚不存在)。
  2. 将以下行添加到此文件中:conf_system_jsse.enableSNIExtension=true
  3. 将此文件的所有者选为 apigee:apigee
    chown apigee:apigee /opt/apigee/customer/application/message-processor.properties
  4. 重启消息处理器。
    /opt/apigee/apigee-service/bin/apigee-service message-processor restart
  5. 如果您有多个消息处理器,请针对所有消息处理器重复第 1 步到第 4 步。

如果您无法确定 TLS/SSL 握手失败的原因并解决问题,或者您需要更多帮助,请与 Apigee Edge 支持团队联系。提供该问题的完整详细信息以及 tcpdump 输出。