Androidandroid开发专题收藏系列--Sorry的Android收藏

Android支持Https总结

2016-11-11  本文已影响10596人  卡布崽

整合了一个工具类HttpsUtil,地址:

https://github.com/kabuzai/Android-Https

基本使用

  1. 全局支持Https只需在Application中调用
    HttpsUtil.initHttpsUrlConnection(this);

  2. 适用的网络请求:

  1. OkHttp不适用,可以调用
    HttpsUtil.getHttpsOkHttpClient(Context context)
    来获取支持Https的OkHttpClient。

  2. 服务端证书放在Assets目录下
    修改证书名:
    private static final String[] CERTIFICATES = new String[]{"证书名.cer"};
    如有多个证书,向CERTIFICATES添加即可。

  3. 默认支持的是单向验证,安全性已足够

    public static void initHttpsUrlConnection(Context context) {
        InputStream[] certificates = getCertificates(context, CERTIFICATES);
        SSLSocketFactory sslSocketFactory = getSSLSocketFactory(certificates, null, null);
        HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
        if (certificates == null) {
            HttpsURLConnection.setDefaultHostnameVerifier(getUnSafeHostnameVerifier());
        }
    }

其中getSSLSocketFactory方法的后两个参数需要传入合适的值
具体实现可参考:
http://blog.csdn.net/lmj623565791/article/details/48129405

为什么Https请求需要这样额外配置?

简单来说,Https相比Http多了一层SSL验证,这个验证有很多步骤。其中有一步是对服务器证书的校验,而服务器证书的获得:

深入了解可参考:
http://www.cnblogs.com/P_Chou/archive/2010/12/27/https-ssl-certification.html

其他可能出现的问题

SSL验证时会对证书的有效期进行验证,如果用户设备的时间不在有效期内也会验证失败。如果想忽略有效期,可以使用HttpsUtil中的NotValidateTimeTrustManager类:

private static class NotValidateTimeTrustManager implements X509TrustManager {

        private X509TrustManager defaultTrustManager;

        public NotValidateTimeTrustManager(X509TrustManager defaultTrustManager) {
            this.defaultTrustManager = defaultTrustManager;
        }

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            defaultTrustManager.checkClientTrusted(chain, authType);
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            try {
                defaultTrustManager.checkServerTrusted(chain, authType);
            } catch (CertificateException e) {
                e.printStackTrace();
                Throwable t = e;
                while (t != null) {
                    if (t instanceof CertificateExpiredException
                            || t instanceof CertificateNotYetValidException)
                        return;
                    t = t.getCause();
                }
                throw e;
            }
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return defaultTrustManager.getAcceptedIssuers();
        }
    }

在checkServerTrusted方法中捕获了CertificateException,并不断向上寻找原因。如果发现是CertificateExpiredException(证书过期)或者CertificateNotYetValidException(证书还未生效)则通过验证,否则继续抛出异常。

使用方法:

private static TrustManager[] prepareTrustManager(InputStream... certificates) {
        ...
            return trustManagerFactory.getTrustManagers();
            // TODO: 2016/11/11 针对有效期异常导致校验失败的情况,目前没有完美的解决方案
//            TrustManager[] keyStoreTrustManagers = trustManagerFactory.getTrustManagers();
//            return getNotValidateTimeTrustManagers((X509TrustManager[]) keyStoreTrustManagers);
        ...
    }

将第一行注释,放开最后两行注释

但要注意,这个方案仍然有一定的安全隐患,因为查看底层进行SSL验证的源码后发现,对证书有效期的校验并不是最后的环节,例如在校验有效期通过后还会校验设备上的吊销证书列表,确认证书是否不在该列表中,其他还有一些自定义校验项。

上述方案在校验有效期异常后就会通过验证,漏掉了剩下的一些校验项,仍然存在一些隐患,如果安全级别要求非常高,并不推荐使用。

更多SSL验证问题可以参考:
https://developer.android.com/training/articles/security-ssl.html#MissingCa

上一篇下一篇

猜你喜欢

热点阅读