先看结论与判断条件

  • AAB 仅是模块化容器,设备最终安装的是经 Play 服务器按配置派生并重签名的 split APK 集合。
  • 启用 Play App Signing 后,设备端 APK 的签名证书指纹与开发者上传密钥指纹不同,必须以下载的应用签名证书为准。
  • 本地 bundletool 生成的 APK 集若未显式传入生产密钥,将使用调试签名,无法作为线上交付身份的取证依据。
  • Android PackageInstaller 强制要求同一应用的所有 split APK 具有相同的包名、版本码和签名证书,否则安装失败。
  • 固定身份需建立三层证据链:上传 AAB 摘要、Play 交付的 split APK 集摘要、以及绑定构建上下文的 in-toto 声明。
  • 供应链声明中的产物摘要必须与真实构建输出一致,且需由可信密钥签名,否则无法防止报告与候选包错配。

AAB 派生 split APK 的身份链构成

AAB 文件内部包含 base 模块、功能模块、配置模块及资产模块,其本质是用于分发的元数据容器而非直接安装包。Google Play 服务器接收上传的 AAB 后,会根据请求设备的具体硬件配置如屏幕密度、CPU 架构和系统语言,动态选择所需模块并组装成一组 split APK。这意味着不同设备从同一 AAB 获得的 APK 集合在数量和内容上可能存在差异,原始 AAB 的哈希值无法直接代表设备上实际运行的代码身份。

当开发者启用 Play App Signing 服务时,上传过程中的签名验证与最终设备安装时的签名验证由不同的密钥完成。开发者使用上传密钥对 AAB 进行签名以证明提交者身份,Google 验证通过后剥离该签名,转而使用托管的应用签名密钥对生成的每个 split APK 重新签名。因此,设备端安装的 APK 证书指纹与开发者本地构建环境中的证书指纹不一致,这是身份固定过程中最容易产生混淆的关键环节。

在非 Google Play 的分发场景中,若开发者自行处理 AAB 拆分,则必须完整记录从源码到最终 APK 的派生路径。这包括保存具体的 AAB 版本号、构建配置文件内容、使用的 bundletool 版本、目标设备规格说明以及生成每个 APK 的摘要和证书信息。缺少上述任一环节的记录,都会导致从模块化描述到具体安装包的追溯过程变成不可验证的推断,无法在安全审计中形成完整的身份证据链。

身份链的完整性依赖于对模块依赖关系的精确记录。base 模块定义了应用的核心组件和权限,而配置模块和资源模块则依附于 base 存在。如果取证时仅关注 base APK 而忽略其他 split 组件,可能会遗漏被篡改的资源或代码片段。因此,固定身份时必须将 base 与所有相关的 split APK 视为一个整体单元,任何单个组件的缺失或签名不匹配都意味着整个应用身份的失效。

AAB 拆分链中的身份要素与作用域
身份组件决定方对身份的影响取证或验证手段
AAB 的模块架构应用开发者定义 split 类型与资源分组,但无签名语义bundletool dump manifest 可检查模块依赖
上传签名开发者或 CI 持有上传密钥证明上传者的控制权,不是设备安装的最终签名使用 apksigner verify 检查上传 AAB 的签名
应用签名(Play App Signing)Google 按开发者选择的应用签名密钥固定设备端 APK 的安装签名,设备验证此签名从设备提取 APK 后用 apksigner verify --print-certs
设备交付 split APK 集摘要Play 服务器生成标识具体交付物内容,与源代码无直接关联本地 bundletool 复现并比对 SHA256 清单
  • 确认是否启用 Play App Signing,确认应用签名证书指纹已从 Play Console 下载
  • 保存每次上传 AAB 的版本信息、bundletool 版本和构建 spec
  • 在 CI 中记录 AAB 模块列表和衍生 APK 的预期 device spec 范围

上传包、设备交付包与取证对象的三层区分

