解决javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed 错误?

612

编辑:我在我的博客上尝试以更加整洁的方式格式化了问题和已接受答案。

以下是原始问题。

我遇到了这个错误:

详细信息 sun.security.validator.ValidatorException: PKIX路径构建失败:
sun.security.provider.certpath.SunCertPathBuilderException: 无法找到到请求目标的有效认证路径

原因 javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX路径构建失败: sun.security.provider.certpath.SunCertPathBuilderException: 无法找到到请求目标的有效认证路径

我正在使用 Tomcat 6 作为 web 服务器。在同一台机器上,我安装了两个 HTTPS Web 应用程序,它们分别位于不同的 Tomcat 上,使用不同的端口。假设 App1 (端口 8443)连接到 App2 (端口 443)。当 App1 连接到 App2 时,我会收到上述错误。我知道这是一个非常常见的错误,因此在不同的论坛和站点上找到了许多解决方案。我在两个 Tomcats 的 server.xml 中都有以下条目:

keystoreFile="c:/.keystore" 
keystorePass="changeit"

每个网站都说同样的原因,即由app2提供的证书不在app1 jvm的受信任存储中。当我尝试在IE浏览器中访问相同的URL时,它可以正常工作(需要警告:“这个网站的安全证书存在问题。在此我选择继续访问此网站)。但是当Java客户端尝试访问相同的URL时(在我的情况下),我会收到上述错误。因此,为了将其放入受信任的存储中,我尝试了以下三个选项:

选项1

System.setProperty("javax.net.ssl.trustStore", "C:/.keystore");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

选项2

在环境变量中设置如下内容

CATALINA_OPTS -- param name
-Djavax.net.ssl.trustStore=C:\.keystore -Djavax.net.ssl.trustStorePassword=changeit ---param value

选项 3

在环境变量中设置以下内容。

JAVA_OPTS -- param name
-Djavax.net.ssl.trustStore=C:\.keystore -Djavax.net.ssl.trustStorePassword=changeit ---param value

结果

但是没有任何一种方法起作用。

最后起作用的方法是执行Pascal Thivent在如何处理Apache HttpClient中无效的SSL证书?中建议的Java方法,即执行InstallCert程序。

但是这种方法适用于开发环境设置,而我不能在生产环境中使用它。

我想知道为什么在App1中将相同的值在App2服务器的server.xml中和信任存储中提到了三种上述方法都不起作用,同时在App1程序中通过以下方式设置:

System.setProperty("javax.net.ssl.trustStore", "C:/.keystore") and System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

有关更多信息,以下是我建立连接的方法:

URL url = new URL(urlStr);

URLConnection conn = url.openConnection();

if (conn instanceof HttpsURLConnection) {

  HttpsURLConnection conn1 = (HttpsURLConnection) url.openConnection();
  
  conn1.setHostnameVerifier(new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
      return true;
    }
  });

  reply.load(conn1.getInputStream());

可能是 HttpClient 和 SSL 的重复问题。 - user207421
有趣的是,我在通信两个没有SSL问题的集群服务器之间时遇到了这个错误。一旦我在我的RHEL服务器上正确设置了“domainname”,问题就解决了。希望能对某些人有所帮助。 - DavidGamba
另外要检查的一件事是你是否拥有最新版本的Java - 我之前遇到类似的错误就是因为这个原因。 - Redzarf
https://dev59.com/s3E85IYBdhLWcg3wMQlm - 这也是相关的,提供了非常好的答案。 - Siddhartha
首先将您的crt文件导入{JAVA_HOME}/jre/security/cacerts,如果您仍然面对此异常,请更改您的jdk版本。例如从jdk1.8.0_17更改为jdk1.8.0_231。 - Tohid Makari
我在Windows上遇到了这个问题,我在这里找到了答案:https://dev59.com/ulgR5IYBdhLWcg3wf9VZ - David Marciel
38个回答

4

在我尝试连接由NGINX反向代理处理SSL的进程时,也出现了这个错误。

问题是证书没有将整个证书链连接起来。当我添加中间证书时,问题得到解决。

希望这可以帮到你。


这看起来像是我所遇到的问题。您能否解释一下您如何添加中间证书以及在哪里添加?我正在使用httpd反向代理而不是NGINX。 - Asaf Magen
这对我很有帮助,因为我正在使用httpd:https://access.redhat.com/solutions/43575 - Asaf Magen
使用nginx进行SSL配置时,只需要使用.key和.pem文件。首先,您需要将.crt转换为.pem(简单地执行:cp yourfile.crt yourfile.pem),然后对于SSL证书链:将.cer文件附加到.pem文件的末尾(cat yourfile.cer >> yourfile.pem)。 - The Anh Nguyen

4

如果您使用的是JDK 11,则文件夹中不再有JRE。证书的位置为jdk-11.0.11/lib/security/cacerts。


