OkHttp 解决HTTPS中间人劫持风险
前言
国内市场,针对app的安全性在逐步提高。对我们app发起的网络请求也提高了要求。
例如HTTPS 中间人劫持风险。
主要就是检测HostnnameVerifier是否允许任意域名绕过SSL认证。执行网络访问。
如果是发布在外网上的小伙伴,可能针对这个会更清除。因为Google 应用市场很早就要求所有的app都进行SSL证书的加强了。
我们通常的网络架构使用OKHttp的较多。我下面介绍在OKHttp中如何配置
1.设置HostnameVerifier 验证域名
public class ZinyanHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession sslSession) {
if ("允许放行的https".equals(hostname)) {
return true;
} else {
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
return hv.verify(hostname, sslSession);
}
}
}
注意 是域名HostName 的比较: 例如:
API地址为: https://zinyan.com/api/manager.json
那么HostName: zinyan.com 为域名Host 。
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.hostnameVerifier(new ZinyanHostnameVerifier());
而如果我们使用OKHttp3的话。默认是验证了hostnameVerifier。 因为它有一个类: OKHostnameVerifier类。
默认帮我们实现了HostNamerifier的验证。
2. X509TrustManager 验证证书是否正确
通常有两种验证方式,一种是将证书本地缓存,一种是通过获取证书的pubicKey 进行比较验证
public class MyTrustManager implements X509TrustManager {
/**
* @param
* @param
* @throws CertificateException
*/
@SuppressLint("TrustAllX509TrustManager")
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
if (chain == null) {
throw new IllegalArgumentException("checkServerTrusted:x509Certificate array isnull");
}
if (!(chain.length > 0)) {
throw new IllegalArgumentException("checkServerTrusted: X509Certificate is empty");
}
if ((chain != null) && (chain.length == 1)) {
chain[0].checkValidity();
}
}
@SuppressLint("TrustAllX509TrustManager")
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
if (chain == null) {
throw new IllegalArgumentException("checkServerTrusted:x509Certificate array isnull");
}
if (!(chain.length > 0)) {
throw new IllegalArgumentException("checkServerTrusted: X509Certificate is empty");
}
if ((chain != null) && (chain.length == 1)) {
chain[0].checkValidity();
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public static SSLSocketFactory createSSLSocketFactory(Context context) {
if (context == null) {
throw new NullPointerException("context == null");
}
CertificateFactory certificateFactory;
try {
String[] str = context.getAssets().list("zinyan");
if (str == null || str.length <= 0) {
//只有assets/zinyan/ 文件夹下面 没有.cer文件 才会执行
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, new TrustManager[]{new MyTrustManager()},
new SecureRandom());
return sc.getSocketFactory();
} else {
certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
for (int i = 0; i < str.length; i++) {
InputStream is = context.getAssets().open("zinyan/" + str[i]);
keyStore.setCertificateEntry(String.valueOf(i), certificateFactory.generateCertificate(is));
if (is != null) {
is.close();
}
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
return sslContext.getSocketFactory();
}
} catch (Exception e) {
Log.e("MyTrustManager", "zinyan - certification path not found.");
}
return null;
}
}
然后在OkHttp Clienter创建的地方执行:
clientBuilder.sslSocketFactory(sslSocketFactory, new MyTrustManager());
我上面的例子只是一个比较简单的配置。
验证了服务端和客户端的证书的有效期,和是否有证书。其他的就没有验证了。
更合理安全的,就是要验证签名等等。
只有assets/zinyan/ 文件夹不存在的时候,才会验证证书的有效期等。如果你在assets里面存储了证书。那么就会用OkHttp默认的证书验证工具,会进行完整的验证。
但是我们这个基本验证,也能达到各种平台的扫描安全性要求了。
本质上来说,OkHttp 直接使用,就能达到了各种https 的安全要求了。这个框架帮我们解决了很多问题
评论区