通过 HTTPS 下载网站失败

Downloading website over HTTPS fails

下面是不起作用的例子:

public class Temp {
  public static void main(String[] args) throws Exception {
    new URL("https://float.software").openConnection().getInputStream();
  }
}

在我的浏览器中转到 https://float.software/ 就可以了。但是 java 抛出这个异常:

Exception in thread "main" 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 sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1937)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1478)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:212)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:957)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:892)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1050)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1363)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1391)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1375)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:563)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1512)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1440)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
    at fb.Temp.main(Temp.java:8)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
    at sun.security.validator.Validator.validate(Validator.java:260)
    at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1460)
    ... 13 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:145)
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
    ... 19 more

当我查看其他答案时,他们都在谈论安装证书。我认为我不必安装证书。这不是自签名的。它使用真正的证书颁发机构。 Python 下载这个 URL 就好了。 chrome 也是如此。 Java 没有。

编辑:对于那些说 Comodo 是尚未添加到 java 的较新权限的人,它已作为根添加到 Java 5。请参阅 http://forums.comodo.com/comodo-cleaning-essentials-killswitch-autoruns-cce/how-do-i-unblock-a-processexecutable-t17086.0.html

如果您使用的是旧版本 Java,您可能需要更新您的 cacerts 文件。 float.software 的 SSL 证书由 "COMODO RSA Certification Authority" 签署,自 2014 年 2 月起才有效。您的 Java 安装时间可能早于此。

此外,请注意 Python。并非所有 HTTPS 实现都会验证远程证书。

如果您查看此域的 SSLLabs report,您将看到两个可能的证书路径:

  • 第一个(较短的)结束期望 "COMODO RSA Certification Authority" 被客户端信任。虽然这个 CA 从 2010 年开始有效,但直到最近(大约半年)才包含在 Firefox 等浏览器中,请参阅 https://bugzilla.mozilla.org/show_bug.cgi?id=1062589。您不会在大多数浏览器之外发现此 CA 是受信任的,即在 Java 或类似的信任库中(Java 不使用系统信任库)。
  • 另一个较长的证书路径需要受信任的根 CA "AddTrust External CA Root "。此 CA 已包含在浏览器和 OS 中多年,并且可能也在 Java 信任库中。不幸的是,要验证此证书路径,需要服务器未提供的附加链证书(在 SSLLabs 报告中标记为 "Extra Download")。

由于 Java 不包含第一条路径中的 Root-CA,因此无法对其进行验证。由于缺少链证书,它无法验证第二条路径。这意味着验证失败。

Python downloads this URL just fine. So does chrome.

Chrome 有较新的根 CA。此外 Chrome 尝试下载丢失的链证书,而 Java、Python 等则不会。

Python 最近才开始默认验证证书,即从 2.7.9+ 和 3.4.3+ 开始。所有以前的(这意味着大多数安装的)版本都没有验证证书(使用请求库时除外)所以它只是工作(典型问题:没有人注意到这是不安全的,因为它似乎工作)。参见 https://www.python.org/dev/peps/pep-0476/