这两天在做与渠道联调的回调,其中回调渠道的时候使用的是https。
简单的测试代码如下;
public static void simpleTest(String httpsURL) throws Exception {
URL myurl = new URL(httpsURL);
HttpsURLConnection con = (HttpsURLConnection) myurl.openConnection();
InputStream ins = con.getInputStream();
InputStreamReader isr = new InputStreamReader(ins);
BufferedReader in = new BufferedReader(isr);
String inputLine;
while ((inputLine = in.readLine()) != null) {
System.out.println(inputLine);
}
in.close();
}
但是,如果为自签发的SSL证书(未提供证书发行链而不被信任)的请求地址,上面的代码就不起作用了。
错误为:
Exception in thread "main" 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。
经过查找资料,现有两种解决方案:
1)为自身系统增加证书
2)绕过证书验证
先来说第一种方案:
1)用IE打开访问的https网址,点击地址栏后面的小锁头(或证书错误标识),查看证书。在弹出的证书信息框中选择第二个页签“详细信息”。点击复制到文件,选择Base64编码的X.509证书导出即可。
2)把证书从其它文件导入到TrustStore文件中。
keytool -import -file D:/test1.cer -keystore D:/crt_test1
执行命令后要求输入密码,记住输入的密码。
3)编写如下代码:
public static void certTest1()throws Exception{
String httpsURL = "https://xxx.xxx.cn/";
String trustStor="D:/crt_test1";
System.setProperty("javax.net.ssl.trustStore", trustStor);
URL myurl = new URL(httpsURL);
HttpsURLConnection con = (HttpsURLConnection) myurl.openConnection();
con.setHostnameVerifier(hv);
InputStream ins = con.getInputStream();
InputStreamReader isr = new InputStreamReader(ins);
BufferedReader in = new BufferedReader(isr);
String inputLine=null;
while ((inputLine = in.readLine()) != null) {
System.out.println(inputLine);
}
in.close();
}
private static HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
return urlHostName.equals(session.getPeerHost());
}
};
可是如果出现两个https地址并且证书不同的话,上面的代码就没有办法处理了。有人可能说用System.setProperty重新设置一下trustStore,很抱歉,我试了好几次,没有成功。如果他人有成功的请给出代码,谢谢。
为了处理两个证书的情况,我又查了下资料,整理一下,使用TrustManager来处理(此时会用到证书的密码)。
代码如下:
public static void certTest2(String certDir, String passwd, String urlStr)
throws Exception {
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
TrustManager[] tms = getTms(certDir, passwd);
sslContext.init(null, tms, new java.security.SecureRandom());
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(urlStr);
HttpsURLConnection.setDefaultHostnameVerifier(hv);
HttpsURLConnection conn = ((HttpsURLConnection) url.openConnection());
conn.setSSLSocketFactory(ssf);
InputStreamReader im = new InputStreamReader(conn.getInputStream(),