先看结论与判断条件

  • 文件摘要是字节指纹,必须先从可信记录获得期望值;攻击者同时替换 APK 和摘要时,单独比对仍会通过。
  • Android 签名验证还检查签名方案覆盖、文件完整性和签名证书,证书身份参与应用更新关系,责任不同于摘要。
  • 同一签名证书可以签署多个不同 APK,同一业务版本也可能产生不同字节,因此摘要和证书必须作为两个字段共同保存。
  • APK Signature Scheme v2 覆盖 APK 文件主体并处理降级保护,但不覆盖 APK 之外单独下载的模型、配置或资源。
  • apksigner 验证成功只说明工具对当前文件的签名判断,不证明密钥托管、渠道归档、构建来源或业务代码安全。
  • 供应链声明和 provenance 应引用当前 APK 摘要;门禁仍需核对声明签名者、构建者、证书允许列表及最终归档文件。

摘要证明字节相同,签名验证证明另一组事实

SHA-256 把 APK 文件映射为固定长度摘要。对同一组字节重复计算会得到同一结果,任何字节变化通常都会产生不同摘要。它非常适合给候选包编号、比较传输前后文件和绑定测试报告。但摘要本身没有发布者身份,拿到一份 APK 和旁边同来源的摘要文件,无法仅凭二者相等判断谁认可了该 APK。

Android 签名验证处理的是签名数据、证书和签名方案覆盖。验证器要确认当前 APK 的受保护区域没有在签名后被修改,并解析哪个证书参与签名。Android 还使用应用签名建立安装与更新身份。因此,摘要回答“是否是这组字节”,签名回答“这些字节是否满足特定签名规则并对应某个签名者”。

两者也不能互相推导。同一个证书可以签署不同版本和不同内容的 APK,所以证书相同不表示文件摘要相同;同一 APK 摘要在任何机器上计算都一致,但没有可信证书或可信外部记录时,不能说明文件来自预期发布方。工程验收应把期望摘要、实际摘要、签名方案结果和证书摘要分栏保存。

APK 摘要与签名验证的职责对照
检查对象能回答不能回答典型输入
文件 SHA-256当前字节是否匹配已知值谁发布并认可该值APK 与可信摘要
签名完整性签名覆盖区域是否被修改业务代码是否安全APK 与签名结构
签名证书文件由哪个证书身份签署密钥管理是否合规证书摘要允许列表
签名方案目标方案是否验证通过外部资产是否完整平台规则和目标版本
归档记录回执是否对应同一候选候选运行是否正确版本、摘要、证书和时间

摘要只有连接到可信期望值才有验收意义

计算实际摘要很容易,困难在于期望摘要从哪里来。若 APK 和 expected.sha256 来自同一个未受保护下载目录,目录被替换时两个文件可以一起变化,比较依然成功。期望值应来自受控发布记录、签名供应链声明或不可混淆的候选归档,并且记录本身要有访问控制、审计与身份验证。

摘要清单需要绑定文件名之外的候选信息,包括应用标识、版本、构建编号、签名流程阶段、生成时间和归档位置。文件名容易被复用,版本字段也可能因重建保持不变;真正消除歧义的是文件摘要。若重建、重新对齐、重新压缩或重新签名导致字节变化,即使源码提交和版本号未变,也应视为新的候选身份。

摘要比较适合早失败。下载后、扫描前、测试前、交付前都可以重新计算,并拒绝不匹配文件。但通过摘要门禁后仍要继续签名验证,因为期望摘要记录可能绑定了错误签名候选,或者归档流程把调试身份和发布身份混在一起。摘要把调查范围缩小到某份字节,不替代签名者判断。

可信摘要记录的组成
字段用途常见失误校验动作
实际 SHA-256标识当前字节从日志复制了旧文件摘要对待验 APK 现场计算
期望 SHA-256声明允许的候选与 APK 放在同一未保护目录从受控发布记录读取
应用与版本连接业务发布仅依赖可复用文件名与包内信息及流水线记录对照
生成阶段区分未签名与已签名文件对签名前文件留最终摘要只登记最终候选
归档身份定位原始文件测试后重新复制或改名保存不可混淆路径和回执

Android 应用签名还建立安装与更新身份

AOSP app signing 说明 Android 使用应用签名建立应用更新身份,并存在面向不同平台版本的签名方案。系统判断新 APK 是否可以作为已安装应用的更新时,会考虑签名关系,而不会查询项目保存的 SHA-256 文本。即便两个 APK 来自同一源码,只要签名身份不满足更新规则,也不能用摘要相近或版本相同弥补。

