天天看點

關于面向Android7.0及以上系統的應用無法通過charles抓包關于面向Android7.0及以上系統的應用無法通過charles抓包

關于面向Android7.0及以上系統的應用無法通過charles抓包

預設情況下,來自所有應用的安全連接配接(使用TLS和HTTPS之類的協定)均信任預裝的系統CA,而面向6.0及以下系統版本的應用預設情況下還會信任使用者添加的CA憑證。如果我們将targetSdkVersion修改到24以上的時候,應用則不會信任使用者安裝的證書了。

詳細說明見官方文檔。

這時,當我們通過charles或其他抓包工具抓取應用的Https請求時,盡管在手機上暗安裝了charles證書,則仍會顯示證書鍊不被信任。

關于面向Android7.0及以上系統的應用無法通過charles抓包關于面向Android7.0及以上系統的應用無法通過charles抓包

我們有兩種解決方案,适用不同的場景。

在manifest檔案中配置application的屬性android:networkSecurityConfig=”@xml/network_security_config”

通過network_security_config.xml檔案可以自定義網絡安全設定,包括如下功能:

  • 自定義信任的證書機構:适用于自簽名證書或限制應用信任系統預裝證書
  • 證書固定:将應用的安全連接配接限制為特定的證書,适用于代理抓包、線下環境調試
  • 明文通信選擇退出:防止應用意外使用明文通信
  • 僅調試重寫:在debug狀态設定上述限制

詳細的配置可參考官方文檔

那麼我們為了使我們的應用信任charles的證書,可以選擇證書固定或者直接信任使用者安裝的證書。配置如下:

固定證書:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <certificates src="@raw/my_ca"/>
        </trust-anchors>
    </domain-config>
</network-security-config>

注: 以 PEM 或 DER 格式将自簽署或非公共 CA 證書添加到 res/raw/my_ca。
           

信任使用者安裝的證書:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
   <trust-anchors>
       <certificates src="system"/>
       <certificates src="user"/>
   </trust-anchors>
</network-security-config>
           

但這樣設定會忽視掉系統提供的安全的校驗,是以盡可能不要配置到線上版本中,那麼就要用到第四的功能了,僅調試重寫。

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <debug-overrides>
        <domain-config>
            <domain includeSubdomains="true">example.com</domain>
            <trust-anchors>
                <certificates src="@raw/my_ca"/>
            </trust-anchors>
        </domain-config>
    </debug-overrides>
</network-security-config>
           

這種配置方式在不改動代碼的情況下,就支援了debug狀态可抓包的操作,算是比較優雅的方案。

但是有另外的一個問題,如果測試同學也想抓取App的網絡請求怎麼辦?送出測試時建構debug狀态的安裝包麼?貌似不妥吧。debug和release版本的簽名、混淆配置都不同。可能有人會說,隻修改debuggable參數,其他不變。那麼在建構線上安裝包時還要手動改回來麼?(如果可以有動态修改debuggable參數的方式請指教。)

就算是可以動态修改debuggable參數,因為證書是我們内置到工程中的,不能根據不同的人做修改,是以局限性還是蠻大的,僅适用于個人開發時使用。

由于以上限制就引出了第二種配置方案。

為網絡請求庫添加證書配置

由于我們不能動态控制debug狀态,但是我們能控制什麼時候為網絡請求庫配置https證書。例如:僅線上環境時不配置charles證書,其他時候由使用者指定加載charles證書檔案。這種方式就做到了不依賴debuggable環境,而且可以動态修改證書檔案。

不同的網絡用戶端配置證書方式,這裡以okhttp為例做說明。

第一步:為你的網絡用戶端添加一個設定證書流的api,由調用者負責判斷建構環境,并決定是否配置自定義證書。

private List<InputStream> cerFileIsList;
    /**
     * 設定Https證書檔案
     *
     * @param cerFileIs 證書檔案流清單
     */
    public void addCertFileIs(InputStream cerFileIs) {
        if (cerFileIsList == null) {
            cerFileIsList = new ArrayList<>();
        }
        cerFileIsList.add(cerFileIs);
    }
           

第二步,在初始化OkHttpClient時使用證書建構SSLSocketFactory

private SSLSocketFactory createSSLSocketFactory() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sslContext = SSLContext.getInstance("TLS");

        if (cerFileIsList != null && cerFileIsList.size() > ) {
            try {
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                //建立一個包含我們信任證書的KeyStore
                String keyStoreType = KeyStore.getDefaultType();
                KeyStore keyStore = KeyStore.getInstance(keyStoreType);
                keyStore.load(null, null);

                int certIndex = ;
                for (InputStream inputStream : cerFileIsList) {
                    String certAlias = String.valueOf(certIndex++);
                    keyStore.setCertificateEntry(certAlias, cf.generateCertificate(inputStream));
                }
                //根據秘鑰庫生成信任管理器
                String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
                TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
                tmf.init(keyStore);
                sslContext.init(null, tmf.getTrustManagers(), null);
            } catch (IOException | CertificateException | KeyStoreException e) {
                e.printStackTrace();
            } finally {
                CloseUtils.closeIO(cerFileIsList);
                cerFileIsList.clear();
            }
        } else {
            sslContext.init(null, null, null);
        }

        return new Tls12SocketFactory(sslContext.getSocketFactory());
    }
           

當測試同學需要抓包時,隻需要下載下傳對應的Charles證書并由App載入後,重新初始化OkHttpClient就可以了。

通過OkHttpClient設定自定義證書時需要注意一點,就是需要把正常服務端接口域名的證書一并設定進來,否則的話App隻信任charles證書,卻不信任後端服務的證書了。