先看结论与判断条件

  • 签名验证范围从应用实际支持的最低系统到交付上限确定,不能只看 targetSdk 或当前开发机版本。
  • 不同 Android 版本可采用不同签名方案和验证路径,单个总体 success 会掩盖低版本或轮换边界。
  • apksigner 的 SDK 范围参数用于评估指定平台范围,完整命令、版本、输出和候选摘要必须作为结构化回执保存。
  • v3.1 的轮换最低 SDK 与历史安装基础相关,需把旧证书升级和新安装分别纳入矩阵。
  • v4 idsig 服务增量安装且依赖配套 v2 或 v3,不能用 v4 回执替代 APK 内部签名验证。
  • 工具矩阵证明的是静态签名验收,真实安装、升级、split 会话和业务启动仍要由设备矩阵独立取证。

直接答案:签名方案选择随平台版本变化,范围必须显式验证

Android 应用签名不仅回答文件是否被签名,还承担安装与更新身份。应用支持多个系统版本时,不同平台可能走不同签名方案、证书轮换规则和安装路径。只在一台主机上执行不带范围解释的 verify,最多证明工具对默认评估条件给出结果,不能证明最低支持版本到最新交付版本都能按预期验证。

正确做法先冻结最终 APK 的 SHA-256、minSdk、targetSdk、实际交付上限、允许证书和轮换策略,再选择覆盖方案边界的 SDK 点。每个点运行相同版本 apksigner,保存完整 stdout、stderr、退出码、v1 至 v4 状态和 signer 摘要。任一点失败都保持 blocked,不能只保留最后一次成功。

静态矩阵后还要执行真实设备安装与升级。apksigner 模拟平台签名验证条件,不运行 Package Manager 的全部安装状态,也不覆盖厂商实现、历史已装版本、split 会话和业务启动。最终报告要把工具通过与设备通过分成不同证据等级,避免把其中一类回执冒充整体兼容。

签名 SDK 范围门禁的四类输入
输入决定什么常见缺口保存方式
最终 APK全部回执绑定对象签名后又修改SHA-256 与字节数
minSdk最低系统验证路径只读取 targetSdkmanifest 与构建配置
交付上限最新目标范围默认等于开发机发布策略版本
方案集合各 SDK 可用签名路径只看 signed=truev1 到 v4 分项
允许证书批准发布身份测试证书混入完整证书摘要
历史基线升级与轮换路径只测全新安装旧版本候选摘要

minSdk、targetSdk 和验证上限承担不同职责

minSdk 描述应用声明支持的最低平台,是签名兼容矩阵的起点。若产品仍允许旧系统安装,门禁就必须保留对应签名路径和证书兼容证据。仅因为主力用户使用新系统而跳过最低版本,会把声明支持与实际验证脱节,直到升级或渠道安装时才暴露。

targetSdk 主要影响平台行为兼容和政策要求,不等于签名验证的唯一版本。一个 targetSdk 较新的应用仍可能声明更低 minSdk,而同一 APK 在不同系统上采用的签名方案验证能力不同。矩阵应该读取 manifest 与发布策略,不能把 targetSdk 当成最小或最大验证点。

验证上限应来自真实交付范围,例如当前认证的最新 Android 版本或计划上线的预览范围,而不是 apksigner 所在主机的操作系统。上限变化、minSdk 提升或轮换策略调整都要产生新矩阵版本。没有实际设备回执时,只能登记工具评估通过,不能声称最新系统业务兼容。

SDK 字段与证据边界
字段用途不代表变化后动作
minSdk最低声明支持平台最低设备已测试补齐工具和设备点
targetSdk目标行为与政策唯一验签版本重跑行为和签名门禁
compileSdk编译 API 范围运行平台范围只更新构建证据
交付上限当前发布测试顶部未来平台永久兼容新增最高版本点
设备 API真实运行环境所有厂商实现记录型号与构建
工具 SDK 参数静态验证假设设备实测保存命令与输出

v1 与 v2 的覆盖模型不同,低版本路径不能被新方案遮住

Android 应用签名文档说明不同方案面向不同平台版本。v1 采用 JAR 风格条目签名,v2 使用 APK Signing Block 覆盖文件主体。现代 APK 可以同时包含多个方案,让不同系统选择可理解的路径;矩阵需要确认每个支持 SDK 都存在可接受方案,而不是只确认最高方案存在。

v2 设计包含防止降级到较弱方案的机制,并覆盖 APK 文件主体,但不覆盖 APK 外部资产。门禁要保存 v2 的存在与验证结果,同时确认低版本所需路径没有因配置、后处理或签名参数改变而缺失。高版本验证通过不能自动补偿最低系统无法识别或无法接受的签名。

