先看结论与判断条件
- 应用签名机制仅建立更新身份与完整性验证,不担保业务代码逻辑安全。
- 可提交材料限于公钥证书、签名方案元数据、SHA-256 产物摘要及构建证明。
- 私钥作为身份根必须绝对隔离,任何交付场景下均不可传输或包含在包内。
- 签名责任需在受控安全环境执行,交付清单仅体现结果而非替代签名过程。
- 构建证明(provenance)需绑定产物主体与构建参数,但仍需门禁策略核验。
- 工具验证通过仅代表文件未篡改,不能证明构建管道或密钥管理无缺陷。
交付材料的核心定义与责任边界划分
加固包交到下游时,接收方需要回答三个问题:文件有没有被替换、由谁签名、来自哪次构建。产物摘要、公钥证书和构建记录分别提供这些线索。清单同时写明禁止提交的材料,让私钥、口令和可导出凭据始终留在签名环境。
根据 Android 开源项目文档,应用签名主要用于建立更新身份,不同的签名方案覆盖不同的文件区域和平台版本,但这仅保证了文件的完整性。Apple 平台则通过代码签名、证书与运行时验证来建立 App 身份,其签名链机制与 Android 完全不同。因此,材料清单必须严格区分平台,分别提供对应的证书链与签名方案证据。接收方需按各自平台的本地验证方式核验,混用判断逻辑可能导致严重的误信风险。
安全开发框架建议保留来源、构建、验证和变更记录,交付清单可以承载其中与当前产物相关的部分。但文件齐全并不自动可信:接收方仍要使用预先认可的公钥和候选包摘要做核对,并把缺失或不匹配项留作明确异常。
| 材料类型 | 提供方责任 | 接收方验证动作 | 信任边界限制 |
|---|---|---|---|
| 签名证书 | 导出公钥部分并附带指纹 | 比对渠道预期指纹 | 仅证明签发者身份,非代码安全 |
| 产物摘要 | 计算并固化 SHA-256 值 | 独立重新计算并比对 | 仅证明文件比特级一致,非逻辑正确 |
| 构建证明 | 生成包含参数的 provenance | 校验签名与参数一致性 | 仅证明记录过程,非运行期行为 |
| 私钥 | 绝对隔离且不参与交付 | 确认清单中无私钥痕迹 | 一旦泄露即丧失所有身份控制权 |
- 是否已依据目标平台要求导出了完整的签名块信息与证书列表?
- 清单是否明确注明只包含公钥部分,且无任何私钥衍生数据或口令?
- 是否附上了独立于签名机制之外的产物摘要供接收方交叉比对?
可提交材料详解:证书、签名元数据与数字化摘要
可提交材料的首要组成部分是 APK 签名信息,这通常需要使用 apksigner 或类似工具导出的签名方案明细。这些明细涵盖了 V1、V2、V3 等不同版本的覆盖区块以及具体的签名算法标识。同时,必须附带签名证书的 SHA-256 指纹及其公钥部分,以便接收方能与渠道预期的发布者信息进行精确比对。签名证书本身不含私钥,只提供签发者身份和公钥要素,因此作为公开证据提交在密码学上是安全的。
数字化摘要必须使用标准的密码学哈希函数,如 SHA-256,对加固后的 APK 文件进行计算,并随包提供。该摘要独立于签名机制存在,主要用于防止重放攻击和产物错配。SLSA Provenance v1.1 明确要求构建证明必须绑定产物主体及其摘要,因此交付清单中绝不能省略产物摘要。接收方可单独校验该摘要,无需依赖复杂的签名工具即可确认文件在传输过程中是否被意外修改。
构建来源证明由构建系统在构建结束时自动生成,详细描述了构建者身份、构建类型、入口参数、依赖材料以及外部输入等关键信息。接收方可根据该证明追溯具体的构建环境,但这并不能直接证明运行时的安全性。提交方必须确保 provenance 中的 subject 字段指向与交付产物完全一致的摘要,否则该证明即刻失效,无法作为信任依据。任何参数不匹配都意味着构建记录与实物脱节。
| 材料类别 | 具体示例 | 是否可提交 | 判定原因 |
|---|---|---|---|
| 签名证书 | X.509 证书(仅公钥部分) | 是 | 用于验证签名者身份,不包含私钥秘密 |
| 签名方案详情 | V2/V3 签名区块元数据 | 是 | 校验文件完整性及签名覆盖范围所需 |
| APK 摘要 | SHA-256 哈希值字符串 | 是 | 作为独立的完整性基准,防篡改 |
| 构建来源证明 | SLSA provenance JSON 文件 | 是 | 追溯构建过程参数与环境配置 |
| 私钥 | PKCS#12 或 JKS keystore 文件 | 否 | 泄露即丧失身份控制,属最高机密 |
| 签名系统凭证 | HSM 访问令牌或 API Key | 否 | 超出产物交付范围,属基础设施秘密 |
- 证书公钥与指纹是否与预期发布者的记录完全匹配?
- 提供的摘要是否与交付包在本地计算的值严格一致?
- provenance 中的 subject 字段是否准确指向同一产物摘要?
不可提交的私钥:身份根的保护与绝对分离
私钥是签名身份的根源,一旦提交私钥,接收方就能任意以提交方身份签发更新,这将彻底破坏信任链条。因此,在交付材料中绝不允许包含私钥、keystore 文件或任何可推导私钥的口令、助记词。传递私钥的行为将使之前所有基于签名的身份校验失去意义,这是一个不可逆的安全失败。任何要求提供私钥的请求都应被视为高危信号并立即拒绝。
签名责任应在独立的、受保护的环境中执行,例如离线 HSM 或临时的专用构建机。提交的只是签名后的产物和证书,而非签名能力本身。这样可以审计每一次签名事件,但签名权仍限定于构建者。即使出于应急恢复的理由,也不应在交付中附加私钥,而应采用预先协商的证书替换、共同签名或门禁恢复流程来解决问题。
将私钥纳入交付是高风险反模式,多数安全评审直接拒绝此类做法。私钥泄露常常源于错误的传输习惯,许多事故证明将私钥通过邮件、即时消息或随源码打包传送会导致攻击者永久接管身份。加固交付流程中必须将私钥完全排除在材料清单之外,由签名实体单独管理,确保物理和逻辑上的双重隔离。
- 交付包中是否彻底排除了任何扩展名为.jks、.p12 或.keystore 的文件?
- 配置文件或脚本中是否硬编码了任何可能用于还原私钥的密码字符串?
- 是否有明确的书面声明指出私钥未包含在此次交付的任何介质中?
签名责任与最终身份:声明约束而非文件堆砌
Android 和 Apple 平台均通过签名建立安装和更新时的身份连续性,但应用签名仅证明文件来源与完整性,不保证其行为无害。因此,在加固交付中接收方需明确:签名合法只满足平台准入条件,不代表应用通过安全审查。从签名验证成功推导应用安全是错误的逻辑,必须结合额外的行为分析和动态测试才能得出更全面的结论。
构建证明可以把产物摘要、构建者和参数放进同一份声明。in-toto 的声明格式便于机器读取这些字段,但格式正确不代表内容真实。接收方仍要核对声明签名者是否在信任列表中,以及摘要是否与手上的候选包一致。
提交材料时应明确声明签名责任主体,例如具体的组织单元,并限定该密钥用途,如仅用于该产品线。接收方需按策略比对声明和实际签名者,发现不一致则应阻断。签名者身份的判定不能仅凭证书通用名称,还要比对 provenance 中的 builder 标识,以排除代理签名造成的身份错配,确保责任归属清晰无误。
| 平台 | 身份建立机制 | 可提交证据 | 不适用场景 |
|---|---|---|---|
| Android | 应用签名 + 签名方案覆盖 | 证书、签名块元数据 | 跨平台评估或非 Android 环境 |
| Apple | 代码签名 + 证书链验证 | embedded.mobileprovision、证书 | 非 Apple 生态或越狱设备 |
| 通用供应链 | 建造者 + 构建类型 + 参数 | SLSA provenance 文件 | 运行期行为安全性保证 |
| 分发渠道 | 加密校验和 + 二次签名 | 摘要、渠道签名文件 | 原始密钥管理证明 |
- 声明中的签名责任主体是否与组织架构中的实际拥有者一致?
- 密钥用途限制是否在证书扩展字段或附属文档中明确标注?
- 接收方是否有能力比对 provenance 中的 builder 标识与证书主体?
构建证明与材料清单的装配逻辑
构建完成时就生成对应记录,至少包含 APK 或 AAB 摘要、构建者、构建类型、加固工具版本和关键依赖摘要。记录与产物一起归档,避免事后补交时无法确认它们是否来自同一次构建。
如果项目对构建证明签名,交付时只提供签名后的声明和公钥验证信息。接收方先验声明签名,再比对其中的产物摘要;任一结果不一致,就把构建记录与实物标为不匹配并回到流水线排查。SLSA 描述证明内容与等级,不应被误写成所有项目都必须采用同一种签名实现。
需要机器可读格式时,可用 in-toto statement:subject 绑定产物摘要,predicateType 标明声明类型,predicate 保存构建字段。是否再使用 DSSE 取决于现有证明系统;无论采用哪种封装,交付材料都不包含用于签名的私钥。
| 字段名称 | 含义描述 | 是否必须提交 | 关键校验点 |
|---|---|---|---|
| subject | 产物主体的摘要信息 | 是 | 与交付 APK 实时计算值完全匹配 |
| builder.id | 构建者的唯一 URI 标识 | 是 | 确认为组织信任的构建系统标识 |
| buildType | 构建类型的定义链接 | 是 | 确认环境配置符合预期标准 |
| externalParameters | 入口参数含加固配置 | 是 | 作为重现构建过程的依据 |
| materials | 依赖项的摘要列表 | 推荐 | 用于审计供应链组件来源 |
- 证明文件本身是否已被组织级根密钥正确签名?
- subject 中的摘要是否与文件系统上的产物哈希完全一致?
- externalParameters 中是否包含了加固工具的具体版本号?
材料清单自检脚本:安全诊断与零私钥接触
以下脚本接收加固后的 APK 路径和可选的期望证书指纹,使用 apksigner 和系统哈希工具完成验证。脚本运行过程中绝不请求或加载任何私钥、keystore 或口令文件。如果提供了期望指纹,脚本会实施严格比对;否则仅输出包含摘要与指纹的 JSON 清单。任何失败状态均返回非零值,适合嵌入 CI 或门禁流水线自动调用,确保流程自动化且安全。
脚本首先计算 APK 的 SHA-256 摘要,随即调用 apksigner verify 提取证书指纹。若签名验证失败,脚本直接报错并将具体原因输出到 stderr,阻止后续步骤。成功执行后,将摘要与指纹包装为 JSON 材料清单输出,不携带多余信息,不泄露密钥材料。这种设计确保了验证逻辑完全基于公钥信息和公开的工具接口,符合私钥隔离原则。
包含此脚本的交付套件可向接收方证明材料清单的生成过程本身也未触碰秘密材料。验证逻辑完全基于公钥信息和公开的工具接口,与组织安全策略中的私钥隔离原则一致。接收方可单独运行脚本复验,确认产物未被传输过程篡改,签名者身份与预期一致,从而建立对交付物的初步信任基础。
- 脚本是否在无网络环境下也能独立完成所有校验步骤?
- 错误退出码是否区分了文件缺失、签名失败和指纹不匹配?
- 输出 JSON 是否严格遵循标准格式且无多余调试信息?
#!/bin/bash
set -euo pipefail
# 检查输入参数
APK="$1"
EXPECTED_FINGERPRINT="${2:-}"
if [ ! -f "$APK" ]; then
echo "ERROR: APK 文件不存在,请检查路径" >&2
exit 1
fi
echo "==> 开始计算 APK 文件摘要"
APK_DIGEST=$(shasum -a 256 "$APK" | awk '{print $1}')
echo "SHA-256: $APK_DIGEST"
echo "==> 验证 APK 签名并提取证书信息"
SIG_INFO=$(apksigner verify --print-certs "$APK" 2>&1) || {
echo "ERROR: 签名验证失败,APK 可能未签名或被破坏" >&2
exit 1
}
CERT_FINGERPRINT=$(echo "$SIG_INFO" | awk '/Signer .* certificate SHA-256 digest/{getline; print $2}')
if [ -z "$CERT_FINGERPRINT" ]; then
echo "ERROR: 无法从签名信息中提取证书指纹" >&2
exit 2
fi
echo "签名者证书指纹:$CERT_FINGERPRINT"
if [ -n "$EXPECTED_FINGERPRINT" ]; then
if [ "$CERT_FINGERPRINT" != "$EXPECTED_FINGERPRINT" ]; then
echo "ERROR: 证书指纹不匹配期望值" >&2
echo "期望:$EXPECTED_FINGERPRINT" >&2
echo "实际:$CERT_FINGERPRINT" >&2
exit 3
fi
echo "证书指纹匹配成功"
else
echo "未提供期望指纹,跳过比对步骤"
fi
echo "==> 生成材料清单摘要 JSON"
cat <<EOF
{
"apk_digest": "$APK_DIGEST",
"signer_fingerprint": "$CERT_FINGERPRINT"
}
EOF常见交付误区与证据边界澄清
apksigner verify 通过后,能确认的是 APK 签名结构和证书信息;构建节点、密钥保管和分发渠道并不在它的检查范围内。报告里应把这个结果写成签名检查通过,而不是扩展为整条流程可信。
代理签名场景还要区分签名者与构建者。证书主体、构建证明中的 builder 标识和委托记录分别核对;不一致时先确认是否为已授权代理,不直接把格式差异解释成攻击,并把核对结果关联到当前候选包。
Android 与 Apple 的签名工具、校验单元和错误语义不同。清单可以共用字段名称,但实际命令、证书链和结果要分平台保存,避免一份脚本把两类证据压成同一个结论。
材料提交完成后,接收方仍保留自己的验收责任。清单提供可核对的输入,不能代替接收方对签名身份、产物摘要和运行结果的独立判断。没有被实际核对的字段继续标为待验证,不随文件齐全自动通过。
| 证据类型 | 可证明内容 | 不能证明内容 | 依据来源 |
|---|---|---|---|
| 签名证书 | 发行者身份、公钥有效性 | 私钥管控力度、业务逻辑安全 | AOSP app signing |
| apksigner 验证结果 | APK 未篡改、文件顺序合法 | 构建环境安全、渠道流程合规 | Android apksigner |
| provenance | 构建配方与参数记录 | 运行时安全性、代码无后门 | SLSA Provenance v1.1 |
| in-toto 声明 | 声明与产物摘要匹配 | 声明内容真实性、构建者诚实 | in-toto Attestation v1 |
| SSDF 实践 | 安全发布流程留存 | 具体硬化级别、防御效果 | NIST SP 800-218 |
| Apple 签名链 | Apple 平台身份连续性 | 第三方 App 内容安全、非 Apple 设备 | Apple app code signing process |
- 是否已清楚告知接收方验证工具的局限性?
- 是否明确了构建代理与证书主体的潜在差异?
- 是否强调了清单提交不等于责任转移的原则?
加固交付材料提交清单与后续行动指南
最终提交材料应打包为可归档的压缩文件,包含加固后的 APK 或 AAB、单独导出的签名证书公钥部分、数字摘要文件以及构建证明文件。每个部分均需提供可单独验证的指纹,确保在解包后能立即进行独立校验,无需依赖外部查询。这种自包含的设计减少了对外部服务的依赖,提高了验证的可靠性和效率。
交付说明文档应明确签名责任主体、签名环境简述及证书有效期,但不包含任何私钥或授权凭据。接收方按说明在隔离环境中使用自检脚本或等效流程重新校验,用公钥验证一切。任何信息残缺都将导致接收方中断接受流程,直到补充完整且通过验证为止。完整性是信任建立的前提条件。
管理层应期望此清单在自动化管道中自动生成,并通过独立通道如已建立的公钥基础设施确认完整性,减少人为错误。长期维护中,证书轮换或签名环境变更都应反映在后续清单中,不可继续沿用旧证据,否则会造成信任链断裂。持续的更新和维护是保持供应链安全的关键环节。
- 所有材料均已包含且无缺漏,特别是构建证明和摘要?
- 证书不是自签名但符合组织 PKI 策略,且未过期?
- provenance 签名可信任链到根,且未被篡改?
- 交付说明文档是否清晰界定了责任边界和联系方式?
事实依据与适用边界
以下内容区分官方事实、本文工程判断和不能外推的范围,避免把设计建议写成未经验证的产品结论。
| 本文判断 | 事实或工程依据 | 适用限制 |
|---|---|---|
| Android 应用签名方案建立更新身份和完整性验证。 | AOSP app signing | 签名有效只证明完整性与签名者身份,不证明业务代码安全。 |
| Apple 平台通过代码签名、证书与运行验证确立 App 身份。 | Apple app code signing process | Apple 签名链与 Android APK 签名方案不能混为一套证据。 |
| 安全发布应留存来源、构建、验证与变更证据,将供应链风险纳入流程。 | NIST SP 800-218 SSDF | SSDF 是组织级实践框架,不定义某个 App 加固产品的具体功能。 |
| 构建证明应绑定产物主体、构建者、构建类型、外部参数与依赖材料。 | SLSA Provenance v1.1 | provenance 只能证明记录的构建过程,不能单独证明运行时安全性。 |
| 供应链证明应将产物摘要与有类型声明负载绑定,避免报告与候选包错配。 | in-toto Attestation Statement v1 | 声明格式不保证声明内容真实,仍需可信签名者和门禁核验。 |
| apksigner 工具可验证签名方案覆盖与证书,并要求签名后不再修改 APK。 | Android apksigner | 工具验证成功不证明 keystore 管理、渠道流程或候选包归档正确。 |
| 私钥绝不能作为交付材料的一部分,必须隔离在签名实体内部。 | 工程判断 | 项目证据尚未接入,但多数行业标准和实践均禁止私钥传输。 |
| 应用签名仅建立平台更新身份,不与业务安全等同。 | AOSP app signing 与 Apple app code signing process 结合 | 平台签名机制不涉及应用内容的安全审查。 |
工程常见问题
加固后 APK 签名是否可变更?交付时是否需重新签名?
要看加固输出是否仍保留有效签名,以及目标渠道如何管理更新身份。常见做法是在全部包体修改完成后,由开发团队按正式流程签名。交付清单记录最终证书和验证结果,接收方据此核对身份连续性。
若没有独立构建证明,能否仅提供证书指纹?
证书指纹可以回答“由谁签名”,却不能说明文件来自哪次构建。暂时没有构建证明时,可补充受控构建日志、提交版本和产物摘要,并明确这组材料的证明范围。
交付清单是否可包含自签名证书?
技术上可以校验自签名证书,前提是接收方已通过独立渠道认可该公钥。是否接受取决于组织的信任策略;清单应写明证书来源、用途和校验方式。
材料中签名信息与数字摘要如何防止篡改?
接收方重新计算包体摘要,并从包内独立提取证书信息,再与清单比较。结果不一致表示材料与手中产物不是同一状态,需要回到交付方和构建记录排查;仅凭不一致还不能判断具体原因。
交付时是否需要说明加固工具及其版本?
建议记录工具和配置版本,因为它们有助于重现构建并定位后续差异。公开到什么粒度取决于合同和披露边界,但内部归档应能关联到具体候选包。
如果 apksigner 验证通过但证书与预期不符怎么办?
先暂停这份候选包,比较签名任务、渠道配置和上一版本证书记录。apksigner 通过说明签名结构有效,证书不符则说明身份没有匹配预期;找到是拿错包、签错密钥还是轮换配置后,再重新生成和验证。