调试 javax.net.ssl.SSLHandshakeException:java.security.cert.CertPathValidatorException:找不到证书路径的信任锚

Posted

技术标签:

【中文标题】调试 javax.net.ssl.SSLHandshakeException:java.security.cert.CertPathValidatorException:找不到证书路径的信任锚【英文标题】:Debugging javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found 【发布时间】:2016-07-01 19:07:29 【问题描述】:

我正在尝试使用a WebSocket client(在 android 上)通过安全 wss 连接到 nginx 代理。

我有两台服务器,一台在 Intranet 中,一台在 AWS。

尝试连接到本地服务器时,连接失败并显示javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

连接到 AWS 上的服务器时,我得到一个 com.neovisionaries.ws.client.OpeningHandshakeException: The status code of the opening handshake response is not '101 Switching Protocols'. The status line is: HTTP/1.1 404 Not Found 这是意料之中的,因为 AWS 上的 NGINX 实例尚未配置为切换协议并连接到上游 websocket 服务器。这是在本地服务器上开发的。但这表明 TLS/SSL 正在 AWS 上运行,这对我来说已经足够了。

此外,与wss://echo.websocket.org 的连接成功并且消息得到回显。

现在,奇怪的是:AWS 上的 NGINX 实例在 docker 容器中运行,其代码库与本地机器上的相同。两者都是在 docker 容器中运行的 NGINX,卷的安装方式相同,配置文件非常相似。唯一不同的是域名和相关的 SSL 证书。

这些是 Comodo 的 DV 证书(Positive SSL)。我也尝试过 LetsEncrypt 证书,结果相同。

我不明白为什么这两个容器的行为如此不同,我也找不到理由责怪托管 docker 服务的服务器(都是 Ubuntu 14.04)

所以我想剖析 Android 客户端应用程序中的证书(及其链)。

如何打印证书链的内容?

Chrome 浏览器 javascript wss 与本地服务器的连接确实可以从 Android 设备和 PC 进行。

~~~~更新~~~~请不要回答下面的东西,因为我会把它分成一个单独的问题

好的,我在@CommonsWare 的帮助下取得了进展。这里有一些新问题,它们开始变得有趣了。

Intranet 中的服务器托管自签名证书作为包罗万象的 https 响应。其背后的逻辑是,如果来自 Internet 的某人通过 https://<IP-INTRANET-GATEWAY> 访问该 Intranet 的公共 IP 的 IP 地址,则会获得该虚拟证书。原因是它不应该返回 Intranet (https://intranet.example.com) 的证书。

具有不同证书的多个域名指向该IP地址https://intranet.example.com、https://testing.intranet.example.com,因此有多个SSL证书服务于该IP

在 NGINX 中是:

server       
  listen 443;
  server_name _;
  ssl on;
  ssl_certificate     /etc/nginx/self-signed-dummy.pem;
  ssl_certificate_key /etc/nginx/self-signed-dummy.key;
  location / 
    return 404;
    
  

server       
  listen 443;
  server_name intranet.example.com;
  ssl on;
  ssl_certificate     /etc/nginx/intranet.example.com.pem;
  ssl_certificate_key /etc/nginx/intranet.example.com.key;
  location / 
    ...
    
  

server       
  listen 443;
  server_name testing.intranet.example.com;
  ssl on;
  ssl_certificate     /etc/nginx/testing.intranet.example.com.pem;
  ssl_certificate_key /etc/nginx/testing.intranet.example.com.key;
  location / 
    ...
    
  

这与 AWS 上的服务器不同,它没有自签名证书。

知道为什么客户可以获得虚拟证书吗?

另外,我如何在没有异常的情况下转储证书,例如为了分析 AWS 服务器返回的证书(+链)?

使用带有 https 的普通 HttpURLConnection 可以正常工作。

【问题讨论】:

SSLHandshakeException 上的 getCause() 可能会返回 CertPathValidatorException。它有一个getCertPath() 方法,它返回一个CertPath,它有一个代表链的getCertificates() 方法。 好的,谢谢! “Internet Widgits Pty Ltd”,我想知道为什么会提供服务。你帮了我。输入答案,我会接受。 【参考方案1】:

SSLHandshakeException 上的getCause() 可能返回CertPathValidatorException,给定错误的文本。它有一个getCertPath() 方法,它返回一个CertPath,它有一个代表链的getCertificates() 方法。如果需要,这些证书应该能够转换为 X509Certificate 对象,从而为您提供有关证书的各种详细信息。

“Internet Widgits Pty Ltd”,我想知道为什么会提供服务

我不知道,但是在 Internet 上搜索该字符串听起来并不乐观。

【讨论】:

我已经更新了问题。我在 Intranet 上的 NGINX 服务器为所有与特定域不匹配的默认命中提供一个虚拟证书。就是那个证书。 @DanielF:“知道为什么客户可以获得虚拟证书吗?” -- 如果服务器除了 WebSocket 之外还提供普通页面,请查看普通 HTTPS 请求的行为(例如,使用 OkHttp3 或 HttpURLConnection)。如果成功,也许您需要为 WebSocket 设置做一些不同的事情。 “我怎样才能在没有异常的情况下转储证书”——我知道的唯一方法是实现你自己的X509TrustManager,这样你就可以获得证书链进行验证,但这很痛苦。 谢谢。我刚刚通过用有问题的域替换该虚拟证书进行检查,它可以工作。显然这是一种解决方法,但它是一个很好的起点。我正在将问题拆分为一个新问题,并将测试您的建议。我不热衷于实现自定义 X509TrustManager,因为事情可能会进一步恶化(即在几年内对证书进行更改)。奇怪的是,WebViews 对此没有任何问题。这可能是 WebSocket 库的实现问题,这就是为什么我接下来会检查您的 https 建议 你知道我如何在不进入SSLHandshakeException 的情况下获取getCertPath() 的内容吗?就像在健康的应用程序流程中一样?您可以将此添加到您的答案中吗?我想检查 AWS 服务器的 CertPath。 仅供参考:HttpURLConnection 成功。

以上是关于调试 javax.net.ssl.SSLHandshakeException:java.security.cert.CertPathValidatorException:找不到证书路径的信任锚的主要内容,如果未能解决你的问题,请参考以下文章

ida调试呢没有出现本地调试

调试事件的发送流程

【调试】Android WebView调试

php异步调试和线上调试网站程序

软件调试的方方面面

为啥我要在不调试的情况下开始调试构建?