java PKIX path validation failed

来源:V型知识库 | 2017年08月05日 14:18 | 570次浏览 | 分类: 互联网&程序员 作者原创 版权保护

最近做支付接口相关的开发,第三方支付厂商给的接口文档url均为https,开发的时候也没在意,实际上以前也开发请求过https的接口地址,用的post方法也没有任何问题,但是不知怎么回事,这次本地请求一直报错,而放到linux环境下却能正常请求到数据,感觉这样会浪费时间,所以上网查了下,终于把问题解决了,特此记录。

错误如下:

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 com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1611)
	at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:187)
	at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:181)
	at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1035)
	at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:124)
	at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:516)
	at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:454)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:884)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1112)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1139)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1123)
	at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:418)
	at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
	at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:896)
	at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:230)
	at com.wepayweb.weixin.util.WeixinPayTest.sendPost2(WeixinPayTest.java:31)
	at com.wepayweb.weixin.util.WeixinPayTest.main(WeixinPayTest.java:110)
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:285)
	at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:191)
	at sun.security.validator.Validator.validate(Validator.java:218)
	at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)
	at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)
	at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:249)
	at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1014)
	... 13 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:174)
	at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:238)
	at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:280)
	... 19 more

错误信息已经说得很明白,就是证书认证失败,网上很多解决办法为在tomcat等容器中加入证书,本人感觉很麻烦,故此寻找了一种绕开证书的好办法,办法代码如下

static HostnameVerifier hv = new HostnameVerifier() {  
  
        @Override  
        public boolean verify(String hostname, SSLSession session) {  
            // TODO Auto-generated method stub  
            return true;  
        }  
    };  
      
    private static void trustAllHttpsCertificates() {  
        try {  
            javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];  
            javax.net.ssl.TrustManager tm = new MiTM();  
            trustAllCerts[0] = tm;  
            javax.net.ssl.SSLContext sc;  
            sc = javax.net.ssl.SSLContext.getInstance("SSL");  
            sc.init(null, trustAllCerts, null);  
            javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc  
                    .getSocketFactory());  
        } catch (NoSuchAlgorithmException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        } catch (KeyManagementException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
  
    static class MiTM implements javax.net.ssl.TrustManager,  
            javax.net.ssl.X509TrustManager {  
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {  
            return null;  
        }  
  
        public boolean isServerTrusted(  
                java.security.cert.X509Certificate[] certs) {  
            return true;  
        }  
  
        public boolean isClientTrusted(  
                java.security.cert.X509Certificate[] certs) {  
            return true;  
        }  
  
        public void checkServerTrusted(  
                java.security.cert.X509Certificate[] certs, String authType)  
                throws java.security.cert.CertificateException {  
            return;  
        }  
  
        public void checkClientTrusted(  
                java.security.cert.X509Certificate[] certs, String authType)  
                throws java.security.cert.CertificateException {  
            return;  
        }  
    }

在获取HttpsURLConnection之前将这些设置进去,注意代码中的requrl便是https开头的请求地址

url = new URL(requrl);
//URLConnection connection = url.openConnection(); 
trustAllHttpsCertificates();  
HttpsURLConnection.setDefaultHostnameVerifier(hv);  
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();


完整java post请求https方法代码如下:

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;


public class HttpsSendPostUtil {
	static HostnameVerifier hv = new HostnameVerifier() {  
		  
	      
        public boolean verify(String hostname, SSLSession session) {  
            // TODO Auto-generated method stub  
            return true;  
        }

		
    };  
    
    private static void trustAllHttpsCertificates() {  
        try {  
            javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];  
            javax.net.ssl.TrustManager tm = new MiTM();  
            trustAllCerts[0] = tm;  
            javax.net.ssl.SSLContext sc;  
            sc = javax.net.ssl.SSLContext.getInstance("SSL");  
            sc.init(null, trustAllCerts, null);  
            javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc  
                    .getSocketFactory());  
        } catch (NoSuchAlgorithmException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        } catch (KeyManagementException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
  
    static class MiTM implements javax.net.ssl.TrustManager,  
            javax.net.ssl.X509TrustManager {  
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {  
            return null;  
        }  
  
        public boolean isServerTrusted(  
                java.security.cert.X509Certificate[] certs) {  
            return true;  
        }  
  
        public boolean isClientTrusted(  
                java.security.cert.X509Certificate[] certs) {  
            return true;  
        }  
  
        public void checkServerTrusted(  
                java.security.cert.X509Certificate[] certs, String authType)  
                throws java.security.cert.CertificateException {  
            return;  
        }  
  
        public void checkClientTrusted(  
                java.security.cert.X509Certificate[] certs, String authType)  
                throws java.security.cert.CertificateException {  
            return;  
        }  
    }  
	
	/**
	 * requrl 请求url
	 * param  参数
	   V型知识库原创 
	 */
	 public static String sendPost(String requrl,String param){
		 URL url;
		  String sTotalString="";  
		try {
			url = new URL(requrl);
			 //URLConnection connection = url.openConnection(); 
			 trustAllHttpsCertificates();  
	            HttpsURLConnection.setDefaultHostnameVerifier(hv);  
	            HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();  
			 connection.setRequestProperty("accept", "*/*");
			 connection.setRequestProperty("connection", "Keep-Alive");
			 connection.setRequestProperty("Content-Type", "text/xml");
			// connection.setRequestProperty("Content-Length", body.getBytes().length+"");
			 connection.setRequestProperty("User-Agent",
                     "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)");
			 
			 
		        connection.setDoOutput(true);  
		        OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "utf-8");  
		        out.write(param); // 向页面传递数据。post的关键所在!  
		        out.flush();  
		        out.close();  
		        // 一旦发送成功,用以下方法就可以得到服务器的回应:  
		        String sCurrentLine;  
		      
		        sCurrentLine = "";  
		        sTotalString = "";  
		        InputStream l_urlStream;  
		        l_urlStream = connection.getInputStream();  
		        // 传说中的三层包装阿!  
		        BufferedReader l_reader = new BufferedReader(new InputStreamReader(  
		                l_urlStream));  
		        while ((sCurrentLine = l_reader.readLine()) != null) {  
		            sTotalString += sCurrentLine + "\r\n";  
		  
		        }  
		        
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}  
	       
	        System.out.println(sTotalString);  
	        return sTotalString;
	 }


}