我尝试通过 java 内置功能建立 https 连接 (HttpURLConnection
)。但我得到这个异常(exception):
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
...
...
我的证书链是:
根证书 -> 中间证书 -> Web 服务器证书
根据“路径发现”的含义,使用的证书是正确的。信任 anchor 是根证书,它是在我系统上的 java keystore 中导入的。中间证书不是......但是
- 中间证书由我信任的根签名 - 所以我也信任中间证书。
- Web 服务器证书使用我信任的中间证书签名(第 1 点)
所以验证必须成功通过?我是不是弄错了什么?
我在某处读到这个:
Browsers can do auto-discovery, server to server doesn't.
但缺少此功能是非常基本的。有明确的方法吗 auto-discovery ?
** 更新
是的,这就是问题所在,GPI。我很困惑,因为浏览器可以验证服务器证书,但 Java 应用程序不能。 该行为的原因是:
- 服务器只发送最终证书,而不是整个证书 链;
- 证书是最近购买的,并且是用相对签名的 新的中级证书;
- 浏览器有相对最新的证书列表 包括中级证书;
- java 有相关的非最新证书列表,并且 中级证书不在里面。
- 浏览器通过中间层验证最终证书 证书 java 无法检查证书链,因为:1。 链条未发送; 2.最终证书的签署人( 中间一个)不是信任 anchor 。
解决方案可能是:
- 服务器返回整个证书链
- 要添加到 java 信任库中的中间证书
最佳答案
我相信您已经检查了您的链,因此我们可以理所当然地认为 RootCert 签署了 IntermediateCert 和 IntermediateCert 签署了 ServerCert,具有有效的 X500 名称链和所有...
也就是说,您的逻辑是有效的,即信任 RootCert 就足够了,但不要忘记,为了构建路径,您的客户端必须拥有所有 路径中的证书。
在您的情况下,如果您只信任根证书,则由服务器公布证书链的其余部分(中间和最终)。如果没有人“给”HTTP 客户端中间证书,那么客户端将失败,因为从 Root 到 Server 而不知道 Intermediate 不是可能。
您实际上可以通过使用 -Djavax.net.debug=all
选项启动您的客户端来查看您的服务器证书链是什么。如果链的长度为 1,则您的服务器仅公布最终证书,客户端无法猜测中间证书的存在。
(也可以使用浏览器检查并要求查看服务器证书,但您应该注意,浏览器会向您显示直到信任 anchor 的整个路径,因此如果您想推断服务器链是什么,您必须从该路径中删除浏览器的 anchor )。
在生产服务上,您应该引用您的证书提供商的网站以了解什么被视为根证书(它可能不是最高级别的证书)。这个有效的根应该是您客户端的信任 anchor ,并且任何服务器都应该至少通告链中从路径中最后一个证书(其公用名称是服务器的 DNS 名称)向上的所有其他证书。
关于java - Java 中的证书路径发现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15974516/