报错 | PKIX path building failed: ...SunCertPathBuilderException:unable to find valid certification...


01 问题场景

在Java中访问不受信任的HTTPS网站时,会提示报错信息:

PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

翻译成中文是:

PKIX路径构建失败:sun.security.provider.certpath.SunCertPathBuilderException:无法找到请求目标的有效认证路径。

这是由于JVM (Java Virtual Machine) 默认信任证书不包含该目标网站的SSL证书,导致无法建立有效的信任链。

02 分析问题

JVM自己维护有一个默认的信任证书,是一个没有后缀名的文件,文件名为cacerts,位于java安装路径下,例如:

C:\Program Files\Java\jdk-17\lib\security\cacerts

当Java访问https的网站时,通常不会出现这一异常,但有的https网站,因为种种原因,存在证书不受信任的问题,使用浏览器访问会出现如下界面:

在这里插入图片描述
Java访问这种网站就会导致报错。

03 三种解决方案

经过我的一番搜寻,网上一般有解决方案:

  1. 信任所有SSL证书
    参考:https://developer.aliyun.com/article/812846
    这种方案不失为一种解决办法,但除了存在安全问题,还会对代码造成侵入,且在框架项目中不易集成。

  2. 将不信任的证书加入到JVM默认信任证书
    参考:https://www.cnblogs.com/qixing/p/11883494.html
    这种方案避免了代码入侵的问题,但想到要对java自带的默认文件动手动脚,且加入的还是不受信任的证书,多少有些不合适,而且加入后以后可能会忘记复原,或是要备份原始默认文件。

  3. 单独为不信任的网站生成证书并在项目中指定
    这也是本文使用的方案,避免了上面两个方案的问题。

04 解决步骤

4.1 获取目标网站的SSL证书

因为我是访问自己的网站,我作为站长,有自己的证书,就不涉及获取目标网站证书的问题,如果需要获取,也不困难,直接在浏览器网页左上角点击图标,进行导出即可(可自行百度),导出后通常为.crt.pem文件。

4.2 创建一个Java信任库

创建一个Java信任库(Truststore),并将目标网站的SSL证书导入其中。使用java自带的keytool命令行工具来完成此操作。在cmd中使用命令:

keytool -import -keystore <信任库文件路径> -storepass <密码> -alias alias_for_certificate -file <证书文件路径>

该命令由4个部分组成:
keytool -import 用于导入的命令
-keystore <信任库文件路径> 希望生成的信任库文件,例如路径可以写:C:\Users\abc\Desktop\truststore.jks
-storepass <密码> 设置一个信任库密码
-alias alias_for_certificate -file <证书文件路径> 指定要导入的证书文件,例如可以写:C:\Users\abc\Desktop\cert.pem

4.3 告知JVM使用信任库

在你的Java应用程序中,告知JVM使用你刚刚创建的信任库。使JVM在运行时加载该信任库。
通过设置以下系统属性来实现:

System.setProperty("javax.net.ssl.trustStore", "<信任库文件路径>");	// 刚才填写的路径
System.setProperty("javax.net.ssl.trustStorePassword", "<密码>");	// 刚才填写的密码

这样问题就得到解决了!

05 补充

5.1 命令行提示没有keytool的解决

通常是因为jdk没有加入环境变量造成的,可以加入环境变量再试,或者直接cd到keytool所在的路径执行命令,例如我的keytool工具存放位置在:C:\Program Files\Java\jdk-17\bin目录下。

5.2 SpringBoot项目中如何指定信任库

将上面的两行代码放到SpringApplication.run()所在位置即可。示例:

@SpringBootApplication
public class DemoApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(DemoApplication.class, args);
        // 代码放到这里 可以使用相对路径
        System.setProperty("javax.net.ssl.trustStore", "C:\\Users\\truststore.jks");
        System.setProperty("javax.net.ssl.trustStorePassword", "123456");
    }
}

猜你喜欢

转载自blog.csdn.net/xuzhongyi103/article/details/131515281