调试 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:找不到证书路径的信任锚的主要内容,如果未能解决你的问题,请参考以下文章