方案状态建议使用 present、verified、failed、not-supported 和 not-required,而不是一个布尔 signed。缺少方案可能符合某个 SDK 的策略,也可能让另一个 SDK 无法安装;只有把状态与验证点关联,审核者才能知道缺失是设计选择还是发布错误。

签名方案状态的矩阵表达
状态含义允许使用条件禁止推论
present候选包含该方案还需验证结果方案有效
verified工具在该范围接受绑定候选与工具设备已安装
failed范围验证失败不得发布业务代码有漏洞
not-supported该 SDK 不理解方案另有可接受路径全范围失败
not-required策略不要求该方案理由与范围明确方案可随意删除
unknown证据不足保持 blocked按成功处理

v3.1 轮换要分别验证新安装与历史升级路径

APK Signature Scheme v3.1 描述签名轮换、最低轮换 SDK 和与 v4 的关联。轮换后,同一发布可能需要为较新平台使用新 signer,同时让较旧平台继续识别兼容身份。矩阵不能只验证新 APK 自身,还要记录批准证书、lineage、最低轮换 SDK 和历史安装基线。

新安装与升级是两种不同用例。新安装关注候选在目标 SDK 是否能建立当前身份;升级关注设备上既有版本的证书和 lineage 是否允许迁移到新候选。只做卸载后的全新安装会清除关键历史状态,无法证明真实用户升级路径。

轮换矩阵至少包含轮换边界之前一个 SDK、边界 SDK、边界之后一个 SDK,以及产品最低与最高验证点。历史包要使用准确归档候选,保存 SHA-256 和证书摘要,不能用重新构建或重新签名的同版本包替代。没有历史候选时应标记证据缺失,而不是推测升级会成功。

v4 与 idsig 是增量安装支线,不能替代内部方案矩阵

v4 使用独立 idsig 支持增量安装,并依赖配套 v2 或 v3 签名。验证矩阵应先确认 APK 内部方案和证书,再对采用增量安装的 SDK 点记录 idsig 与准确 APK 的双摘要、生成工具和安装回执。只有 idsig 文件存在,不足以确认配对或安装身份。

并非所有分发路径都使用 v4。普通文件安装、商店交付、开发调试和增量安装的工件角色不同,报告应注明 installationMode。若某个 SDK 点不走增量安装,可以把 v4 标成 not-required 并写明理由,但不能据此省略 v2 或 v3 的内部签名验证。

APK 在 idsig 生成后发生任何字节变化,都产生新候选并使配对回执失效。矩阵中的 APK 摘要必须与 idsig 清单和 apksigner 输出一致。相同版本名或 versionCode 不能证明字节相同,也不能让旧 idsig 复用于新签名候选。

验证点要覆盖方案边界,不必机械穷举所有 SDK

高价值矩阵从平台方案边界、产品 minSdk、轮换最低 SDK、目标上限和渠道特殊点中选取。每个边界至少包含边界前、边界本身和边界后,去重后形成验证列表。若中间版本存在厂商缺陷、历史事故或安装模式变化,再加入代表点,理由保存在清单中。

不是所有 SDK 点都需要同等设备数量,但每个声明支持范围都必须有可辩护覆盖。工具矩阵可逐点运行且成本较低,设备矩阵则按方案边界、主流厂商、架构与升级基线分层抽样。未测试组合明确标记 not-tested,不能用相邻版本通过自动填充。

矩阵还要记录失败归因。签名方案缺失、证书不符、lineage 错误、idsig 错配、安装 API 限制、ABI 问题和业务启动崩溃属于不同类别。把所有失败写成签名失败会误导修复,也会把 VMP 或加固变更背上没有证据的因果。

SDK 验证点选择方法
点位为何选择工具门禁设备门禁
minSdk声明支持起点必跑至少一台真实设备
方案边界前旧验证路径必跑按风险抽样
方案边界平台能力切换必跑必测安装升级
轮换最低 SDK证书身份切换必跑新装与升级
渠道特殊点安装模式不同必跑对应工件真实渠道路径
交付上限当前最高平台必跑认证设备

主机验签、设备安装和 CTS 兼容性证据不能混为一谈

apksigner 是 Android 官方签名工具,可以根据 SDK 范围验证方案和证书。它适合在构建流水线快速发现静态签名问题,但不启动 Package Manager,不保留用户设备上的历史安装状态,也不运行应用业务。因此命令 exit 0 只能登记工具门禁通过。

真实设备回执应包括设备型号、系统 API、构建指纹、安装模式、旧包摘要、新包摘要、命令与退出状态、Package Manager 错误、首次启动和关键流程。升级测试不应先卸载旧包,轮换测试必须使用准确历史 signer。崩溃、启动页出现或安装完成都不是完整业务通过。