验收不能只记录证书主题名称。证书显示名称可重复或填写不严谨,适合比对的是签名证书的密码学摘要及其经过审批的身份记录。若工具报告多个签名者或多种方案,记录要完整保留,策略明确接受哪些证书和方案。本文不展开证书轮换流程,只强调当前候选必须与当前允许身份相匹配。

签名有效也不等于业务代码安全。合法发布密钥可以签署包含缺陷的 APK,构建环境也可能在签名前引入错误内容。签名验证证明当前文件满足签名完整性和签名者身份规则,代码审计、恶意行为分析、加固验证与设备回归仍需要独立证据。把签名通过写成“APK 安全”会扩大结论。

签名验收应保存的事实
事实来源通过含义仍需证据
签名验证结果apksigner 或平台验证器签名结构和完整性满足规则允许证书匹配
签名方案结果工具逐方案输出目标方案在当前文件有效目标平台覆盖策略
证书 SHA-256签名者证书解析识别当前签名身份证书允许记录来源
签名者数量工具证书输出知道文件包含哪些签名者多签名者策略是否允许
更新身份关系平台规则与发布记录候选满足预期更新关系真实设备升级回归

v2 签名覆盖 APK 主体,但不保护外部文件

APK Signature Scheme v2 通过 APK Signing Block 保护 APK 文件主体,并包含防止降级到较弱方案的机制。与只检查 ZIP 条目列表的自制脚本相比,标准验证器理解签名块、内容摘要和平台验证规则。发布门禁应调用可信 Android 构建工具中的验证器,而不是从 META-INF 是否存在若干文件来猜测签名状态。

Android apksigner 文档明确提醒签名后再修改 APK 会使签名失效。对齐、压缩、注入渠道字段或替换资源都应在签名前完成。若交付流程在签名后执行任何可能改写文件的步骤,必须重新验证摘要和签名;不能因为改动看似与代码无关就沿用旧回执。最终摘要应在所有字节修改和签名结束后计算。

v2 的覆盖边界是 APK。应用首次启动后下载的模型、规则、补丁或配置不在该 APK 签名保护范围内,它们需要自己的摘要、签名和更新策略。工程判断是按交付容器建立清单:APK 使用 Android 签名验证,外部资产使用独立的可信元数据和完整性机制,不能把 APK 验证结果扩展到运行期下载内容。

APK 内外资产的完整性责任
对象适用验证何时计算摘要不能沿用
最终 APKAndroid 签名方案与文件摘要签名及所有修改完成后签名前中间包回执
APK 内资源由 APK 签名覆盖并随包核对包含在最终 APK 摘要中单独旧资源摘要
外部模型独立签名或可信更新元数据下载完成且激活前APK 签名结论
远程配置服务身份、签名与版本策略接收后应用前安装包证书结论
测试报告绑定 APK 摘要和签名回执测试开始与归档时同版本其他候选结果

apksigner 回执要同时看方案和证书

Android apksigner 可以签名 APK,也可以验证签名方案覆盖并打印证书。验收侧应使用 verify、verbose 和 print-certs 等验证输出,检查命令返回状态、总体 Verified 结果、所需方案是否通过以及证书 SHA-256 是否位于允许集合。工具版本、命令参数和输入 APK 摘要应一起保存,避免回执脱离上下文。

只看命令退出成功仍可能漏掉策略问题。工具可以正确验证一个由非预期证书签署的 APK,或者文件对某些方案有效却不满足项目的目标平台策略。验证工具负责报告事实,发布策略负责决定是否接受。两者之间应有结构化解析层,禁止人工从长日志中只截取一行“Verified”。

工具验证成功也不证明 keystore 管理、签名服务访问、渠道上传和归档流程正确。签名密钥是否处于受控环境、谁批准了签名、最终上传文件是否仍为同一摘要,属于流程证据。项目证据尚未接入时,可以给出检查字段和门禁方法,不能声称某个真实候选已经通过商业签名验收。

  • 保存 apksigner 版本与完整验证参数
  • 验证命令返回非零时立即阻断
  • 解析总体结果和每个签名方案状态
  • 提取全部签名证书 SHA-256
  • 证书摘要与受控允许集合比对
  • 验证后的 APK 摘要再次绑定归档记录

attestation 与 provenance 把摘要连接到构建声明

in-toto Attestation Statement v1 通过 subject digest 把产物与有类型的 predicate 绑定。APK 的 SHA-256 可以作为 subject,predicate 则承载扫描、测试或签名审批等声明。这样做能降低报告引用同版本其他文件的风险,但 statement 格式不保证内容真实,仍要验证声明签名者和策略。

SLSA Provenance v1.1 把 subject、builder、buildType、外部参数和依赖材料组织进构建证明。它可以说明某份 APK 摘要与某次受控构建之间的关系,却不替代 APK 自身的 Android 签名验证。两种身份可以不同:构建证明由流水线身份签署,APK 由应用发布证书签署,门禁要分别核对。

