一、先摆出问题:
如题,今天遇到了一个安卓手机无法抓取 https 请求的问题,提示:Client SSL handshake failed: An unknown issue occurred processing the certificate(certificate_unknown)
于是我重新检查了一下环境配置:
- 1. 小米安卓手机与 mac 笔记本连接的是同一个网络;
- 2. mac 笔记本已经通过 Charles Help ---> SSL Proxying ---> Install Charles Root Certificate 安装了 Charles 证书,并且通过钥匙串已设置完全信任该证书,如下图所示:
- 3. 小米安卓手机网络设置已设置代理,代理的ip确认是mac 笔记本的 ip,同时端口号已设置8888
- 4. 小米安卓手机通过谷歌浏览器访问 http://chls.pro/ssl 链接,成功下载到证书,并按照手机的提示将证书名称改为 1.crt 。点确定后,手机提示成功安装该证书。
- 5. 通过手机系统设置 ---> 更多设置 ---> 系统安全 ---> 已信任的凭据信息 ---> 用户 界面中,能看到 charles 证书确实已成功安装。
排查了一圈,没发现哪里有问题。要是换作是 iOS(操作步骤跟上面的大同小异),都到这一步了肯定是没问题的。我重新打开 安卓的 app 再尝试抓包,发现还是不行。于是我重启了安卓手机,再尝试抓包,还是不行。摸不着头脑的我只能开始在网上寻找答案。
二、后面终于找到了解决方案:
前提:在手机端和电脑端都必须安装 https 的安全证书;
配置:打测试包时,项目设置默认信任所有证书(系统+用户)
(1) 在工程 res-xml 目录中创建一个名为 network_security_config.xml 的文件,文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" overridePins="true" />
<certificates src="user" overridePins="true" />
</trust-anchors>
</base-config>
</network-security-config>
(2) 在 AndroidManifest 里的标签中,添加如下代码:
android:networkSecurityConfig="@xml/network_security_config"
重新打包项目,然后抓包,即成功了。
你以为到这就结束了?然而并没有。
三、我还是有个疑问,上面的例子只适合能够修改源码并能重新打包或者是真机调试的情况。如果是之前已经打好的包突然某个接口网络请求出问题只能采取抓包的方式来解决,并且之前并没有添加上面的代码的情况,那又该怎么呢?
接下来要敲重点了:Android 7.0及7.0以上的机子改变了安全策略,默认不信任用户添加到系统的 CA 证书:
To provide a more consistent and more security experience accross the Android ecosystem,beginning with Android Nougat, compatible devices trust only the standardized system CAs maintained in AOSP.
翻译:
为了在Android生态系统中提供更一致、更安全的体验,从Android Nougat开始,兼容设备只信任AOSP中维护的标准化系统CA。
也就是说对基于 SDK24 及以上的 App 来说,即使你在手机上安装了抓包工具的证书也无法抓取 https 请求。
解决方案:
将抓包工具的证书添加到系统证书中!效果如下图所示:
实践步骤:
- 需要先获取你的安卓测试机 root 权限。我用的是红米手机,获取 root 权限操作如下:设置 ---> 授权管理 --> 开启ROOT权限 即可。之后手机会重启,重启之后即可成功 root 。
- 通过 电脑端的 Charles 拿到后续要安装到手机上的证书,并通过 openssl x509 -subject_hash_old -in 命令计算出它的哈希值。并将证书重命名为 哈希值.0 或者哈希值.1 。
-
获取证书的哈希值,如下图所示:
- 命令如下:
openssl x509 -subject_hash_old -in
-
通过 电脑端的 Charles 拿到后续要安装到手机上的证书步骤也很简单:
Charles 菜单栏 ---> Help ---> SSL Proxying ---> Save Charles Root Certificate。选择要保存的路径即可。如下图所示:
- 证书重命名如下图所示:
注意:这一步必须保证证书没有隐藏后缀名,可以点右键,显示简介,将隐藏后缀名的勾去掉。将后缀名置为1或者0 。系统会提示你是否确认使用该后缀,点确定即可。
- root 成功的安卓测试机连接电脑。我这里用是 mac 笔记本。接下来是mac 通过终端来操作:
- 打开终端,通过终端将当前的工作目录切换到第二步保存证书的所在目录,我这里是将证书直接保存到家目录下的 Download 目录下。所以切换工作目录的命令如下:
cd /Users/pilipala/Downloads; clear; pwd
安卓系统的安全证书是在 /system/etc/security/cacerts 目录下的,终端输入 adb shell ,打开目录就能看到这些证书,如下图所示:我们最终目标是要将安装到手机端的证书添加到该目录下。而在这之前,我们得先将证书发送到手机端的 sdcard 的 Download 目录,再将证书从手机端的 sdcard 的 Download 目录复制到安卓系统的安全证书目录。
接着输入 adb push 命令将证书推给手机端的 sdcard 的 Download 目录。命令如下:
adb push f050a275.1 /sdcard/Download
注意看终端的提示,此时终端很快会提示你有一个文件被 pushed,即 push 成功。如下图所示:- 这一步很关键,需要将 /sdcard/Download 目录下的证书复制到系统证书的所在目录。命令如下:
cp /sdcard/Download/f050a275.1 /system/etc/security/cacerts/
然而我复制的时候提示我证书是一个 ReadOnly 文件。如下图所示:我通过测试,发现关闭手机安全验证机制的方案是可以解决的,命令如下:
adb root
adb disable-verity
adb reboot //此时手机会重启,这时候不要拔插数据线
手机重启后,再次执行上面的 cp 命令,如果终端没有任何提示,即复制成功,看到胜利的曙光了。
- 接着,赋予证书执行的权限。
chmod 644 /system/etc/security/cacerts/f050a275.1
- 最后重启手机即可。
到这里,再打开手机设置 --> 更多设置 --> 系统安全 --> 信任的凭据 --> 系统,就能看到抓包工具的证书已经被我们成功添加进来了。默认是开启信任的。如果没有,打开信任即可。
再重新抓包,就能发现 https 的请求也能抓取到了。
四、写在最后
- 证书重命名那一块,一般会将证书重命名为:哈希值.0,如果安卓系统的安全证书目录下已经存在相同哈希值的证书,可以将证书的后缀名重命名为 .1 。再将证书复制到安卓系统的安全证书目录下。
- 其他品牌的手机(如 vivo等)我没试过,不过操作步骤应该是差不多的。
以上,感谢阅读!