一、问题现象

之前一个项目使用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 targetat sun.security.ssl.Alerts.getSSLException(Alerts.java:192)at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1946)at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:316)at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:310)

二、原因

javax.net.ssl.SSLHandshakeException错误通常表示 SSL 握手失败。这意味着在建立 SSL 连接时,Java 安全管理器无法构建一个有效的证书路径,从而无法找到有效的证书路径来验证目标站点的证书,触发此错误信息的调用方环境都是JAVA开发环境

三、解决方法

/* * Copyright 2006 Sun Microsystems, Inc.All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Sun Microsystems nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import java.io.*;import java.net.URL; import java.security.*;import java.security.cert.*; import javax.net.ssl.*; public class InstallCert { public static void main(String[] args) throws Exception {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) " />[: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; i > 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);}} }

把上面这段代码copy出来,什么都不要改(包括package名);再在本地新建一个跟此类名相同的.java文件,将内容粘贴进去;然后进入命令行

1、编译:

javac InstallCert.java

2、运行

java InstallCert domain.company.com.cn

(说明:java InstallCert 后面需要替换为自己对应Java请求出错的站点URL,不需要输入 https:// 信息,如:www.baidu.com)

在运行过程中,会看到以下信息:

at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:194)at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:216)... 13 moreServer sent 2 certificate(s):1 Subject CN=ecc.fedora.redhat.com, O=example.com, C=US Issuer CN=Certificate Shack, O=example.com, C=US sha12e 7f 76 9b 52 91 09 2e 5d 8f 6b 61 39 2d 5e 06 e4 d8 e9 c7md5 dd d1 a8 03 d7 6c 4b 11 a7 3d 74 28 89 d0 67 542 Subject CN=Certificate Shack, O=example.com, C=US Issuer CN=Certificate Shack, O=example.com, C=US sha1fb 58 a7 03 c4 4e 3b 0e e3 2c 40 2f 87 64 13 4d df e1 a1 a6md5 72 a0 95 43 7e 41 88 18 ae 2f 6d 98 01 2c 89 68Enter certificate to add to trusted keystore or 'q' to quit: [1]

这个时候输入1,直接回车即可;然后会在相应的目录下生成一个名为 jssecacerts 的证书

将此证书复制到 $JAVA_HOME/jre/lib/security 目录下,最后重启项目即可访问成功。