上传 AAB 的摘要只标识提交给 Play 的输入文件。验收时先保存该摘要、versionCode、track 和上传证书,再从目标设备导出实际安装的 base 与配置 split,逐个计算摘要并提取签名证书。两组记录通过同一次发布事件关联,但不能互相替代。

设备交付包是指最终通过 Play 商店、设备系统安装会话或其他分发渠道成功安装到终端上的 split APK 集合。每个 split APK 在其 manifest 文件中声明了特定的拆分名称及其对 base 模块的依赖关系,整个集合在安装时必须共享同一个签名证书。系统级的 PackageInstaller 会严格校验 base 与所有 split 的版本码和签名一致性,只有满足这些约束的集合才能被视为合法的执行环境,这也是安全策略锚定的真实对象。

取证时通常能拿到设备导出的 APK、构建日志和 AAB 存档,工作重点是把这些材料按同一次发布关联起来。不应使用本地 bundletool 生成的调试签名 APK 代替线上交付包;它可以验证模块结构,却没有 Play 交付包的签名与摘要,不能证明设备实际收到的内容。

三层实体的区分对于责任界定至关重要。上传包证明了开发者的提交意图,设备交付包代表了用户实际运行的代码,而取证对象则是连接两者的桥梁。如果在审计过程中混淆了这三者,例如用上传密钥指纹去验证设备 APK,会导致错误的结论。正确的做法是分别采集各层的特征数据,并通过逻辑链条将它们关联起来,确保每一步的转换都有据可查,避免因证据链断裂而无法定责。

三个实体在身份判定中的角色比较
实体签名主体能否直接安装身份证明价值
上传 .aab上传密钥(或自签名)证明开发者提交意图,不代表设备最终内容
设备 split APK 集Play 应用签名密钥或自持有发布密钥代表真实可执行身份,是安全策略的锚点
本地 bundletool 复现 APK调试证书或显式传入的密钥仅测试环境可安装可辅助验证派生逻辑,但不可作为线上取证
供应链证明声明可信构建系统签名者不直接安装将构建产物摘要与上下文绑定,可作审计支持
  • 取证时严禁混用本地 bundletool 调试输出与设备导出的 APK 签名
  • 若使用 Play App Signing,必须单独获取应用签名证书指纹才能验证设备 APK
  • 固定交付身份时,至少保存设备 APK 的 SHA256 和证书链以及对应的 Play 交付会话 ID

AAB 格式与模块组成对身份的影响

为设备端 APK 集建立身份记录时,应同时保存 packageName、versionCode、split 名称、目标 ABI、密度、语言和证书指纹。这样才能解释为什么两个设备的文件数量或摘要不同。device spec 是重建交付条件的输入,不是生产交付回执;缺少设备导出包时,只能标记为待核验。

动态特性模块允许应用在 Android 5.0 及以上版本中按需下载和安装,这类 split APK 的安装时间往往滞后于基础安装。这导致初次安装时的 APK 集合与用户最终交互时的集合不一致,增加了身份固定的复杂度。基础安装通常只包含 base 模块和预装的 split,而按需模块各自拥有独立的签名和摘要,尽管它们整体仍受同一证书约束,但在取证时需要分别记录每个模块的加载状态和版本信息。

Play 服务器在为 AAB 生成交付包时,可能会创建额外的优化 APK,例如压缩 slice 或特定的资源配置文件,这些文件并未直接列在开发者上传的 AAB manifest 中,但却属于交付的一部分。如果在取证过程中忽略这些衍生 APK,可能会导致证据不完整,影响对应用整体性的判定。因此,明确这些衍生文件是否属于受信任的身份范围,是确保存证完整性的必要步骤,防止部分内容遗漏在证据之外。

模块化的设计虽然提升了分发效率,但也引入了身份碎片化的风险。攻击者可能尝试替换某个特定的资源 split 而不触动 base 模块,从而绕过基于 base 签名的简单校验。为了应对这种威胁,身份固定机制必须覆盖所有可能被加载的 split 组件,确保任何一个模块的篡改都能被检测到。这需要建立细粒度的清单,记录每个 split 的名称、大小、摘要和依赖关系,形成多环节的防护网。

  • 在 CI 中为每个目标 device spec 生成清单,列出预期模块和拆分名称
  • 记录动态特性模块的安装状态,作为身份完整性的补充信息
  • 确认 bundletool dump 生成的模块依赖图与实际交付一致

