Reqable实战系列——Android用户证书那些事儿

2,702 阅读5分钟

大家好,很多同学抓包调试App卡在证书信任这块儿,今天给大家带来一篇关于Android用户证书相关的文章,捋一捋Android证书信任策略的历史变更和解决方案。

1. 行为变更

Android系统的CA证书分为两种,一种是系统内置的系统证书,一种是用户自行安装的用户证书。在Android系统版本的迭代中,用户证书主要经历了两次大的行为变更。

第一次变更是在Android 7.0,从这个版本(以目标App的TargetSdkVersion为准)起,系统默认不再信任用户证书。这次变更最大的影响就是基于MITM(Man-in-the-middle)的流量分析方案无法正常工作了,因为安装到用户证书目录的CA证书无法被目标App信任。这个变更对开发者的影响非常大,因此Google同时推出了网络安全配置(Network Security Config)的方案。简而言之就是虽然关上了一扇门,但还是给开发者开了一扇窗户。

第二次变更是在Android 11,从这个版本起无法自动化安装用户证书,必须由用户手动在系统设置里面选择证书文件进行安装。这次变更最大的影响就是复杂化了证书安装流程,对普通用户提高了操作门槛,但对开发者影响不大。

2. TargetSdkVersion机制

我们来细说下Android 7.0的那次大变更,这个变更是针对的目标App的TargetSdkVersion而不是全部App。每个Android应用程序都会指定一个TargetSdkVersion,在开发时会有如下配置:

android {  
    defaultConfig {    
        targetSdkVersion 34  
    }
}

比如上面的34就是TargetSdkVersion的值,我们在反编译Apk文件的时候也可以找到这个TargetSdkVersion。

前面说到变更是针对的目标App的TargetSdkVersion而不是全部App,这是什么意思呢?我们知道Android 7.0对应的SDK值是24,假如一个App的TargetSdkVersion是23,那么这个App在Android 7.0及以上版本的系统上运行时仍然会信任用户证书,但如果TargetSdkVersion是24+那就不行了,这是Android的系统兼容机制。

概括一下,判断用户证书是否可以被信任,取决于两个条件,满足其一即可。

  • 系统版本是否低于Android 7.0。
  • App的TargetSdkVersion是否低于24。

如果没有Android 7.0以下系统的设备,想对一个Android应用进行抓包,最简单的做法就是临时修改TargetSdkVersion为23。

既然如此,那么开发者为何不将TargetSdkVersion直接设置为一个较低的值呢,因为应用商店上架时对此值有要求,比如现在Google Play商店要求上架的应用程序的TargetSdkVersion值必须大于等于34,要求开发者必须适配新系统特性。

3. 关于那扇窗户

前面说到Google给开发者开了一扇窗户,这个窗户是什么呢?就是网络安全配置文件。开发者可以通过配置文件指定应程序如何信任证书,信任哪些证书等等。

对于使用MITM方案来抓包,则可以配置应用程序信任用户证书,分为两个步骤。

首先,在项目xml目录下面创建network_security_config.xml文件,内容如下:

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

image.png

然后,在AndroidManifest.xml文件中配置引用此文件。

<?xml version="1.0" encoding="utf-8"?>
<manifest>
  <application android:networkSecurityConfig="@xml/network_security_config">
    ...
  </application>
</manifest>

为了简化上面的两个步骤的操作,我们提供了现成的插件,只需要在build.gradle文件中依赖即可。

debugImplementation("com.reqable.android:user-certificate-trust:1.0.0")

上面的依赖表示在Debug版本信任用户证书,而Release等其他版本则不会。如果想调试Release包,把上面debug去掉或者改成release即可。

4. 例外情况

有的同学可能会说了,我修改了TargetSdkVersion到23,或者也创建了网络安全配置文件,但是App还是无法信任用户证书,这是为什么呢?

这里需要补充下,无论是修改TargetSdkVersion还是创建网络安全配置文件,这两种方案都只对原生应用起作用,或者说对使用Java Socket进行通信的应用起作用。类似Flutter、Unity等非原生应用,是不起作用的,比如Flutter网络框架中写死了只信任系统证书,无论怎么捣鼓用户证书都是无效的。

5. 手机浏览器

如果有同学需要使用手机浏览器来调试网页,我们不太可能去重新编译手机浏览器,那么有什么好的方案呢?

第一个方案,使用最新版本的手机Chrome浏览器,已经默认信任用户证书了。

第二个方案,使用手机Firefox浏览器,在调试菜单中启用信任用户证书,操作步骤如下:

  • 打开 设置 -> 关于Firefox。
  • 持续点击顶部logo 5下,直到出现已启用调试菜单
  • 返回设置,打开Secret Settings,启用Use third party CA certificates选项。

6 结语

非Root设备调试App相对来说还是比较麻烦,但并非不可行。如果能Root设备安装证书到系统目录是最好,可以避免修改项目代码。当然,Root设备上面安装证书步骤也不简单,所以Reqable开发了ADB一键安装系统证书功能。

image.png

Reqable的理念是先进API生产力工具,宗旨是做优秀的国产软件。如果您对本篇文章满意的话,也可以通过订阅Reqable的方式来支持我。

Reqable的官网:reqable.com
GitHub建议&反馈:github.com/reqable/req…

如果您对Reqable有任何问题都可以与我联系,或者在GitHub上提交Issue!