侧边栏壁纸
博主头像
Z同学博主等级

工作磨平激情前,坚持技术的热忱。 欢迎光临Z同学的技术小站。 分享最新的互联网知识。

  • 累计撰写 274 篇文章
  • 累计创建 55 个标签
  • 累计收到 74 条评论

OkHttp 解决HTTPS中间人劫持风险

Z同学
2021-09-06 / 0 评论 / 0 点赞 / 631 阅读 / 3,603 字
温馨提示:
本文最后更新于 2021-11-18,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

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 的安全要求了。这个框架帮我们解决了很多问题

0

评论区