签名密钥体系:上传签名与应用签名的责任分离

签署 Android 应用涉及三个关键的密钥角色:应用签名密钥、上传密钥以及用于支持 Play App Signing 的 Google 管理密钥。其中,应用签名密钥是设备判定应用身份和接收更新的根密钥,一旦丢失或轮换不当,所有已安装用户将无法获得后续更新。因此,固定发布身份的首要任务是把应用签名密钥的证书指纹作为固定基准,无论上传密钥如何变化,这个指纹在应用生命周期内应保持不变。

上传密钥的主要作用是证明构建物来自合法的开发者,Play 在接收 AAB 后会校验该签名,若校验通过则将签名更换为应用签名密钥。这意味着上传密钥仅在上传管道中起作用,不能用于设备端的签名比对。如果开发者在本地使用上传密钥为 bundletool build-apks 命令签名,得到的 APK 证书将与 Play 最终签发的设备 APK 不同,这种差异是身份验证中常见的混淆来源,必须加以区分。

Play App Signing 服务允许生成独立的上传密钥,并提供密钥升级路径,但应用签名密钥不能从上传密钥导出。开发者在 Play Console 中看到的应用签名证书的 SHA-1 或 SHA-256 指纹是固定身份必须采集的基准数据。无论后续如何更改上传密钥,该指纹保持不变,因此固定身份记录必须包含该基准,并记录如何获取该基准的审计日志。这确保了即使上传密钥泄露,只要应用签名密钥安全,设备端的信任链就不会断裂。

密钥管理的疏忽可能导致严重的身份漂移。例如,如果开发者在本地测试时误用了调试密钥,而在生产环境中使用了上传密钥,会导致不同环境下的 APK 指纹不一致。在固定身份时,必须明确记录每个阶段使用的密钥类型及其指纹,并在签名轮换决策中同步更新所有取证引用。解释指纹变化带来的身份漂移是审计过程中的重要环节,有助于追踪潜在的安全事件源头。

签名密钥用途与身份固定需求
密钥类型保管方签名范围固定身份时应保存的内容
应用签名密钥Google(Play App Signing)或开发者自持有对所有交付设备的 APK 签名证书指纹(SHA256)和证书链
上传密钥开发者或 CI 系统对上传的 AAB 签名公钥指纹与轮转历史,仅用于上传证明
调试签名本地或 CI 默认仅用于本地和非生产测试不纳入固定身份记录,仅作开发区分
供应链证明签名密钥可信构建环境对 in-toto 声明的签名公钥与验证门禁策略
  • 从 Play Console 导出并归档应用签名证书指纹,不得仅依赖上传密钥
  • 在签名轮换决策中同步更新所有取证引用,并解释指纹变化带来的身份漂移
  • 本地测试时显式指定发布密钥以消除调试签名的干扰

设备 split APK 安装约束与身份一致性校验

Android PackageInstaller 在处理 split APK 安装时会强制执行严格的校验逻辑,要求所有 APK 的 packageName、versionCode 与签名证书完全相同。如果任何一个 split 不符合这些条件,安装过程将立即终止并返回 INSTALL_FAILED_INVALID_APK 或签名不匹配错误。这一机制构成了身份固定的第一道防线,确保构成一次交付的所有 split APK 满足一致性条件,防止恶意模块混入合法应用中。

base APK 包含了应用的根 manifest 和核心组件声明,其他拆分 APK 的 manifest 中会明确声明 split 名称和派生依赖。安装系统要求它们与 base 保持版本同步,不可能出现 base 和 split 分别来自不同 AAB 版本还能合法安装的情况。因此,在固定身份时只需重点记录 base 的摘要和证书,并通过一致性约束推导其余 split 的受信关系,简化了验证流程的同时保证了安全性。

