0. 一图速览(签名覆盖与"签名块")
APK 结构(末尾)
javascript
... [文件内容区] ... | APK Signing Block | ZIP Central Directory | EOCD
^ 仅 v2+ / v3 / v3.1 存在
- v1(JAR) :逐个文件计算摘要,写进 META-INF/(MANIFEST.MF/.SF/ .RSA)。不覆盖 ZIP 元数据;安装校验要逐条目解压校验。
- v2+(v2 / v3 / v3.1) :在 Central Directory 前 插入"APK Signing Block",对除签名块本身外的全包字节 分块哈希并签名,任何对包体/ZIP 元数据的改动都会使签名失效,校验更快。
1. v1(JAR 签名)------逐文件签名,兼容老设备
工作原理(验证链)
-
校验 META-INF/.SF 与 .RSA/DSA/EC 的关系(证书 + 对 .SF 的签名)。
-
.SF 引用 MANIFEST.MF 中的各条目摘要;MANIFEST.MF 里记录**每个 ZIP 条目(解压前或后按实现)**的摘要。
-
逐条目比对摘要,未列入清单的条目不受保护,ZIP 元数据也不受保护。
作用与局限
-
作用:确证开发者身份;支撑 Android 6.0 及以下 的安装。
-
局限:攻击面大且校验慢(需要解析大量"尚未信任"的 ZIP 结构并解压比对),不能发现"仅改 ZIP 目录/对齐"的篡改。
官方因此在 Android 7.0 引入 v2 以提升完整性与性能。
2. v2(APK Signature Scheme v2)------整包分块签名,快且更牢
签什么
- 在 APK Signing Block 中保存 v2 签名数据;对除签名块外的几乎整包 按块(典型 1MB)做摘要,再对摘要集签名。改动任何字节或 ZIP 元数据都会破坏签名。
怎么验
- 安装器把 APK 当成整体 blob 校验(而非逐条目),因此显著加快。
作用
- 强完整性 + 快校验;并通过在 v1 元数据里声明"此 APK 也有 v2 签名",防降级(剥离 v2 仅按 v1 验) 。
3. v3(APK Signature Scheme v3)------在 v2 基础上加入"密钥轮换"
新增能力
-
在签名数据里加入:
-
minSdk / maxSdk 元信息;
-
Proof-of-Rotation(签名谱系) :一条"旧证书 → 新证书 ..."的单链 ,声明"新证书继承旧证书的信任"。这让应用安全更换签名证书而不破坏依赖"签名相同"判断的系统行为(签名级权限、签名比较、历史共享等)。
-
验证与优先级
- Android 9+:先验 v3,失败再回退 v2,再回退 v1(老设备看不见 v3)。
限制
- 多签者发布不支持 (Google Play 也不发布多证书签名应用);v3 是单链轮换模型,不是任意图结构。
4. v3.1(Android 13+)------"定向生效"的轮换修复
要点
- v3.1 签名数据与 v3 内容等价 ,但放在新的 Block ID 下:只有 Android 13+ 识别 ;旧系统会忽略 v3.1 块、继续使用 v3 块中的原签名 。这样就能在 13+ 切换到新密钥,同时不触发旧系统的历史兼容问题。
典型用法
-
同一 APK 同时携带:
- v3 块:老密钥,给 Android 12− 使用;
- v3.1 块:新密钥,给 Android 13+ 使用。
-
构建工具支持设置轮换生效的最小 SDK(--rotation-min-sdk-version)。
5. v4(Android 11+)------增量/流式安装的"旁路签名"
签什么与放哪
- 生成独立的 *.apk.idsig 文件(不改 APK 本体 ),内容基于整包字节的 Merkle 哈希树(与 fs-verity 结构一致)。
怎么用
- ADB/安装器可用它做流式/增量验证 (如 adb install --incremental)。设备/环境不支持时会回退普通安装。v4 需要配套 v2 或 v3 才能安装。
作用
- 明显加速大 APK的开发安装与分发验证,不改变 v2/v3 的完整性保障职责。
6. 平台支持与选择建议
平台识别
-
< Android 7.0 :只认 v1。
-
Android 7.0--8.x :优先 v2;无 v2 则回退 v1。
-
Android 9+ :优先 v3 → v2 → v1。
-
Android 13+ :还能识别 v3.1 (定向轮换);Android 11+ 支持 v4 增量安装。
配置策略(发布面广)
- 需要兼容老设备:v1 + v2 + v3 ;计划在 13+ 轮换密钥再加 v3.1 ;开发/大包按需加 v4。
7. 工程实操
7.1 Gradle(AGP)开关示例
arduino
android {
signingConfigs {
release {
// 兼容旧机就开 v1;现代发布至少 v2+v3
v1SigningEnabled true
v2SigningEnabled true
v3SigningEnabled true
v4SigningEnabled true // 需要增量安装/流式分发时开启
}
}
}
(AGP 具体行为以版本为准;底层仍由 apksigner 完成签名与校验策略。)
7.2apksigner常用命令
- 签名与校验
arduino
apksigner sign --ks release.jks app.apk
apksigner verify -v --print-certs app.apk
- 密钥轮换(生成谱系 + 设定最小生效 SDK;33 即 13+ 触发 v3.1)
css
# 生成/更新轮换谱系(lineage)
apksigner rotate --out lineage.sig --old-signer --ks old.jks \
--new-signer --ks new.jks
# 使用旧+新签名,附带谱系;指定轮换最小 SDK
apksigner sign --ks old.jks --next-signer --ks new.jks \
--lineage lineage.sig --rotation-min-sdk-version 33 app.apk
(apksigner 会根据 rotation-min-sdk-version 选择写入 v3 还是 v3.1 块。)
7.3 v4 增量安装(开发提速)
bash
# 先生成 idsig(AGP/adb 新版可自动生成)
adb install --incremental app.apk # 需要同目录下 app.apk.idsig
(设备/工具不支持则回退普通安装。)
8. 常见坑与排障
- 签后又改包 :v2+/v3 把整包当 blob 签名;zipalign/二次压缩/改注释/重排目录 都会破坏签名。务必"先 zipalign,后 apksigner" (AGP 已处理)。
- 只签 v2 不带 v1 :在 7.0 以下无法安装(如还需兼容老机,务必带 v1)。
- 轮换后旧系统出兼容问题 :用 v3.1(仅 13+ 生效)+ --rotation-min-sdk-version,让旧系统继续沿用旧密钥,13+ 才切换。
- 多证书发布 :Android 9+ v3 文档明确不支持多签者,Play 也不发布多证书签名的 App。
- 为何 v2 更安全 :因为 APK Signing Block 靠近末尾,覆盖(除签名块外的)所有字节+ZIP 元数据;相比 v1,能发现"只改 ZIP 中央目录/EOCD"的篡改且校验更快。
9. 结论与落地建议
-
默认组合:v2 + v3;需要兼容 6.0− 再加 v1;计划 13+ 轮换钥时加 v3.1;大包/开发提速按需 v4。
-
流程纪律 :构建顺序先对齐后签名;签名后禁止再改 APK;使用 apksigner verify -v 做发布前机型范围校验。
-
密钥治理 :提前规划 proof-of-rotation ,并把 SHA-256 指纹同步到外部依赖(如 App Links 的 assetlinks.json 等)。
需要的话,我可以把这份说明转成你们团队的 发布自检清单 (含 Gradle 片段、CI 步骤、apksigner 命令与轮换 SOP),或根据你们 minSdk / 目标市场 / 是否计划换钥 直接给出最小变更建议。