3、签名机制基础:数字签名原理、Android签名方案v1、v2、v3、v4对比

聊到Android安全,签名机制是绕不开的一环。说白了,签名就是APK的身份证。没有它,系统根本不知道这个App是谁发的,有没有被篡改过。我刚开始接触Android安全时,总觉得签名就是个校验过程,后来踩过坑才明白——这里面的门道,远比想象中深。

3.1 数字签名原理:先搞懂它是什么

数字签名不是加密,而是「防伪技术」。你想想看,我写一封信,怎么证明这封信是我写的,而且中途没人改过?

数字签名就是干这个的。它用到了非对称加密:

  • 私钥:只有开发者自己知道,用来签名
  • 公钥:公开的,任何人都可以用它来验证签名

流程其实很简单:

  1. 开发者用私钥对APK的摘要(Hash值)进行加密,生成签名
  2. 签名和公钥一起打包进APK
  3. 安装时,系统用公钥解密签名,拿到摘要A
  4. 系统再对APK内容计算摘要B
  5. 对比A和B,一致则通过,否则拒绝安装

核心要点:签名校验的本质是「摘要比对」。只要摘要对得上,就说明内容没被改过,而且签名者确实持有私钥。

嗯,这里要注意:私钥一旦泄露,别人就能冒充你签名。我在项目中遇到过某厂商的签名私钥被内部员工泄露,结果市场上出现了大量伪造的官方App,用户数据被窃取。那叫一个惨烈。

3.2 Android签名方案演进:从v1到v4

Android的签名方案不是一蹴而就的。从早期的v1到现在的v4,每个版本都在解决不同的问题。我个人习惯把它们分成两代:

  • v1(JAR签名):古老但兼容性最好
  • v2(APK签名方案):性能更好,安全性更高
  • v3(密钥轮转):支持更换签名密钥
  • v4(增量更新):配合APK增量更新使用

下面这张表,我建议你收藏起来,面试时经常被问到:

特性 v1 v2 v3 v4
引入版本 Android 1.0 Android 7.0 Android 9.0 Android 11.0
签名对象 ZIP条目 整个APK文件 整个APK文件 APK的Hash
签名存储 META-INF目录 APK签名块 APK签名块 单独签名文件
支持密钥轮转
增量更新支持
校验速度 极快

3.3 v1签名:老古董但还在用

v1签名,也叫JAR签名。它是对APK里的每个文件单独签名,签名信息放在META-INF目录下。你解压一个APK,看到MANIFEST.MFCERT.SFCERT.RSA这些文件,就是v1签名的产物。

为什么说它不安全?因为v1只校验ZIP条目,不校验ZIP文件头。这意味着,攻击者可以删除META-INF目录,或者往APK里添加新文件而不破坏签名。我曾经在逆向一个恶意App时,发现它就是用v1签名的漏洞,在APK里插入了额外的DEX文件,绕过了签名校验。

避坑指南:如果你还在用v1签名,建议尽快升级。Android 11及以上版本已经默认禁用v1签名。我曾经见过一个老项目,因为一直用v1签名,在Android 12上安装失败,排查了半天才发现是签名问题。

3.4 v2签名:目前的主流方案

v2签名是Android 7.0引入的。它不再对单个文件签名,而是对整个APK文件进行签名。签名信息嵌入在APK文件中间的「APK签名块」里,而不是放在META-INF目录。

这样做的好处很明显:

  • 校验速度更快:只需要一次Hash计算,不用遍历所有文件
  • 安全性更高:任何对APK的修改都会破坏签名,包括ZIP文件头
  • 支持多签名:可以同时使用多个签名密钥

v2签名的APK结构是这样的:

APK文件结构:
┌─────────────────────┐
│ ZIP条目(内容区)    │
├─────────────────────┤
│ APK签名块(v2/v3)  │
├─────────────────────┤
│ ZIP中央目录         │
├─────────────────────┤
│ ZIP结束记录         │
└─────────────────────┘

你想想看,v2签名把签名信息放在文件中间,而不是末尾。这样设计是为了兼容ZIP格式——ZIP解析器从末尾开始读,而签名校验从中间开始读,互不干扰。

个人经验:我在做App加固时,经常需要处理v2签名。加固后的APK必须重新签名,而且要用v2方案。如果只用v1签名,加固后的APK在Android 7.0以上设备上会安装失败。记住:apksigner sign --v2-signing-enabled true

3.5 v3签名:密钥轮转的救星

v3签名在Android 9.0引入,最大的亮点是支持密钥轮转。什么意思呢?就是你可以更换签名密钥,而不需要用户卸载重装App。

以前,如果你不小心丢了签名密钥,或者想换一个更安全的密钥,唯一的办法就是换包名重新发布。用户必须卸载旧App,再安装新App,数据全没了。

v3签名通过「签名块中的签名证书链」来解决这个问题。它记录了新旧密钥的继承关系,系统会验证新密钥是否由旧密钥签名授权。

// 使用apksigner进行密钥轮转
apksigner rotate \
  --out /path/to/rotation_file \
  --old-signer --ks old-keystore.jks \
  --new-signer --ks new-keystore.jks

// 使用新密钥签名并带上轮转信息
apksigner sign \
  --ks new-keystore.jks \
  --next-signer --ks old-keystore.jks \
  --rotation-file /path/to/rotation_file \
  app.apk

嗯,这里要注意:密钥轮转不是无限次数的。Android系统最多支持5次轮转。超过这个次数,系统会拒绝安装。我曾经在测试时连续轮转了6次,结果App死活装不上,查了半天文档才发现这个限制。

3.6 v4签名:为增量更新而生

v4签名是Android 11引入的,它不直接嵌入APK,而是生成一个单独的.idsig签名文件。这个文件包含了APK的Hash值和签名信息。

v4签名的核心用途是配合APK增量更新(APK Incremental)。增量更新允许用户只下载APK的变化部分,而不是整个APK。v4签名可以快速验证增量更新后的APK是否完整。

说实话,v4签名在日常开发中用得不多。它主要面向Google Play等大型应用商店。我建议你了解它的存在即可,不需要深入实现细节。

总结一下:

  • v1:老方案,不推荐单独使用
  • v2:当前主流,必须使用
  • v3:需要密钥轮转时使用
  • v4:增量更新场景使用

最佳实践:v2 + v3组合签名,兼容性和安全性都兼顾。

3.7 签名校验的常见攻击与防御

签名机制虽然安全,但也不是无懈可击。我在项目中遇到过几种常见的攻击方式:

  1. 签名劫持:攻击者用自己生成的签名替换原签名。防御方法:在代码中二次校验签名,比如检查签名证书的指纹。
  2. 重打包攻击:攻击者反编译APK,修改代码后重新签名。防御方法:使用加固方案,增加反调试和完整性校验。
  3. 签名伪造:利用v1签名的漏洞,在APK中插入恶意文件。防御方法:禁用v1签名,只使用v2/v3。

避坑指南:我曾经在做一个金融App时,发现攻击者通过Hook系统签名校验函数,绕过了签名检查。后来我们在Native层实现了签名校验,并且对校验结果进行了加密传输,才彻底解决这个问题。记住:永远不要完全信任系统的签名校验结果

好了,签名机制的基础就讲到这里。下一章我们会深入实战,看看如何在代码中实现签名校验,以及如何绕过常见的签名校验方案。你想想看,理解了这些原理,再去分析那些恶意App的签名绕过手法,是不是就清晰多了?