CTS 用于验证设备实现与 Android 兼容性定义的一致性,它提供重要平台基础,但设备通过 CTS 不能证明第三方 App 加固后的业务兼容。项目仍要在自己的候选、依赖和流程上取证,尤其关注签名后处理、原生库加载、split 与密钥轮换。

用 apksigner 逐 SDK 生成结构化验证回执

下面的 Python 示例接收最终 APK、最低 SDK 和最高 SDK,逐点调用 PATH 中固定的 apksigner verify,并传入相同的 min 与 max SDK 参数,使每条记录对应一个明确平台版本。脚本限制矩阵宽度、验证 APK 存在、计算 SHA-256,并保存退出码及输出摘要。

代码不签名、不修改 APK、不读取私钥,也不执行安装。任一 SDK 返回非零状态时,脚本输出完整 JSON 后以失败结束,便于流水线保留全部点位而不是在首个错误处丢失上下文。生产环境还应记录 build-tools 完整路径与文件摘要,并对输出中的证书信息做结构化解析。

所有点通过只说明 apksigner 对指定范围接受该候选,不代表设备矩阵完成。下游必须把 JSON 回执与同一 APK SHA-256 绑定,并追加真实安装、升级、轮换和增量安装证据。若 APK 在命令后被重签或修改,整个矩阵回执立即失效。

逐 SDK 运行 apksigner verify 的只读矩阵脚本
import hashlib
import json
import shutil
import subprocess
import sys
from pathlib import Path

if len(sys.argv) != 4:
    raise SystemExit("usage: verify_sdk_matrix.py application.apk min_sdk max_sdk")
+apk_path = Path(sys.argv[1]).resolve()
+if not apk_path.is_file():
+    raise SystemExit(f"APK missing: {apk_path.name}")
+try:
+    min_sdk = int(sys.argv[2])
+    max_sdk = int(sys.argv[3])
+except ValueError as error:
+    raise SystemExit("SDK bounds must be integers") from error
+if min_sdk < 1 or max_sdk < min_sdk or max_sdk - min_sdk > 20:
+    raise SystemExit("SDK range must be ordered and contain at most 21 points")
+apksigner = shutil.which("apksigner")
+if apksigner is None:
+    raise SystemExit("apksigner was not found on PATH")
+
+apk_sha256 = hashlib.sha256(apk_path.read_bytes()).hexdigest()
+records = []
+failed = []
+for sdk in range(min_sdk, max_sdk + 1):
+    command = [
+        apksigner, "verify", "--verbose", "--print-certs",
+        "--min-sdk-version", str(sdk),
+        "--max-sdk-version", str(sdk),
+        str(apk_path),
+    ]
+    result = subprocess.run(command, text=True, capture_output=True, check=False)
+    record = {
+        "sdk": sdk,
+        "exit_code": result.returncode,
+        "stdout": result.stdout.splitlines(),
+        "stderr": result.stderr.splitlines(),
+    }
+    records.append(record)
+    if result.returncode != 0:
+        failed.append(sdk)
+
+receipt = {
+    "apk_name": apk_path.name,
+    "apk_sha256": apk_sha256,
+    "apksigner_path": apksigner,
+    "range": {"min": min_sdk, "max": max_sdk},
+    "records": records,
+    "failed_sdks": failed,
+    "next_gate": "run install and upgrade tests on the selected device matrix",
+}
+print(json.dumps(receipt, ensure_ascii=False, indent=2))
+if failed:
+    raise SystemExit(f"signature verification failed for SDK points: {failed}")

发布报告要保留矩阵缺口,不用一次成功填平未知项

最终证据包包含 APK 摘要、manifest SDK 字段、签名配置、apksigner 版本与摘要、逐点原始输出、证书摘要、lineage、idsig 配对、历史基线、设备矩阵和异常归因。所有材料必须引用同一最终候选,签名后任何修改都会使旧回执失效。

可以公开陈述某个候选在指定 SDK 点的工具验证结果和真实设备结果,不能写成全 Android 兼容、签名绝不会失败或轮换对所有历史用户安全。没有候选与回执时,本文只提供矩阵设计,不虚构客户、性能、攻击阻断、收录或排名数据。

