场景:
HttpURLConnection访问https URL,并且下载PDF文件.
异常信息为:
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
原因分析:
google发现stackoverflow上不少人反应,twitter和新浪微博的api也会报这个异常,不少人反映客户端需要导入证书,其实大可不必,如果要导证书的话,用户不得哭了。。
解决方案:
HttpURLConnection访问https并相信所有证书,如下:
import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class PDFTool {
private static final LoggerControler log = LoggerControler.getLogger(PDFTool.class);
public static void httpGetByNotSSLAndSaveFile(String httpUrl, String fileName, String savePath) {
BufferedReader input = null;
StringBuilder sb = null;
URL url = null;
HttpURLConnection con = null;
try {
url = new URL(httpUrl);
try {
// trust all hosts
trustAllHosts();
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
if (url.getProtocol().toLowerCase().equals("https")) {
https.setHostnameVerifier(DO_NOT_VERIFY);
con = https;
} else {
con = (HttpURLConnection) url.openConnection();
}
//设置超时间为10秒
con.setConnectTimeout(10 * 1000);
//防止屏蔽程序抓取而返回403错误
con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36");
//得到输入流
InputStream inputStream = con.getInputStream();
//获取数组
byte[] getData = readInputStream(inputStream);
//文件保存位置,如果不存在新建
File saveDir = new File(savePath);
if (!saveDir.exists()) {
saveDir.mkdir();
}
File file = new File(saveDir + File.separator + fileName);
FileOutputStream fos = new FileOutputStream(file);
fos.write(getData);
if (fos != null) {
fos.close();
}
if (inputStream != null) {
inputStream.close();
}
log.info(url + " download success, savePath =" + savePath);
} catch (IOException e) {
e.printStackTrace();
}
} catch (MalformedURLException e1) {
e1.printStackTrace();
} finally {
// close buffered
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// disconnecting releases the resources held by a connection so they may be closed or reused
if (con != null) {
con.disconnect();
}
}
}
final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
private static void trustAllHosts() {
final String TAG = "trustAllHosts";
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
log.info(TAG + " checkClientTrusted");
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
log.info(TAG + " checkServerTrusted");
}
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] readInputStream(InputStream inputStream) throws IOException {
byte[] buffer = new byte[1024];
int len = 0;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while ((len = inputStream.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
bos.close();
return bos.toByteArray();
}
}
测试code:
import org.testng.annotations.Test;
public class PDFToolTest {
@Test
public void testSavePDF() {
String savePath_local = "C:\Users\myName\Desktop\TestPDF";
String pdfName = "TEST.pdf";
String pdfURL = "https://www.uhcmedicaresolutions.com/alphadms/ovdms10g/groups/ov/@ov/@highrespdf/documents/highrespdf/4678057.pdf";
PDFTool.httpGetByNotSSLAndSaveFile(pdfURL, pdfName, savePath_local);
}
}
可以看出其中与之前的HttpsURLConnection测试代码主要的不同就是加入了
trustAllHosts(); 和 https.setHostnameVerifier(DO_NOT_VERIFY);
表示相信所有证书,并且所有host name验证返回true,这样就能定位到之前的异常是证书验证不通过的问题了。