正确的证据链是相互引用而不是彼此取代。provenance 的 subject 摘要匹配当前 APK,apksigner 确认 APK 签名与证书,允许列表确认当前证书身份,归档记录再确认交付的是同一摘要。任一环节错配都应停止放行。没有真实声明和签名回执时,只能把该项标为未接入。

APK 供应链证据的连接关系
证据绑定对象主要判断边界
文件摘要最终 APK 字节报告是否对应当前文件不识别签名者
Android 签名APK 受保护内容和证书完整性与签名身份不证明构建来源
in-toto statementsubject 摘要与 typed predicate声明对应哪个产物格式不保证声明真实
SLSA provenance产物与构建过程谁用什么流程和材料构建不证明运行时安全
归档回执最终交付文件交付与验收是否同一候选不代替设备回归

按 SSDF 把摘要和签名结果纳入发布证据

NIST SP 800-218 SSDF 提倡在安全软件开发中保留来源、构建、验证和变更证据,并管理供应链风险。项目可以据此定义发布记录:候选如何生成,摘要何时计算,谁验证签名,允许证书从何处批准,测试和扫描绑定哪个摘要,最终交付由谁确认。SSDF 是组织级实践,不替团队选择 Android 签名策略。

失败状态必须具体。摘要不匹配表示当前字节不是期望候选;签名无效表示完整性或签名结构不满足验证;证书不允许表示签名者身份不符合当前策略;provenance 错配表示构建声明对应其他文件。不同失败分配给不同负责人,避免所有问题都被归为“打包失败”而无法追踪。

例外也不能只写一个批准人。若因为遗留渠道或紧急修复采用不同证书或方案,记录要绑定精确 APK 摘要、适用范围、业务理由、到期触发器和补充验证。本文不提供统一例外规则,也不声称御盾产品能力。真实放行结论必须来自同一候选的可复核回执。

联合门禁失败的责任分流
失败直接含义优先责任禁止的捷径
SHA-256 不匹配文件不是登记候选构建与归档负责人更新期望摘要迎合未知文件
签名验证失败签名结构或完整性不成立签名与打包负责人只看文件摘要继续发布
证书不允许签名者不在当前身份策略发布审批负责人用证书名称代替摘要
证明 subject 错配声明对应其他文件供应链证据负责人复制同版本旧报告
最终归档变化验证后文件被替换交付负责人只凭文件名确认

用只读脚本联合校验摘要、签名和证书

下面的 Python 示例读取待验 APK、允许清单和 apksigner 可执行文件。它先对 APK 计算 SHA-256,与清单中的 apkSha256 比对,再以只读方式运行 apksigner verify,要求返回成功并包含总体验证结果,最后从证书输出提取 SHA-256 摘要并与 allowedCertSha256 集合比较。任一输入不满足都会返回非零状态。

示例没有签名、修改或重新打包 APK,也不会接触 keystore。允许清单必须来自受控发布记录,不能由待验 APK 所在目录临时生成。实际环境还应锁定 apksigner 工具来源、保存工具版本与完整输出,并按目标平台策略解析各方案结果。脚本展示联合责任,不取代正式 Android 工具和审批系统。

准备 APK 软件加固评估时,可整理最终候选、SHA-256、apksigner 完整回执、允许证书摘要、签名方案策略、构建证明和设备回归范围,再通过御盾中央平台提交申请。所有结论应绑定同一候选摘要;未提供真实证据时,不声称签名通过、兼容通过、攻击阻断或客户项目结果。

  • 期望摘要来自受控发布记录
  • 实际摘要对最终 APK 现场计算
  • apksigner 总体和方案结果均保存
  • 全部证书摘要与允许集合比较
  • 证明 subject 与 APK 摘要一致
  • 归档交付前再次确认文件未变化
  • 申请评估时提交同一候选的完整回执
联合校验 APK SHA-256、apksigner 与允许证书摘要
from pathlib import Path
import hashlib
import json
import re
import subprocess
import sys

if len(sys.argv) != 4:
    raise SystemExit(2)
apk_path = Path(sys.argv[1])
policy_path = Path(sys.argv[2])
apksigner_path = Path(sys.argv[3])
if not apk_path.is_file() or not policy_path.is_file() or not apksigner_path.is_file():
    raise SystemExit(2)
policy = json.loads(policy_path.read_text(encoding="utf-8"))
expected_apk_digest = str(policy.get("apkSha256", "")).lower()
allowed_cert_digests = {str(value).lower() for value in policy.get("allowedCertSha256", [])}
actual_apk_digest = hashlib.sha256(apk_path.read_bytes()).hexdigest()
if len(expected_apk_digest) != 64 or actual_apk_digest != expected_apk_digest:
    raise SystemExit(2)
