最近做支付接口相关的开发,第三方支付厂商给的接口文档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; } }