动态特性模块在安装时同样受相同的签名限制,且每个模块拥有独立的 split APK,但其版本码和包名必须与已安装的 base 一致。如果取证时发现设备上存在某个动态模块的签名与 base 不同,说明该模块来自非官方来源,应标记为异常。使用 adb shell pm dump 或 dumpsys package 命令可以查看当前安装的所有 split 和签名者详情,这些工具提供了现场验证的有效手段,帮助快速识别潜在的风险点。

一致性校验不仅是安装时的强制措施,也是事后审计的重要依据。通过对比设备端提取的 APK 签名与预期的应用签名证书指纹,可以快速判断应用是否被篡改。如果发现签名不匹配,应立即启动应急响应流程,排查可能的入侵途径。此外,定期检查设备上安装的 split 名称列表,防止未经授权的拆分单元被注入,也是维护应用身份完整性的必要措施。

  • 验证设备 base APK 证书指纹是否与应用签名密钥指纹一致
  • 提取所有 split APK 后使用 apksigner verify 检查每个 APK 的签名者一致性
  • 与预期 split 名称列表比对,防止未经授权的拆分单元被注入

使用 bundletool 本地验证派生 APK 与身份清单

bundletool 工具可以从输入的 AAB 文件和指定的设备 spec 产生与实际 Play 交付结构相似的 APK 集,但它缺少 Play 线上重签名的步骤。为了获得有参考价值的清单,应在运行 build-apks 命令时显式传入发布密钥库参数,包括 --ks、--ks-pass 和 --key-pass。在签名完成后,将每个生成 APK 的摘要和证书指纹记录为清单,这样生成的 APK 集在证书指纹上与设备交付产物一致时,清单才可视为可信的派生证据。

使用 bundletool build-apks 并指定具体设备 spec 后,输出的 .apks 文件实际上是一个 ZIP 包,内含 splits/base-master.apk 等文件。必须使用 bundletool extract-apks 指令解出全部分 APK,对每个文件单独计算 SHA256 摘要并记录除原生库和资源外一致的结构。清单中应详细包含文件名、packageName、versionCode、目标 ABI、屏幕密度、语言以及签名证书指纹,确保信息的全面性和可追溯性。

本地生成无法完全复现 Play 动态交付中的额外处理,因此 bundletool 清单不能直接声明与线上交付完全一致。记录生成模式、bundletool 版本、签名信息来源和 device spec 来源;未传入生产密钥时,生成包只用于结构验证。还可以把每个 APK 的摘要与调用参数写入已签名的 in-toto 声明,但该声明仍不是 Play 交付回执。

自动化脚本可以帮助高效地生成和记录身份清单。通过编写 Shell 脚本调用 bundletool 和相关工具,可以实现从 AAB 到 APK 清单的全自动处理。脚本应具备错误处理能力,当关键步骤如 build-apks 或 extract-apks 失败时返回非零状态,确保数据的可靠性。同时,脚本不应执行删除操作,以免破坏原始数据,输出目录应由调用者自行管理,保证操作的可控性和安全性。

  • 在每次 AAB 构建后运行该脚本,将清单归档到不可变存储
  • 如果传入 --ks 等参数指定发布密钥库,必须在清单中标注签名密钥来源
  • 检查 cert_sha256 列是否与 Play Console 的应用签名证书一致,不一致则代表本地签名身份与线上分离
从 AAB 生成并记录 APK 身份清单脚本
#!/bin/bash
set -euo pipefail

# 用法:record_aab_apks_manifest.sh <aab_path> <output_dir>
# 功能:使用 bundletool 从 AAB 生成指定设备配置的 APK 集,提取每个 APK 的清单信息。
# 要求 bundletool、aapt、sha256sum 在 PATH 中,并需指定输出目录存放临时解压的 APK。
# 注意:脚本不执行任何删除操作,输出目录由调用者自行管理。