if not allowed_cert_digests:
    raise SystemExit(2)
completed = subprocess.run([str(apksigner_path), "verify", "--verbose", "--print-certs", str(apk_path)], capture_output=True, text=True, check=False)
verification_output = completed.stdout + "\n" + completed.stderr
if completed.returncode != 0 or re.search(r"^Verified$", verification_output, re.MULTILINE) is None:
    raise SystemExit(2)
reported = re.findall(r"certificate SHA-256 digest:\s*([0-9a-fA-F:]{64,95})", verification_output)
cert_digests = {value.replace(":", "").lower() for value in reported}
if not cert_digests or cert_digests.isdisjoint(allowed_cert_digests):
    raise SystemExit(2)
result = {"status": "verified", "apkSha256": actual_apk_digest, "matchedCertSha256": sorted(cert_digests & allowed_cert_digests), "toolExitCode": completed.returncode}
print(json.dumps(result, ensure_ascii=False, indent=2))

事实依据与适用边界

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

本文判断事实或工程依据适用限制
apksigner 可以验证 APK 的签名方案覆盖并打印签名证书,签名后不应再修改 APK。Android apksigner 说明签名、verify、方案验证和证书输出的工具行为。工具验证成功不证明 keystore 管理、渠道流程、允许证书审批或最终归档正确。
Android 使用应用签名建立更新身份,并由不同签名方案服务不同平台规则。AOSP app signing 描述 Android 应用签名、更新身份和签名方案。签名有效只证明相应完整性和签名者身份,不证明业务代码安全或运行兼容。
v2 通过 APK Signing Block 保护 APK 文件主体并处理向较弱方案降级的风险。APK Signature Scheme v2 描述签名块、内容摘要和防降级属性。v2 不覆盖 APK 之外独立下载的模型、配置、补丁或其他外部资产。
供应链声明可以用 subject digest 把 typed predicate 绑定到当前 APK。in-toto Attestation Statement v1 定义 subject、predicateType 与 predicate 的通用结构。声明格式正确不保证内容真实,仍需可信签名者、策略和当前候选核验。
构建证明可以把 APK 产物与 builder、buildType、外部参数和依赖材料关联。SLSA Provenance v1.1 定义 provenance 的构建与产物字段。provenance 不替代 Android APK 签名,也不单独证明运行时安全或业务正确。
安全发布应保留来源、构建、验证和变更证据并管理供应链风险。NIST SP 800-218 SSDF 提供组织级安全软件开发与供应链实践。SSDF 不定义具体 APK 签名方案、证书允许列表或某个加固产品能力。
APK 文件摘要和签名证书摘要必须作为不同字段共同进入候选记录。工程判断:文件摘要绑定字节,证书摘要绑定签名身份,两者回答不同验收问题。联合记录提高可追溯性,仍不替代代码审计、设备回归和密钥治理。
任何签名后字节修改都应触发摘要与签名重新验证。工程判断:最终归档必须与验证回执保持同一字节身份,才能避免候选错配。具体修改是否发生和当前候选是否通过,必须由项目归档与工具回执确认。

工程常见问题

APK 的 SHA-256 与官方公布值一致,为什么还要验证签名?

摘要一致只说明你拿到的字节与该期望值相同,还要确认期望值来源可信,并验证 Android 签名方案和证书身份,才能建立发布者与更新身份判断。

apksigner verify 成功后还需要比较证书摘要吗?

需要。工具可能正确验证一个由非预期证书签署的 APK。门禁应提取全部签名证书 SHA-256,并与当前受控允许集合比较。

签名证书相同是否说明两个 APK 完全一样?

不说明。同一证书可以签署多个不同版本和不同内容,必须分别计算文件 SHA-256。证书相同回答签名身份,文件摘要相同才回答字节身份。

v2 签名验证通过能否证明下载模型也未被修改?

不能。v2 覆盖 APK 文件主体,不覆盖安装后独立下载的模型和配置。外部资产需要自己的签名、摘要、版本与更新策略。

provenance 已绑定 APK 摘要,是否可以省略 apksigner?

不能。provenance 说明构建声明与产物的关系,Android 签名验证说明 APK 完整性和应用签名身份,二者需要分别验证并相互引用。

申请 APK 加固评估前需要准备哪些签名材料?

准备最终 APK、SHA-256、apksigner 完整输出、允许证书摘要、签名方案策略、构建证明和设备回归范围,再通过御盾中央平台提交申请。

想用自己的 App 验证?

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

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