4
为了安全起见,在我们的实现中不应使用自签名证书。然而,当涉及到开发时,我们经常需要使用带有自签名证书的试用环境。我尝试在我的代码中通过编程方式解决这个问题,但失败了。然而,将证书添加到jre信任库中可以解决我的问题。请按以下步骤操作:
1. 下载站点证书, - 使用Chrome - 使用Firefox 2. 将证书(例如cert_file.cer)复制到目录$JAVA_HOME\Jre\Lib\Security中。
3. 以管理员身份打开CMD,并将目录更改为$JAVA_HOME\Jre\Lib\Security。
4. 使用以下命令将证书导入到信任库中:
keytool -import -alias ca -file cert_file.cer -keystore cacerts -storepass changeit
如果你遇到了“keytool不可识别”的错误,请参考这里
像下面这样输入yes

信任此证书:[是]

5.现在尝试使用Java运行代码或以编程方式访问URL。

更新

如果您的应用服务器是jboss,请尝试添加以下系统属性。

System.setProperty("org.jboss.security.ignoreHttpsHost","true");

希望这有所帮助!

3

我编写了一个小的Win32(在WinXP 32位上测试过)愚蠢的cmd(命令行)脚本,该脚本查找程序文件中的所有Java版本并将证书添加到它们中。密码需要是默认的“changeit”或在脚本中自己更改 :-)

@echo off

for /F  %%d in ('dir /B %ProgramFiles%\java') do (
    %ProgramFiles%\Java\%%d\bin\keytool.exe -import -noprompt -trustcacerts -file some-exported-cert-saved-as.crt -keystore %ProgramFiles%\Java\%%d\lib\security\cacerts -storepass changeit
)

pause

3
这似乎是记录闻名的PKIX错误信息的另一个可能原因的好地方。花了很长时间查看密钥库和信任库的内容以及各种Java安装配置后,我意识到我的问题在于……打错了一个字。该拼写错误意味着我也将密钥库用作信任库。由于我的公司根CA在密钥库中未被定义为独立证书,而只是作为证书链的一部分,并且在其他任何地方(例如cacerts)都没有定义,因此我一直收到PKIX错误。
在经历了一次失败的发布(这是生产配置,在其他地方却没有问题)和两天的苦苦思索后,我终于发现了打字错误,现在一切都好了。
希望这能帮助有需要的人。

2

对我来说,这篇帖子中被认可的解决方案并没有起作用: https://dev59.com/s2kw5IYBdhLWcg3w1d8m#9619478

相反,我通过将证书导入到我的受信任证书中来解决了问题。

步骤:

  1. 进入证书无法工作的URL(例如:https://localhost:8443/yourpath)。
  2. 按照上述提到的方法导出证书。
  3. 在您的Windows计算机上打开:管理计算机证书
  4. 前往受信任的根证书颁发机构 -> 证书
  5. 在此处导入您的your_certification_name.cer文件。

2

对于MacOS X,以下是对我有效的确切命令,其中我不得不在“importcert”选项中尝试双破折号,这很有效:

最初的回答:

sudo keytool -–importcert -file /PathTo/YourCertFileDownloadedFromBrowserLockIcon.crt -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/security/cacerts -alias "Cert" -storepass changeit

1
我想插一嘴,因为我有一个QEMU环境,在那里我必须用Java下载文件。结果发现QEMU中的/etc/ssl/certs/java/cacerts存在问题,因为它与主机环境中的/etc/ssl/certs/java/cacerts不匹配。主机环境位于公司代理后面,因此java cacerts是一个定制版本。
如果您正在使用QEMU环境,请确保主机系统可以先访问文件。例如,您可以先在主机上尝试this script。如果脚本在主机上运行得很好,但在QEMU中无法运行,则您遇到了与我相同的问题。
要解决这个问题,我不得不在QEMU中备份原始文件,将主机环境中的文件复制到QEMU chroot jail中,然后Java就可以在QEMU中正常下载文件了。
更好的解决方案是将/etc挂载到QEMU环境中;但我不确定在此过程中是否会影响其他文件。所以我决定使用这个丑陋但易于操作的解决方法。

1

我也遇到了这个问题。

我尝试了几乎所有的方法,将SSL证书添加到.keystore中,但在Java1_6_x中仍然无法正常工作。 对我来说,如果我们开始使用更新版本的Java,如JVM Java1_8_x,它会有所帮助。


1
对我来说也是一样的。从Java 1.8.0_91更新到1.8.0_121解决了这个问题。我使用Apache HTTPClient时遇到了异常。 - Devabc
我仍然使用Oauth2身份验证存在此问题。 - Sofiane

1
我的建议: 在我的情况下,cacerts不是一个文件夹,而是一个文件,并且它存在于两个路径上。 发现后,将.jks文件复制到该文件上后错误消失了。
# locate cacerts    
/usr/java/jdk1.8.0_221-amd64/jre/lib/security/cacerts
/usr/java/jre1.8.0_221-amd64/lib/security/cacerts

备份完毕后,我将 .jks 文件复制过去。
cp /path_of_jks_file/file.jks /usr/java/jdk1.8.0_221-amd64/jre/lib/security/cacerts
cp /path_of_jks_file/file.jks /usr/java/jre1.8.0_221-amd64/lib/security/cacerts

注意:这个基本技巧解决了Genexus项目中的错误,尽管file.jks也在Tomcat的server.xml文件中。

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