准备签名矩阵前可查看[APK 哈希与签名验证的区别](/zh-cn/articles/apk-hash-vs-signature-verification/),避免把文件摘要当作签名身份。需要提交最终候选与 SDK 范围,可从[御盾中央平台申请加固服务](https://www.leonadev.com/console/)。登录、注册、价格、购买和控制台统一由中央平台承接,提交时不得包含私钥。

  • 矩阵起点来自 APK 实际 minSdk,终点来自当前交付上限
  • 每个签名方案边界至少覆盖前一版、边界版和后一版
  • apksigner 命令、版本、完整输出和候选摘要已保存
  • v1、v2、v3、v4 与 signer 状态按 SDK 分项记录
  • 轮换路径同时覆盖全新安装与准确历史包升级
  • v4 idsig 与 APK 双摘要绑定且不替代内部验签
  • 工具通过和真实设备安装、启动、业务通过分开登记
  • 未测试、失败和证据缺失保持显式状态,不自动填充

事实依据与适用边界

以下内容区分官方事实、本文工程判断和不能外推的范围,避免把设计建议写成未经验证的产品结论。

本文判断事实或工程依据适用限制
Android 使用应用签名建立更新身份,并由不同签名方案覆盖不同平台版本。AOSP app signing 说明 Android 应用签名总体职责与方案演进。签名有效只证明完整性与签名者身份范围,不证明业务代码安全。
v2 通过 APK Signing Block 覆盖文件主体并包含防降级机制。APK Signature Scheme v2 说明 v2 的文件覆盖和降级保护设计。v2 不覆盖 APK 外部资产,也不能证明设备安装与业务兼容。
v3.1 描述签名轮换、最低轮换 SDK 及其与 v4 的关联。APK Signature Scheme v3.1 说明轮换签名块与平台版本边界。轮换能力仍受分发渠道、允许证书和历史安装基础约束。
v4 使用独立 idsig 支持增量安装,并依赖配套 v2 或 v3 签名。APK Signature Scheme v4 说明 idsig、增量安装用途和依赖关系。idsig 不能替代 APK 内部签名,也不是所有分发路径的唯一身份。
apksigner 可验证方案覆盖与证书,并支持显式 SDK 范围参数。Android apksigner 说明 verify、min/max SDK、证书和签名后修改边界。工具成功不证明 keystore、渠道、设备安装或业务启动正确。
CTS 用于验证设备实现与 Android 兼容性定义的一致性。Android CTS overview 说明 CTS 的目的、测试对象与兼容性角色。设备通过 CTS 不代表第三方 App 加固后的业务兼容通过。
签名矩阵应覆盖 minSdk、方案边界、轮换边界和交付上限。工程判断:这些点最可能改变平台可用方案、证书身份或安装路径。点位选择仍要结合项目声明、渠道、历史包和真实设备证据。
工具矩阵和设备矩阵必须保持不同证据等级。工程判断:主机验签不运行 Package Manager 历史状态、厂商实现和业务流程。设备单次安装通过也不能外推所有厂商、系统和升级基线。
历史升级验证必须使用准确归档候选,不能重新构建替代。项目证据尚未接入:这是轮换与更新发布前必须满足的身份门禁。没有历史候选时应登记缺口,不声称真实用户升级已通过。
本文不提供性能、客户、排名、收录、兼容或攻击阻断结论。项目证据尚未接入:缺少真实候选、工具回执、设备矩阵和公开运营数据。文章只提供可复核矩阵方法,不能作为具体产品效果证明。

工程常见问题

apksigner verify 返回成功为什么还要指定 SDK 范围?

应用支持多个系统版本,不同版本可采用不同签名路径。显式范围能暴露低版本、方案边界和轮换条件。

签名矩阵只验证 targetSdk 可以吗?

不可以。targetSdk 不等于运行范围,矩阵应从实际 minSdk 覆盖到当前交付上限。

是否必须机械验证每一个 Android API?

工具逐点成本可控,设备则优先覆盖 minSdk、方案边界、轮换边界、渠道特殊点和最高版本,并明确未测组合。

v2 验证通过能否忽略 v1?

取决于支持的最低系统和发布策略。高版本方案通过不能自动补偿旧系统缺少可接受签名路径。

证书轮换只做新安装测试够吗?

不够。必须使用准确历史包测试升级,因为卸载后的新安装不会覆盖既有证书与 lineage 状态。

v4 idsig 验证通过能否代替 v2 或 v3?

不能。v4 服务增量安装并依赖配套 v2 或 v3,APK 内部签名仍需独立验证。

设备通过 CTS 是否表示加固 APK 一定兼容?

不表示。CTS 证明设备实现的兼容性基础,具体 APK 的安装、升级、启动和业务仍需项目测试。

矩阵脚本全部通过能否登记发布完成?

不能。它只完成工具门禁,还需内部候选一致性、真实设备安装升级、业务流程和发布归档回执。

想用自己的 App 验证?

提交候选包、目标系统和关键业务路径,申请御盾 PoC 与兼容性评估。

继续阅读: APK、DEX 与签名完整性如何一起验收