1. 知识预备
要理解本文,需要掌握以下知识:
- https基本原理,包括证书分发和密钥协商等 [1];
- http代理和https代理的基本原理 [2];
- 常用的本地http代理工具,如Fiddler, BurpSuite;
- 懂Java语言,了解HttpClient的基本功能和API [3]。
2. 问题描述与分析
使用httpclient写程序访问https页面,中间需要通过本地的http/https代理,如下所示:
Java程序(基于httpclient) <——-> 代理工具 (如Fiddler) <———> Web服务器
当运行Java程序后,程序出现异常,提示:
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
从错误提示可以看到是无法验证网站的证书,找不到证书验证链。为了解决问题,将代理工具生成的证书作为可信根证书导入系统证书库,但是问题依然存在。在阅读了资料[1]之后才恍然大悟,原来Java的证书验证系统是独立于操作系统和浏览器的,而是使用JRE中证书库,所有必须把代理工具的证书加入到JRE的证书库中。
3. 问题解决
在Windows系统中,JRE证书库路劲为Program Files\Java\jdk1.x.x\jre\lib\security\cacerts。Java使用Keytool管理证书,其路径为Program Files\Java\jdk1.8.0_144\bin\keytool.exe。所以,需要的操作是使用keytool将代理工具的证书加入到cacerts中。在[2]对上述过程有详细的原理解释。打开命令行,进行如下操作:
Step 1. cd到keytool.exe所在目录;
Step 2. 执行下述命令(命令参数说明参考keytool帮助文件)
>keytool -import -alias FIDDLER -keystore ../lib/security/cacerts -file 代理工具证书路径\证书文件名
如果提示输入口令,java默认口令是changeit,最后输入y信任此证书。
Step 3. 确认证书已经被导入可信根证书库
keytool -list -keystore ../lib/security/cacerts
参考资料
[1] Java 和 HTTP 的那些事(四) HTTPS 和 证书
[2] https代理原理
[3] HttpClient 4.5