版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/catoop/article/details/80819638
问题
java使用httpclient或者restTemplate进行https请求时,出现如下异常:
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(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1514)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1026)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:961)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:396)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:355)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:359)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:381)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:111)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
at com.shanhy.zhongxin.module.a919.CreateOrderService.postXXX(CreateOrderService.java:275)
at com.shanhy.zhongxin.module.a919.CreateOrderService.main(CreateOrderService.java:203)
Caused by: 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.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1496)
... 21 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
... 27 more
解决方法
1、运行如下java文件(InstallCert.java),生成 jssecacerts 文件。
2、然后将生成的 jssecacerts 文件,拷贝到jdk中,目录位置:%JAVA_HOME%\jre\lib\security
(例如D:\Program Files\Java\jdk1.8.0_131\jre\lib\security)
package com.shanhy.zhongxin.module
import java.io.BufferedReader
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.InputStream
import java.io.InputStreamReader
import java.io.OutputStream
import java.security.KeyStore
import java.security.MessageDigest
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLException
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManager
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
public class InstallCert {
public static void main(String[] args) throws Exception {
// args = new String[]{"shanhytest.com.cn"}
String host
int port
char[] passphrase
if ((args.length == 1) || (args.length == 2)) {
String[] c = args[0].split(":")
host = c[0]
port = (c.length == 1) ? 443 : Integer.parseInt(c[1])
String p = (args.length == 1) ? "changeit" : args[1]
passphrase = p.toCharArray()
} else {
System.out.println("Usage: java InstallCert <host>[:port] [passphrase]")
return
}
File file = new File("jssecacerts")
if (file.isFile() == false) {
char SEP = File.separatorChar
File dir = new File(System.getProperty("java.home") + SEP + "lib" + SEP + "security")
file = new File(dir, "jssecacerts")
if (file.isFile() == false) {
file = new File(dir, "cacerts")
}
}
System.out.println("Loading KeyStore " + file + "...")
InputStream in = new FileInputStream(file)
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType())
ks.load(in, passphrase)
in.close()
SSLContext context = SSLContext.getInstance("TLS")
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
tmf.init(ks)
X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0]
SavingTrustManager tm = new SavingTrustManager(defaultTrustManager)
context.init(null, new TrustManager[] { tm }, null)
SSLSocketFactory factory = context.getSocketFactory()
System.out.println("Opening connection to " + host + ":" + port + "...")
SSLSocket socket = (SSLSocket) factory.createSocket(host, port)
socket.setSoTimeout(10000)
try {
System.out.println("Starting SSL handshake...")
socket.startHandshake()
socket.close()
System.out.println()
System.out.println("No errors, certificate is already trusted")
} catch (SSLException e) {
System.out.println()
e.printStackTrace(System.out)
}
X509Certificate[] chain = tm.chain
if (chain == null) {
System.out.println("Could not obtain server certificate chain")
return
}
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
System.out.println()
System.out.println("Server sent " + chain.length + " certificate(s):")
System.out.println()
MessageDigest sha1 = MessageDigest.getInstance("SHA1")
MessageDigest md5 = MessageDigest.getInstance("MD5")
for (int i = 0
X509Certificate cert = chain[i]
System.out.println(" " + (i + 1) + " Subject " + cert.getSubjectDN())
System.out.println(" Issuer " + cert.getIssuerDN())
sha1.update(cert.getEncoded())
System.out.println(" sha1 " + toHexString(sha1.digest()))
md5.update(cert.getEncoded())
System.out.println(" md5 " + toHexString(md5.digest()))
System.out.println()
}
System.out.println("Enter certificate to add to trusted keystore or 'q' to quit: [1]")
String line = reader.readLine().trim()
int k
try {
k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1
} catch (NumberFormatException e) {
System.out.println("KeyStore not changed")
return
}
X509Certificate cert = chain[k]
String alias = host + "-" + (k + 1)
ks.setCertificateEntry(alias, cert)
OutputStream out = new FileOutputStream("jssecacerts")
ks.store(out, passphrase)
out.close()
System.out.println()
System.out.println(cert)
System.out.println()
System.out.println("Added certificate to keystore 'jssecacerts' using alias '" + alias + "'")
}
private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray()
private static String toHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 3)
for (int b : bytes) {
b &= 0xff
sb.append(HEXDIGITS[b >> 4])
sb.append(HEXDIGITS[b & 15])
sb.append(' ')
}
return sb.toString()
}
private static class SavingTrustManager implements X509TrustManager {
private final X509TrustManager tm
private X509Certificate[] chain
SavingTrustManager(X509TrustManager tm) {
this.tm = tm
}
public X509Certificate[] getAcceptedIssuers() {
throw new UnsupportedOperationException()
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
throw new UnsupportedOperationException()
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
this.chain = chain
tm.checkServerTrusted(chain, authType)
}
}
}
注意事项
假设之前为了别的域名生成过 jssecacerts 文件,现在又要添加其他域名到 jdk 证书,此时如果你直接重新生成 jssecacerts 文件,然后覆盖到 %JAVA_HOME%\jre\lib\security 中。则之前的域名信息就会丢失。
基于这种情况,你需要按如下方法操作:
1、运行 InstallCert 生成 jssecacerts (这步是为了知道 InstallCert 生成 jssecacerts 的位置)
2、拷贝原来的 %JAVA_HOME%\jre\lib\security\jssecacerts 文件,替换刚刚生成的 jssecacerts 文件
3、再重新运行 InstallCert 将新的域名,追加到 jssecacerts 中
整体来说,其实就是一个追加 host 的过程。
PS:操作之前注意备份,并且 jssecacerts 是一个固定的名字,不要随意修改。