Android证书有效性验证方案

Posted 汤米粥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android证书有效性验证方案相关的知识,希望对你有一定的参考价值。

android证书有效性验证方案

1、前言:

1.1、SSL劫持攻击:

         目前虽然很多Android APP使用了https通信方式,但是只是简单的调用而已,并未对SSL证书有效性做验证。在攻击者看来,这种漏洞让https形同虚设,可以轻易获取手机用户的明文通信信息,攻击示意图如下:

2、解决方案:

2.1、服务器证书锁定:

2.1.1、简介:    

         服务器证书锁定的原理是在代码中精确的验证当前服务器是否持有某张指定的证书。X509TrustManager接口是实现证书锁定一种方法,它通过在SSL回调函数中读取服务器证书密钥并和程序预埋的证书密钥进行对比,如果两者不一致则强行断开链接。

2.1.2、流程图:

可以看出,百度和Tomcat服务器的数据都能成功获取。但是这种方式存在极大的安全漏洞。因为并没有做任何SSL证书的校验,很容易被MITM(Man In The Middle)攻击。

比较好的优化方式当然是在客户端使用自签名SSL证书,验证服务器的身份合法之后,再进行后续的数据传输操作。通过以下步骤将客户端证书my_client.cer导入到项目中:

1 将my_client.cer保存在assets文件夹中

创建assets目录,并将my_client.cer保存到此目录下,如下:

保存好后,通过如下方式将证书转换为InputStream格式:

private InputStream getInputStreamFromAsset()
    InputStream inputStream = null;
    try 
        inputStream = getAssets().open("my_clent.cer");
     catch (IOException e) 
        e.printStackTrace();
    
    return inputStream;

2 创建只信任自签名证书的X509TrustManager

将转换后的InputStream传入以下方法,创建自定义X509TrustManager

    /**
     * 创建只信任指定证书的TrustManager
     * @param inputStream:证书输入流
     * @return
     */
    @Nullable
    private static X509TrustManager createTrustCustomTrustManager(InputStream inputStream) 
        try 
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null);

            Certificate certificate = certificateFactory.generateCertificate(inputStream);
            //将证书放入keystore中
            String certificateAlias = "ca";
            keyStore.setCertificateEntry(certificateAlias, certificate);
            if (inputStream != null) 
                inputStream.close();
            

            TrustManagerFactory trustManagerFactory = TrustManagerFactory.
                    getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keyStore);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

            if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) 
                throw new IllegalStateException("Unexpected default trust managers:"
                        + Arrays.toString(trustManagers));
            
            return (X509TrustManager) trustManagers[0];
         catch (Exception e) 
            e.printStackTrace();
        
        return null;
    

以上方法将自签名证书保存到Java对象KeyStore中,并最终创建只信任自签名证书的X509TrustManager对象。重新将此对象传给上文中的 createSSLClient方法后,就是一个加载自签名SSL证书的OkHttpClient对象了。

再次执行getBaidu和getTomcat方法,执行结果如下:

getBaidu onFailure: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.


getTomcat response: 

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <title>Apache Tomcat/8.5.72</title>
            <link href="favicon.ico" rel="icon" type="image/x-icon" />
            <link href="tomcat.css" rel="stylesheet" type="text/css" />
        </head>

        <body>
        ...
        </body>
    </html>

以上结果显示获取baidu数据失败,而获取Tomcat数据成功。

参考 :【android】Okhttp3信任所有证书设置_永远不要矫情的博客-CSDN博客_okhttp 信任所有证书

2.2、根证书锁定+域名验证:

2.2.1、简介:

         和服务器证书锁定类似,区别在于该方案检查服务器证书是否由指定的CA证书签名,即检查签名服务器证书的CA证书的公钥和本地预置的CA证书公钥是否一致,同时,还会进一步校验服务器证书的域名是否有效。

2.2.2、流程图:

2.3、方案对比:

方案

优点

缺点

服务器证书锁定

安全性最高,实施攻击必须拿到对应服务器私钥证书。

更换证书时APP影响大

根证书锁定+域名验证

更换服务器证书不受影响

安全性和CA机构以及域名验证机制有关。

以上是关于Android证书有效性验证方案的主要内容,如果未能解决你的问题,请参考以下文章

Android证书有效性验证方案

在java中怎么验证ssl证书的有效性

针对 MITM 验证 SSL 证书

用户传入的任何参数都必须做有效性验证,如果忽略的话可能会导致哪些危害系统

在 java 中验证证书会引发异常 - 无法找到请求目标的有效证书路径

星说·从数据分析角度看安全运营 深度探索攻击有效性验证