Java HTTPS连接SSL证书错误

6
我试图在Apache服务器上使用从StartSSL.com获得的SSL证书。与浏览器的连接良好,但是当我尝试使用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"
我不明白问题可能是什么,因为在浏览器中,我得到了带有绿色标签的SSL证书。

你可能没有将证书导入JVM中,请参见此处的详细信息。 - user4545399
在我的HTTPS库中,我有一个方便的方法trustAll(),以便接受所有服务器证书,包括自签名的证书。这对于某些客户端应用程序可能是可以接受的(但肯定不适用于主流浏览器!)安全威胁是一些中间人可能能够窃听甚至修改流量;但这是相当不可能的。它需要像NSA这样的资源;但我非常确定NSA无论如何都可以破解SSL :) - ZhongYu
@bayou.io 实际上,这种情况可能发生在许多其他地方,例如公共WiFi。 - Bruno
@Bruno - 问题在于证书篡改会立即被浏览器用户注意到。因此,如果我们的客户是低调的小鱼,我们可能可以不那么严格。 :) - ZhongYu
@bayou.io 这纯属无稽之谈。这就是为什么有很多移动应用程序是不安全的原因。 - Bruno
@Bruno,现实中是否存在针对SSL的中间人攻击?黑客有比MITM SSL更好的攻击向量可供利用。 - ZhongYu
5个回答

10

问题在于您的Java没有信任该证书。您需要将证书导入到Java的truststore中。

# Copy the certificate into the directory Java_home\Jre\Lib\Security
# Change your directory to Java_home\Jre\Lib\Security>
# Import the certificate to a trust store.
# Here's the import command:

keytool -import -alias ca -file somecert.cer -keystore cacerts -storepass changeit [Return]

Trust this certificate: [Yes]

changeit 是默认的信任存储库密码。

对于您导入到信任存储库中的每个证书,都需要提供一个新别名。

导入方法引用自此处


谢谢!如果我需要在Web上共享Java应用程序,那么我是否需要在该文件中插入密钥?顺便说一下,这就是StartSSL告诉我的:您必须将此站点设置为服务器的默认网站或为其使用专用IP地址。你怎么看? - I love coding
如果您想在网上共享Java应用程序,您还必须向用户提供SSL文件,但是它可以很容易地通过浏览器获得。好吧,SSL证书可以通过许多方式轻松创建,即使您使用专用IP地址,如果客户端的操作系统(Windows等)未导入您的证书,用户在进入您的网站时将在浏览器上看到警告。 - Sercan Ozdemir
另一件事是,使用Comodo的另一个SSL证书,Java应用程序可以在不进行任何额外修改的情况下正常工作。所以问题只与Java有关,对吧?有没有办法修改Apache来解决这个问题? - I love coding
你可以通过谷歌搜索如何在Java代码中绕过SSL,但这并不推荐。 - Sercan Ozdemir

1

此消息原因如下:

  1. Java运行环境(JRE)未将根证书授信作为其密钥库中的受信条目。
  2. 服务器未发送正确的证书链。

但是,情况2在您的情况下无效,因为浏览器能够构建证书链并验证证书。

因此,您需要获取根CA并将其放入JRE密钥库中作为受信条目。有很多资源可以记录“如何”进行此操作。其中之一是: https://access.redhat.com/documentation/en-US/Fuse_Message_Broker/5.3/html/Security_Guide/files/i379776.html

编辑1:由于您想共享Java应用程序,因此值得从已在Java版本的信任存储中受信任的根证书颁发机构获得证书。


谢谢!我还没有完全理解你的编辑。我应该怎么做才能在Java中修复这个问题? - I love coding
检查密钥库以获取已信任的CA根证书,并获取可以建立与之信任的服务器证书。或者您可以附带自己的受信任存储并指定一个命令行选项给Java来信任它。Djavax.net.ssl.trustStore=path/to/trustStore.jks - Khanna111

0

我建议使用基于Apache HTTP API构建的http-request

import org.junit.Test;

import static org.apache.http.HttpHeaders.ACCEPT;
import static org.apache.http.HttpStatus.SC_OK;
import static org.apache.http.entity.ContentType.APPLICATION_XML;
import static org.junit.Assert.assertEquals;

public class HttpRequestSSLTest {

private final HttpRequest<?> httpRequest = HttpRequestBuilder.createGet("https://mms.nw.ru/")
        .trustAllCertificates()
        .trustAllHosts()
        .addDefaultHeader(ACCEPT, APPLICATION_XML.getMimeType())
        .build();

@Test
public final void ignoreSSLAndHostsTest() throws Exception {

    assertEquals(SC_OK, httpRequest.execute().getStatusCode());
}

}


0

0

在SO上已经有一些类似的问题(比如这个:PKIX path building failed: unable to find valid certification path to requested target)。

通常的答案是你的Java客户端没有完成证书链所需的证书。

如果你需要正确的证书验证,那么你就必须找出证书链断裂的地方。如果你不需要(因为这是一个概念验证或开发沙盒等),那么你可以通过将证书添加到你与客户端使用的信任存储库来轻松解决此问题。

编辑:

至于为什么你的浏览器接受这个证书,很可能是因为你的浏览器具有你需要的证书链,或者你不小心告诉了你的浏览器信任该证书,即使它也无法验证该证书。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接