Charles 和 Proxyman 的 HTTPS 解密配置教程
Charles 和 Proxyman 的 HTTPS 解密配置教程
在 Android 开发里抓包,最常见的情况不是配代理,而是配完代理之后满屏的红色叉号。Charles 的列表里显示 <unknown>,Proxyman 的 Flow 窗口里弹出 SSL Handshake Failed,这时候你知道问题出在 HTTPS 解密上,但按照网上教程点了一圈菜单,手机浏览器里证书也装了,回到应用里依然握手失败。这种情况在 Android 7.0 之后变成了常态,因为 Google 从 Nougat 开始收紧了用户证书的信任策略,而大多数抓取工具的教学文档还停留在 Android 6.0 的时代。Charles 和 Proxyman 作为目前开发者最常用的两款桌面抓包工具,它们的证书体系、代理架构和 Android 适配方式有本质差异,理解这些差异比记住几个点击步骤更重要。
抓包解密的核心:自签根证书与中间人代理
Charles 和 Proxyman 的 HTTPS 解密逻辑都是典型的中间人代理(MITM)。当手机把代理指向运行着 Charles 或 Proxyman 的电脑时,应用发出的 HTTPS 请求实际先到达桌面端的代理软件。代理软件需要向手机应用呈现一个由自己签发的服务器证书,然后自己再去和真实服务器建立连接。为了让手机应用信任这个伪造的证书,必须在系统或应用层面预置该代理软件的自签根证书。
这里有个关键细节:Charles 使用 Java 原生的 keytool 体系管理其根证书,证书存储在应用内部的 Java KeyStore 中。你可以在 Charles 的菜单 Help > SSL Proxying > Save Charles Root Certificate 中把它导出为 .pem 或 .cer 格式。Proxyman 则更接近现代 macOS 应用的实现,它的根证书生成后导入 macOS 系统钥匙串,随后可以通过 Certificate > Install Certificate on This Mac 或导出到移动设备。两者的证书格式虽然都是 X.509,但默认导出编码和文件名处理不同,这在 Android 端安装时会导致一些微妙的兼容性差异。
对于 Android 平台来说,系统对证书的校验极其严格。Android 从 7.0(API 24)开始,默认不再信任用户手动安装到 Settings > Security > User credentials 里的 CA 证书。这意味着,你把 Charles 或 Proxyman 的证书下载到手机,点击安装,系统提示“已安装”,但你的 targetSdkVersion 大于等于 24 的应用根本不会把它纳入信任链。于是 OkHttp 或 HttpURLConnection 在握手阶段就会抛出 java.security.cert.CertPathValidatorException: Trust anchor for certification path not found,对应到 Charles 里就是一条 Client SSL handshake failed - Remote host closed connection during handshake,Proxyman 里则显示 The client failed to establish a secure connection。
Charles 的证书体系与配置路径
Charles 是 Java 写的跨平台应用,官网 https://www.charlesproxy.com/,单用户许可证 50 美元,买断制,可免费试用 30 天。它的代理功能极其成熟,但 UI 和操作逻辑带着浓厚的 Java Swing 年代感。要用 Charles 解密 HTTPS,第一步不是去手机装证书,而是先打开 Proxy > SSL Proxying Settings,勾选 Enable SSL Proxying,然后在 Include 列表里添加 __PLACEHOLDER_ITALIC_0__ 或者至少 *:443。
很多初学者在这里踩坑:Charles 默认不会解密所有 HTTPS 流量,它只解密你明确配置的主机。如果你只加了 __PLACEHOLDER_ITALIC_0__:*,虽然这会让 Charles 的 CPU 占用稍微上升,但在现代开发机器上基本可以忽略。
手机端的配置步骤看似简单,实际坑很多。把手机 Wi-Fi 代理设为 Charles 运行的机器 IP 和端口 8888(默认),然后在手机浏览器访问 chls.pro/ssl 下载证书。这个地址是 Charles 内置的证书分发页面,会根据你当前的 Charles 版本返回对应的根证书。在 Android 13 之前,下载的 .pem 文件通常可以直接点击安装。到了 Android 14,系统对证书安装的路径做了更严格的限制,点击下载完成的 .pem 文件可能只弹出“已保存”而不进入安装向导。你需要手动进入 Settings > Security & privacy > More security settings > Encryption & credentials > Install a certificate > CA certificate,然后选择下载的 .pem 文件。注意,Android 14 还会弹出一个系统级警告,告诉你安装第三方 CA 证书的风险,并要求你输入锁屏密码确认。
安装完成后,打开你的应用。如果 targetSdkVersion 是 33 或 34,且应用没有自定义 network_security_config.xml,你的应用大概率仍然不信任这个证书。Charles 的 SSL Proxying 日志里会继续显示握手失败。这时候,你需要在应用的 res/xml/network_security_config.xml 里添加:
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>并在 AndroidManifest.xml 的 <application> 标签里声明 android:networkSecurityConfig="@xml/network_security_config"。这是 Android 官方文档里明确记录的调试手段,也是 Charles 在现代 Android 上能抓到包的最关键一步。不要试图去修改设备的 /system/etc/security/cacerts/ 目录来做全局系统证书,除非你的测试机已经 root 且不怕搞坏系统信任链,否则这条路费时费力且不可复现。
Charles 还有一个长期存在的坑:证书缓存。Charles 的根证书有有效期,旧版本 Charles 生成的证书可能只有一年有效期。如果你之前在一台手机上装过旧证书,后来 Charles 升级后重新生成了根证书,但手机上的旧证书没有清理,Android 可能会因为证书冲突或缓存导致握手异常。解决方法是 Help > SSL Proxying > Reset Charles Root Certificate,然后在手机上把所有旧的 Charles 证书从用户凭据里删掉,重新安装新的。
Proxyman 的架构差异与移动端适配
Proxyman 是近几年崛起的 macOS 原生抓包工具,官网 https://proxyman.io/,采用免费增值模式,基础抓包免费,高级功能如自动重写、断点、脚本和远程设备导出需要购买 Pro 授权,价格在 49 到 69 美元/年的订阅区间,也有一次性买断选项。它的界面和交互明显比 Charles 现代,基于 Apple 的 Network Extension 框架和原生网络栈,响应速度和内存占用远优于 Charles 的 Java 进程。
Proxyman 的证书管理逻辑和 Charles 不同。打开 Proxyman 后,它会自动生成一个根证书并尝试安装到 macOS 系统钥匙串。对于 Android 设备,你需要通过 Certificate > Install Certificate on Android 进入向导,Proxyman 会生成一个二维码或提供本地证书下载链接。你也可以手动导出证书,格式通常是 .pem 或 .cer。在 Android 端,证书安装后的信任问题与 Charles 完全一致:Android 7.0+ 的用户证书限制同样生效,Proxyman 的证书不会因为它的导出界面更美观而被系统特殊对待。
Proxyman 的代理默认监听在 9090 端口,而不是 Charles 的 8888。手机端配置代理时注意不要填错。Proxyman 对 iOS 模拟器和 Android 模拟器有更友好的自动配置支持,特别是 iOS Simulator,它可以一键安装证书到模拟器系统。但 Android 模拟器方面,由于 Android 模拟器的系统证书分区通常是只读的,且不同模拟器镜像(Google APIs vs. Google Play)对证书写入的权限不同,Proxyman 的一键安装功能在 Android 上经常需要配合 adb root 和 adb remount,实际体验并不比 Charles 手动推证书更顺畅。
Proxyman 真正强于 Charles 的地方在于过滤和脚本能力。它的 Scripting 和 Map Local 功能基于原生代码实现,延迟极低,而且支持用 JavaScript 编写拦截规则,这在 Charles 里对应的是 Map Remote 和 Breakpoints,但 Charles 的脚本能力相对原始,且需要额外学习它的 proprietary 配置语法。不过,如果你只是做 HTTPS 解密和基础抓包,Proxyman 的免费版已经够用。
Proxyman 有一个 Charles 很少出现的坑:网络冲突。因为 Proxyman 深度依赖 macOS 的 Network Extension,如果你的机器上同时运行着 VPN 软件(如 Shadowsocks、Clash、WireGuard 或公司内网 VPN),Proxyman 可能会和它们抢占网络扩展槽位,导致手机代理虽然连接成功,但没有任何流量进入 Proxyman 的 Flow 列表。这种冲突没有明确的错误日志,只是表现为“连上了,但抓不到包”。Charles 因为走的是传统 HTTP 代理 Socket 监听,和 VPN 软件的冲突反而少一些,虽然它也要改系统代理设置,但退出的清理机制更粗暴,通常不会残留路由规则。
Android 14 的额外限制与证书格式
Android 14 对第三方 CA 证书的安装做了进一步收紧。在旧版本 Android 上,通过浏览器下载 .pem 文件后点击通知栏的下载项可以直接进入证书安装界面。Android 14 要求用户必须进入系统设置的安全面板手动安装,而且不再允许应用隐式地调用证书安装 intent。这对 Charles 和 Proxyman 的教程都提出了新要求:你不能只告诉用户“下载证书并点击安装”,而必须明确说明进入 Settings > Security & privacy > Encryption & credentials 的路径。
另外,Charles 导出的 .pem 文件在 Android 14 上如果直接安装失败,可以尝试把后缀改为 .crt,因为某些 Android 皮肤(尤其是 Samsung One UI 和小米 HyperOS)对 MIME 类型的判断不够宽松,.pem 被识别为普通文本而不是证书。Proxyman 导出的证书格式更规范,但在某些国产 ROM 上也存在同样的后缀识别问题。
HTTPS 解密的硬边界:SSL Pinning 与双向认证
即使你完整配置了 Charles 或 Proxyman 的证书,并且用 network_security_config.xml 把用户证书加入信任链,仍然有大量流量无法解密。最常见的原因是 SSL Pinning(证书绑定)。很多应用在生产环境使用 OkHttp 的 CertificatePinner 或者 TrustManager 的自定义实现,直接比对服务器证书或公钥的哈希,而不是依赖系统信任链。这种情况下,Charles 和 Proxyman 的自签证书虽然被系统信任,但应用代码层面的校验会拒绝握手,你看到的错误通常是 SSLPeerUnverifiedException 或 Certificate pinning failure。
Charles 和 Proxyman 本身都不提供绕过 SSL Pinning 的内置功能。要解决这个问题,通常需要借助 Frida、Objection 或 Xposed 框架在运行时 hook 应用的 X509TrustManager 实现。这已经不是抓包工具的配置范畴,而是逆向工程领域。很多开发者误以为 Charles 装了证书就应该万能,这是不对的。Charles 和 Proxyman 能解密的是“信任系统 CA 链”的 HTTPS 流量,对于做了证书绑定的应用,它们无能为力。
另一个极端情况是双向认证(mTLS)。如果服务器要求客户端提供证书,Charles 作为中间人无法提供原应用私钥签名的客户端证书,连接会在服务器侧被拒绝。这种情况在企业内网应用或某些金融 SDK 中比较常见,遇到时直接放弃抓包,改用应用层日志输出更为现实。
性能、稳定性与日常使用的权衡
在日常开发中,Charles 的稳定性经受住了多年考验,但它的 Java 进程内存占用偏高,长时间运行后可能出现 UI 卡顿,特别是在处理高并发视频流或大量 WebSocket 消息时。Charles 的 WebSocket 支持是完整的,但界面刷新效率不高。Proxyman 在处理现代 HTTP/2 和 WebSocket 流量时明显更流畅,而且它的请求体预览支持 JSON Tree、Protobuf 解码和 GraphQL 语法高亮,这些对移动开发调试非常实用。
价格方面,Charles 的 50 美元买断制对长期开发者很友好,一次购买,跨平台可用(Windows、macOS、Linux)。Proxyman 的免费版已经覆盖大部分基础需求,但如果你想使用自动重写规则、导出 Swagger 文档、或多人团队协作同步配置,就得进入订阅循环。我个人觉得,如果你的主要工作机在 macOS,且只抓包不解密高强度二进制协议,Proxyman 的免费版配合手动证书安装已经足够。如果你需要频繁在 Windows 和 macOS 之间切换,或者需要用到 Charles 的 Throttle Settings(弱网模拟)和 Reverse Proxies,Charles 的许可证更划算。
当工具失效时的替代思路
Charles 和 Proxyman 并非唯一选择。在服务器或 CI 环境里,没有 GUI 可供点击,这时通常用 mitmproxy 或 tcpdump 配合 SSLKEYLOGFILE。对于 Android 开发,如果只是想看自家应用的 HTTPS 请求,从 Android Studio 的 Network Inspector(旧版叫 Network Profiler)可以直接查看明文流量,完全不需要安装根证书。它的原理是 Android Profiler 通过 JVMTI 在应用进程内部插桩,不依赖系统级证书信任,因此不受 Android 7.0 用户证书限制的约束。缺点是它只能调试 debug 签名且开启 profiling 的应用,对第三方应用或 release 包无效。
另一个值得关注的工具是 adb 的内置功能。Android 11 以上的系统支持 adb shell cmd activity memory-use 等命令,但抓包方面仍然需要 proxy。Android 10 引入的 pac_proxy 和 global_http_proxy 开发者选项也可以配合 Charles 做全局代理,但本质上还是绕回证书问题。
真实场景中的配置顺序
如果你现在有一台 Pixel 8 或小米 14,要抓一个 targetSdkVersion 34 的 Android 应用的 HTTPS 包,我建议按以下顺序操作,不再重复那些过时的 Android 6.0 教程。
先在桌面端启动 Charles 或 Proxyman。以 Charles 为例,确认 Proxy > macOS Proxy 已开启,记录下 Help > Local IP Address 里的局域网 IP。打开 SSL Proxying Settings,确保 Enable SSL Proxying 开启,并且 Include 里至少有一条 __PLACEHOLDER_ITALIC_0__。在手机 Wi-Fi 设置里配置手动代理,填入 IP 和 8888 端口。用浏览器访问 chls.pro/ssl,下载证书。进入 Android 设置的安全面板,找到 Install a certificate > CA certificate,选择下载的文件,输入锁屏密码完成安装。
接着,确保你的应用是 debug 包,在 res/xml/network_security_config.xml 里加入信任用户证书的声明,manifest 中指向该配置。重新编译安装应用。打开应用触发网络请求。如果 Charles 的 Structure 或 Sequence 视图里出现了明文 URL 和 JSON 响应体,说明配置成功。如果依然显示 unknown,检查 Charles 的 SSL Proxying 日志,看具体是握手失败还是证书校验失败。
使用 Proxyman 时,逻辑完全一致,只是代理端口改为 9090,证书下载路径在 Certificate > Install Certificate on Android 中。Proxyman 的界面会提示你当前证书的有效期和 SHA-256 指纹,可以在 Android 的 Trusted credentials > User 中核对,确保安装的是同一张证书。
证书装完、代理配好,如果还是抓不到包,大概率是应用做了 SSL Pinning,或者你的 targetSdkVersion 已经触发了最新 Android 版本的证书限制。回到代码里加 network_security_config 是最省时的办法,而不是在代理工具里反复折腾。