背景
在Java代码里面访问https域名的url进行通信时,经常会遇到下面的一个异常:
Caused by: 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(Unknown Source)
at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
PKI介绍
公钥基础设施 PKI 《Public Key Infrastructure》, 是指使用公钥加密在网络上实现信息安全交换的设置。此设置依赖于通信参与方之间建立的信任。这种信任基于由称为证书颁发机构 (CA) 的中立且受信任的机构颁发的数字证书。
在Java的安全架构图中,如下所示:
在oracle官网,关于PKI编程,也有详细的介绍:https://docs.oracle.com/javase/10/security/java-pki-programmers-guide.htm#JSSEC-GUID-650D0D53-B617-4055-AFD3-AF5C2629CBBF
在Java里面PKI相关的操作类和接口称做:Java Certification Path API,用于处理认证路径,也称为认证链。如果证书路径满足某些验证规则,它可以用于安全地建立公钥到主题的映射,里面包括特定于算法的类,用于根据 PKIX 标准构建和验证 X.509 认证路径。 PKIX 标准由 IETF PKIX 工作组开发,如果验证失败,就会出现上面的我们熟悉的那个异常:
ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
X.509科普
X.509是密码学里公钥证书的格式标准。X.509证书已应用在包括TLS/SSL在内的众多网络协议里,同时它也用在很多非在线应用场景里,比如电子签名服务。X.509证书里含有公钥、身份信息(比如网络主机名,组织的名称或个体名称等)和签名信息(可以是证书签发机构CA的签名,也可以是自签名)
X.509还附带了证书吊销列表和用于从最终对证书进行签名的证书签发机构直到最终可信点为止的证书合法性验证算法。X.509是ITU-T标准化部门基于他们之前的ASN.1定义的一套证书标准
问题原因
到这里想必大家已经能够知道上述异常的出现的原因了,大多数时候,都是因为我们的客户端JDK里面,没有添加服务端签发的数字证书(涵盖证书,公钥等信息)而造成的,知道了原因之后,解决方法也很简单,使用JDK自带的一些证书管理工具,来把我们服务侧签发的证书加入JDK自带的cacerts信任库即可。
解决方法
首先,我们需要获取到服务端的证书文件,这个一般是xxx.pem或者xxx.cer文件,然后通过JDK自带的keytool工具来管理证书:
查看证书:
keytool -list -storepass changeit -keystore $JAVA_HOME/jre/lib/security/cacerts | grep my_ca -A 10
查看证书内容:
keytool -list -rfc -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass "changeit" | grep my_ca -A 10
添加证书:
keytool -import -noprompt -trustcacerts -file my.pem -alias my_ca -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit
注意:changeit是默认的密码
删除证书:
keytool -storepass changeit -delete -alias my_ca -keystore $JAVA_HOME/jre/lib/security/cacerts
通过上面几个命令,来把数字证书加入JDK信任库就可以解决证书校验失败问题。
总结
https提供了一种更加安全的通信协议,实际开发中会经常遇到在编程代码中操作访问https url相关的网络编程,这样不可避免的就会遇到文章中描述的问题,通过本文的学习,相信可以做到知其然并知其所以然了,关于https协议安全通信是一个复杂的话题,里面涉及知识颇多,后面有空再总结一篇https原理相关的文章