Android https忽略证书信任问题
原文地址 www.cnblogs.com
【第一部分,忽略证书信任问题】 直接去第二部分性能问题
搬运自:https://blog.csdn.net/lizeyang/article/details/18983843
java 程序在访问 https 资源时,出现报错
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
1)https 通信过程
(1)客户使用 https 的 URL 访问 Web 服务器,要求与 Web 服务器建立 SSL 连接。
(2)Web 服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
(3)客户端的浏览器与 Web 服务器开始协商 SSL 连接的安全等级,也就是信息加密的等级。
(4)客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
(5)Web 服务器利用自己的私钥解密出会话密钥。
(6)Web 服务器利用会话密钥加密与客户端之间的通信。
2)java 程序的证书信任规则
如上文所述,客户端会从服务端拿到证书信息。调用端(客户端)会有一个证书信任列表,拿到证书信息后,会判断该证书是否可信任。
如果是用浏览器访问 https 资源,发现证书不可信任,一般会弹框告诉用户,对方的证书不可信任,是否继续之类。
Java 虚拟机并不直接使用操作系统的 keyring,而是有自己的 security manager。与操作系统类似,jdk 的 security manager 默认有一堆的根证书信任。如果你的 https 站点证书是花钱申请的,被这些根证书所信任,那使用 java 来访问此 https 站点会非常方便。因此,如果用 java 访问 https 资源,发现证书不可信任,则会报文章开头说到的错误。
解决问题的方法
1)将证书导入到 jdk 的信任证书中(理论上应该可行,未验证)
2)在客户端(调用端)添加逻辑,忽略证书信任问题
第一种方法,需要在每台运行该 java 程序的机器上,都做导入操作,不方便部署,因此,采用第二种方法。下面贴下该方法对应的代码。
1)先实现验证方法
void trustAllHttpsCertificates() throws Exception {
TrustManager[] trustAllCerts = new TrustManager[1];
trustAllCerts[0] = new MyTrustManager();
SSLContext sslCtx = SSLContext.getInstance("SSL");
sslCtx.init(null, trustAllCerts, null);
javax.net.ssl.SSLSocketFactory sslsocketfactory = sslCtx.getSocketFactory();
javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sslsocketfactory);
}
class MyTrustManager implements TrustManager, X509TrustManager {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public boolean isServerTrusted(X509Certificate[] certs) {
return true;
}
public boolean isClientTrusted(X509Certificate[] certs) {
return true;
}
public void checkServerTrusted(X509Certificate[] certs, String authType)
throws CertificateException {
return;
}
public void checkClientTrusted(X509Certificate[] certs, String authType)
throws CertificateException {
return;
}
}
2)在访问 https 资源前,调用
try {
trustAllHttpsCertificates();
} catch (Exception e) {
e.printStackTrace();
} //setDefaultSSLSocketFactory
HostnameVerifier hv = (urlHostName, session) -> {
Log.e("apibhuc", "Warning: URL Host: v7" + urlHostName + " vs. " + session.getPeerHost());
return true;
};
HttpsURLConnection.setDefaultHostnameVerifier(hv); // outer try catch
【第二部分,以上代码性能问题】
如果你像上面说的每次请求都添加上面的代码,那么会有性能问题,具体就是访问耗时,应将将上面部分代码放至应用初始化时;