if [[ $# -lt 2 ]]; then
  echo "用法:$0 <aab_path> <output_dir>" >&2
  exit 1
fi

AAB_PATH="$1"
OUTPUT_DIR="$2"

if [[ ! -f "$AAB_PATH" ]]; then
  echo "错误:AAB 文件不存在:$AAB_PATH" >&2
  exit 1
fi

if [[ ! -d "$OUTPUT_DIR" ]]; then
  echo "错误:输出目录不存在:$OUTPUT_DIR" >&2
  exit 1
fi

if ! command -v bundletool &>/dev/null; then
  echo "错误:需要 bundletool 在 PATH 中" >&2
  exit 1
fi

if ! command -v aapt &>/dev/null; then
  echo "错误:需要 aapt 在 PATH 中" >&2
  exit 1
fi

APKS_FILE="$OUTPUT_DIR/output.apks"
APKS_DIR="$OUTPUT_DIR/apks"

echo "从 AAB 生成 APK 集(universal 模式)..." >&2
bundletool build-apks --bundle="$AAB_PATH" --output="$APKS_FILE" --mode=universal 2>&1 || {
  echo "错误:bundletool build-apks 失败" >&2
  exit 1
}

mkdir -p "$APKS_DIR"
bundletool extract-apks --apks="$APKS_FILE" --output-dir="$APKS_DIR" 2>&1 || {
  echo "错误:解压 APKS 文件失败" >&2
  exit 1
}

apk_files=$(find "$APKS_DIR" -type f -name "*.apk" 2>/dev/null || true)
if [[ -z "$apk_files" ]]; then
  echo "错误:未找到任何 APK 文件" >&2
  exit 1
fi

echo "filename,package_name,version_code,sha256_digest,cert_sha256"

FAILED=0
while IFS= read -r apk; do
  fname=$(basename "$apk")
  pkg=$(aapt dump badging "$apk" 2>/dev/null | grep "^package:" | sed -n "s/.*name='\([^']*\)'.*/\1/p" || echo "unknown")
  vercode=$(aapt dump badging "$apk" 2>/dev/null | grep "^package:" | sed -n "s/.*versionCode='\([^']*\)'.*/\1/p" || echo "0")
  sha256digest=$(sha256sum "$apk" | awk '{print $1}')

  cert_sha256="N/A"
  cert_file="$OUTPUT_DIR/temp_cert.der"
  if unzip -q -o "$apk" META-INF/CERT.RSA -d "$OUTPUT_DIR" &>/dev/null; then
    if command -v keytool &>/dev/null; then
      cert_sha256=$(keytool -printcert -file "$cert_file" 2>/dev/null | grep -m1 "SHA256:" | awk '{print $2}' || echo "N/A")
    fi
    # 注意:不删除提取的证书文件,留给调用者处理。
  fi

  echo "$fname,$pkg,$vercode,$sha256digest,$cert_sha256"

  if [[ "$cert_sha256" == "N/A" ]]; then
    FAILED=1
  fi
done <<< "$apk_files"

if [[ $FAILED -ne 0 ]]; then
  echo "警告:部分 APK 无法提取有效签名证书,请检查输出。" >&2
fi

供应链证明:使用 in-toto 声明绑定 APK 摘要

in-toto 声明可以为每个构建步骤提供可验证的背书,将产物摘要与构建上下文如 bundletool 参数、源码仓库 commit 和 AAB 哈希绑定在同一个 attestation 中,防止审计报告引用到错误的候选包。固定发布身份的最后环节是生成并签名这种声明,使其独立于 Play 交付管道。这样即使交付渠道发生变化,构建过程的真实性依然可以通过声明得到验证,增强了供应链的透明度。

声明结构中的 subject 字段应包含每个派生 APK 的 artifact URI 和 SHA256 摘要,predicate 部分描述生成方式,如 bundletool 命令、设备 spec 和设备平台版本。声明本身必须由开发者或构建系统持有的证明密钥签名,否则任何中间步骤都可以篡改产物列表而不被察觉。签名密钥的安全性直接决定了声明的可信度,因此必须妥善保管并定期轮换。

准入验证流程先校验声明签名,再提取摘要并与实际设备 APK 比对。Play App Signing 场景下,声明中的上传证书并非设备最终证书,应另设 expectedFinalCert,记录从 Play Console 获取的应用签名证书指纹。in-toto 只规定声明结构,内容是否可信仍取决于签名者身份和实物摘要核对。

验证脚本应在 CI 中比对 in-toto 声明签名、上传 AAB 摘要和预期应用签名证书;设备取证阶段再逐个比对 split APK 摘要。任何字段缺失或不一致都要保留原始输出并停止登记,不能用人工备注把不一致改成通过。

  • 生成声明前确认所有 APK 均使用生产签名,若不启用 Play App Signing 则为自有发布密钥
  • 在声明中包含 AAB 本身的摘要和 bundletool 版本,保证输入可复现
  • 设定门禁策略,禁止接受签名者未知或签名密钥被撤销的声明

Play 交付回执与本地复现证据的差异

Google Play 的交付回执如 Play Developer API 返回的 delivery metadata 能为某次分发指定 track、版本码和时间戳,但该回执不包含设备实际安装的 APK 摘要或签名指纹。它只能证明 Play 的分发决策,不能证明设备实际收到的二进制内容。固定身份不能将回执作为终端证据,必须配合设备提取的 APK 摘要。回执仅能作为辅助信息,帮助定位特定的分发事件,但不能替代实物证据。

使用本地 bundletool 按设备 spec 复现 APK 集时,结构应与设备安装集合近似,但 Play 服务器可能在交付时插入额外的资源配置 APK 或签名层,导致本地生成的 APK 数量、大小或证书指纹与设备端不同。这些差异使本地复现只能用作辅助验证,而不能单独作为取证断言。在分析差异时,应详细记录本地环境与线上环境的具体区别,以便解释不一致的原因。

在安全响应中,取证者应优先从受控设备提取实际 APK 和 data/app 目录下的 oat 文件,用 apksigner 和 bundletool 逆向分析其结构,再与构建系统记录的 AAB 和清单比对。缺失的 device spec 可以通过系统属性 ro.product.cpu.abi 和其他 build.prop 条目重建。核心原则是:设备真实安装的 APK 集身份优先于任何离线推导的结果。只有基于真实设备的证据才具有最高的法律效力和技术可信度。

理解交付回执与本地复现证据的差异对于准确判断安全事件至关重要。过度依赖回执可能导致误判,而忽视本地复现的价值则可能错失发现问题的机会。正确的做法是将两者结合起来,互为补充。通过对比回执中的元数据和本地复现的结构信息,可以更全面地了解应用的交付状态,及时发现潜在的异常情况,保障应用的安全运行。

  • 取证时优先提取设备 APK 并获得证书指纹,再联系 developer 提供的清单
  • 不要未经验证将 Play 回执等于设备包摘要
  • 当本地复现结果与设备 APK 数量或签名不一致时,记录差异原因并补充 Play 交付日志

事实依据与适用边界

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

本文判断事实或工程依据适用限制
AAB 在上传后由 Play 按设备配置生成派生 APK 集,最终安装的是该派生集而非原始 AAB。Android App Bundle format该文档未提供从上传 AAB 到设备安装的在线服务时延和重签名细节测量,因此不能推断交付过程的原子性或设备收到 APK 的具体时间窗口。
bundletool 可从 AAB 生成针对具体设备 spec 的 APK 集,并用于测试和验证。bundletool工具默认使用调试签名,除非显式传入发布密钥;该文档不保证本地生成的 APK 与 Play 线上产物的签名完全相同。
发布流程应区分应用签名密钥和上传密钥,Play App Signing 会使设备端使用应用签名密钥而非上传密钥。Sign your Android app该文档描述正确的密钥管理模型,但不包括对实际生产包签名证书的验证方法,也不能证明任意特定版本的 APK 确实使用了声明的应用签名密钥。
Android PackageInstaller 在安装 split APK 时强制校检所有 APK 的 packageName、versionCode 和签名一致性。Android PackageInstaller该 API 层面的约束仅适用于通过合规会话发起的安装,不能涵盖所有可能的非标准安装路径或已修改系统栈的场景。
bundletool 和测试轨道可用于重现 AAB 的派生 APK 行为,帮助开发者在交付前验证结构。Build and test Android App Bundles本地测试环境与 Play 线上交付在处理资源优化和即时动态模块方面存在差异,不能认为本地测试通过即全面代表线上表现。
in-toto 供应链声明通过绑定产物摘要与声明负载,防止报告与候选包错配。in-toto Attestation Statement v1声明格式本身不保证声明内容真实,需要可信签名者与门禁核验;且规格不会提供 Play 或特定渠道的签名者身份信息。
对 AAB 释放 split APK 的身份判定,必须将设备端提取的 APK 签名与供应链证明结合,而非仅依赖上传历史。Android App Bundle format该文档仅定义格式和分发模型,不涉及设备端证据收集规范或生产事故取证流程的具体设计。
使用 bundletool dump 命令可输出 AAB 内模块依赖和 target 信息,协助理解派生 split 组件。bundletooldump 命令只分析静态元数据,不能体现 Play 动态资源优化或即时模块的线上实现细节。

工程常见问题

AAB 自身有签名吗?如何验证上传包的签名?

AAB 文件可以签名,通常使用上传密钥。META-INF 目录中包含签名文件,可用 jarsigner 或 apksigner 验证,但该签名仅用于上传管道。设备端 APK 由应用签名密钥重签名,因此 AAB 签名不等同于设备安装的签名。验证上传包应使用上传密钥指纹,并保持与 Play Console 上传签名证书一致。

如果本地用 bundletool 生成 split APK 并用发布密钥签名,能直接当作设备交付包的证据吗?

不能直接等同。即使使用相同的发布密钥,bundletool 生成的结构和资源优化仍可能与 Play 服务器产生的交付包存在差异,且无法保证动态模块的延迟配置一致。这类产物仅可作为结构验证与补充证据,必须与真实设备提取的 APK 进行摘要比对和证书指纹核对,方可纳入证据链。

启用 Play App Signing 后,如何获取设备安装 APK 的预期签名指纹?

在 Google Play Console 的设置下的应用完整性页面,可下载应用签名证书,记下 SHA-256 指纹。设备端 APK 的证书应与该指纹一致。注意此证书由 Google 保管,与本地上传密钥不同。在取证时,应当以此指纹为基准,而不是使用本地 keystore 中的上传密钥指纹。

动态特性模块的 split APK 身份如何固定?

动态模块的 split APK 同样由包名、版本码和签名证书共同决定。安装时会校验这些一致性。固定身份时,应列出所有模块的 split 名称、版本和摘要,并确保它们由同一应用签名密钥签署。如果动态模块来自按需下载,还需要记录下载会话元数据,以关联到相应交付。

如果我只有设备上的 APK,如何反向确认它是由特定 AAB 生成的?

可通过提取 APK 的 manifest 和 split 名称,结合 bundletool 使用与该设备匹配的 device spec 对嫌疑 AAB 进行本地生成,然后将生成 APK 的资源表和 manifest 与设备 APK 比对。但签名部分若受 Play 重签名则无法直接匹配证书。更可靠的方法是比对 AAB 构建时记录的模块依赖和资源哈希,只要找到差异即可排除假设,但永远无法百分百证明关联,除非拥有 Play 提供的交付证明。

in-toto 声明可以替代 Play 的应用签名吗?

不能。in-toto 声明是供应链层面的辅助证据,它不能使未签名的 APK 变得可安装,也不能绕过设备安装器对签名的强制校验。声明的作用是将已被正确签名的 APK 摘要与构建上下文绑定,供审核和门禁系统验证构建环境未遭篡改。发布身份仍需真实有效的应用签名证书固定。

想用自己的 App 验证?

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

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