Google 系统服务 APK 透明度完整验证

本页概述了各种方法,以确保安装在 您的 Android 设备上的 APK 与 声明方模型中所做的声明相符。这包括从设备中提取相关 APK、检查其代码完整性,以及对提取的工件执行日志包含证明。

验证流程

透明度日志采用由哈希值组成的 Merkle 树实现。 叶节点包含数据,父节点包含其子节点的哈希值。

基本上,系统会对 Merkle 树执行两项计算,以验证透明度日志的防篡改属性:包含证明和一致性证明。前者证明日志包含与特定 APK 版本对应的条目。日志条目包含一个哈希值,该哈希值是 JSON Web 令牌 (JWT) 形式的代码签名令牌的 SHA256 摘要,可以从相应的 APK 中获取。后者证明,当向树中添加新条目时,新检查点在密码学上与树的先前版本一致。

如需验证受保护的 APK,请根据见证的检查点执行包含证明测试。 请注意,我们计划使用标准化 见证协议将此日志与公共见证网络集成。 这将提供一个见证的检查点,以保证日志的一致性。

如果您想确信设备上的 APK 符合声明方模型中所做的声明,请参阅下面的说明。

包含证明

Android 用户可以先提取设备上受保护的 APK 及其相关元数据,然后将其重新计算的根哈希值与已发布的检查点中包含的根哈希值进行比较,以检查受保护的 APK 是否在日志中。 如果两者匹配,则 Android 用户可以确信自己受到威胁模型中所述的某些保护。

如何验证日志中是否包含 APK

如前所述,您可以在概览页面中找到当前受保护的 APK 列表 。

通过验证需要满足的前提条件

在继续验证工件(固件或 APK)是否符合声明之前,请确保网络连接的计算机上提供以下工具。 根据验证目标的不同,您可能不需要所有这些工具。

Golang

验证工具 是用 Go 编写的。如需构建该工具,请从 Go 网站安装 Go 1.24 或更高版本 。

Android 调试桥 (ADB)

ADB 是一种与 Android 设备通信以检查映像或 提取 APK 的工具,可在 Android SDK 平台工具 网站上找到。

包含证明验证程序

包含证明验证程序是一个 Go 模块,用于查询透明度日志并输出工件是否包含在日志中。 它发布在 android-binary-transparency GitHub 代码库中,网址为 https://github.com/android/android-binary-transparency/tree/main

如需克隆 AOSP avb 代码库,请执行以下操作:

computer:~$ git clone https://android.googlesource.com/platform/external/avb

如需克隆 android-binary-transparency GitHub 代码库,请执行以下操作:

computer:~$ git clone https://github.com/android/android-binary-transparency

AAPT2

AAPT2(Android 资源打包工具)是一种构建工具,用于编译和打包 Android 应用的资源。 您可以在 Android SDK Build Tools 26.0.2 及更高版本中找到作为独立工具提供的 AAPT2。

bundletool

bundletool 是一种用于构建 Android App Bundle (AAB) 的工具。它还可用于验证 APK 代码签名。 您可以从 GitHub 下载该工具。

wget 或 curl

wgetcurl 是可用于从互联网检索或下载文件的工具。 请务必安装其中一个工具,因为验证脚本可能依赖于这些工具。

Avbtool

avbtool 用于计算出厂映像的 VBMeta 摘要和验证 二进制文件。 克隆 avb 代码库后(如包含证明验证程序 部分所示),您可以在克隆的 avb 目录中找到 avbtool.py。 您应将此目录添加到 $PATH 环境变量中。

computer:~$ PATH=$PATH:DIRECTORY_CONTAINING_AVBTOOL

Fbpacktool

fbpacktool 是一个 Python 脚本,用于解压缩 Pixel 的 bootloader.img中的各个组件。 如果您要验证 APK,则不需要此脚本。 此工具非常重要,因为它可让 avbtool.py 发现验证所需的映像。

如需使用此工具,您应将以下三个 Python 脚本 下载到 avbtool.py 所在的目录中, 使 fbpacktool.py 可执行, 并创建一个名为 fbpacktool 的符号链接到 fbpacktool.py,以方便使用。

如果您有 wget,请执行以下操作:

computer:dir_to_avbtool$ wget https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/fbpacktool.py \
https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/fbpack.py \
https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/packedstruct.py
computer:dir_to_avbtool$ chmod +x fbpacktool.py
computer:dir_to_avbtool$ ln -s fbpacktool.py fbpacktool

如果您有 curl,请执行以下操作:

computer:dir_to_avbtool$ curl -O https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/fbpacktool.py
computer:dir_to_avbtool$ curl -O https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/fbpack.py
computer:dir_to_avbtool$ curl -O https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/packedstruct.py
computer:dir_to_avbtool$ chmod +x fbpacktool.py
computer:dir_to_avbtool$ ln -s fbpacktool.py fbpacktool

OpenSSL

openssl 有助于在验证工作流中使用终端执行各种加密 操作。 请确保您的系统上已安装该工具。 您可以在终端中运行以下命令,并确保返回的结果不为空,以进行检查:

computer:~$ which openssl
/usr/bin/openssl

构建用于验证的载荷

如需验证您从设备中提取的 APK 是否符合我们的声明,您必须根据从 APK 中派生的信息构建日志载荷。

在开始之前,请确保在设备上启用 adb 调试,以便在设备上使用 adb

然后,找到 APK 在设备上的安装位置。在本指南中,我们将使用 Android System Key Verifier APK (com.google.android.contactkeys) 作为工作示例。

computer:~$ adb shell pm list packages -f | grep contactkeys
package:/data/app/~~i5WYSO4PuAAv798-eHdM7A==/com.google.android.contactkeys-PQCKjnn7xDqjeVhcUDibBA==/base.apk=com.google.android.contactkeys

如果您的设备上安装了 Android System Key Verifier APK,则上述命令将返回一个路径,指示该 APK 在设备上的安装位置。 否则,您将不会 看到任何输出。

然后,使用以下命令将 APK 从 Android 设备下载到您正在使用的计算机上(请注意,设备上的实际位置和 APK 文件名可能会有所不同):

computer:~$ mkdir -p /tmp/testdir && cd /tmp/testdir
computer:/tmp/testdir$ adb pull /data/app/~~i5WYSO4PuAAv798-eHdM7A==/com.google.android.contactkeys-PQCKjnn7xDqjeVhcUDibBA==/base.apk ./contactkeys_candidate.apk

如需获取并验证您刚刚下载的 APK 的软件包名称,您需要先解压缩 APK,因为 APK 最终是一种特殊的 ZIP 文件。

computer:/tmp/testdir$ mkdir extracted && unzip contactkeys_candidate.apk -d extracted/

此步骤会解压缩构成 APK 的所有文件。您可以在 APK 的 清单 中找到软件包名称和 版本,该清单通常位于名为 AndroidManifest.xml 的文件中。

不过,获取的清单文件采用二进制形式,人类无法读取。 为了将二进制 XML 转换为人类可读的形式,我们使用了之前安装的 aapt2 工具(如 前提条件部分中所述,需要安装该工具)。

computer:/tmp/testdir$ aapt2 dump badging ./contactkeys_candidate.apk
package: name='com.google.android.contactkeys' versionCode='7805' versionName='1.219.791156583' platformBuildVersionName='Baklava' platformBuildVersionCode='36' compileSdkVersion='36' compileSdkVersionCodename='Baklava'

从上面的输出中,我们现在可以确定此 APK 的软件包名称为 com.google.android.contactkeys,版本号 (versionCode) 为 1413

现在,我们将在 APK 中搜索代码公开透明签名。它应该是名为 code_transparency_signed.jwt 的文件,包含在 META-INF 文件夹中,与其他从 APK 中提取的文件一起。

computer:/tmp/testdir$ sha256sum extracted/META-INF/code_transparency_signed.jwt
1779a2aee029112c2c9bfc9390b9678f3e5f4595b39705e8528dd522e8042f11  code_transparency_signed.jwt

有了此哈希字符串,我们现在就拥有了根据 日志内容部分中所述的格式拼凑 日志载荷所需的每一条信息。在此示例中,相应的日志载荷应如下所示:

1779a2aee029112c2c9bfc9390b9678f3e5f4595b39705e8528dd522e8042f11
SHA256(Signed Code Transparency JWT)
com.google.android.contactkeys
1143

另请注意软件包版本后的换行符。

您可以将内容保存到文件中,例如 payload.txt。这在稍后进行包含证明测试时会非常有用。

验证 APK 代码签名的真实性

现在,我们应验证 APK 中嵌入的代码签名令牌的真实性。为此,我们使用了 bundletool 和最初用于对其进行签名的密钥对的公钥。它们发布在相应 APK 的每个部分中。假设您已将公钥证书 (例如,Android System Key Verifier 的公钥证书) 保存到名为 signing_cert_pubkey.pem 的文件中, 请按照以下指南执行代码签名验证。

首先,您应创建一个 ZIP 归档,并将候选 APK 添加到该 ZIP 归档中。

computer:/tmp/testdir$ zip -u test.zip contactkeys_candidate.apk
        zip warning: test.zip not found or empty
  adding: contactkeys_candidate.apk (deflated 58%)

computer:/tmp/testdir$ file test.zip
test.zip: Zip archive data, at least v2.0 to extract, compression method=deflate

我们现在可以使用 bundletoolcheck-transparency 命令来验证候选 APK 中嵌入的代码签名是否与已发布的代码签名匹配。

computer:/tmp/testdir$ java -jar BUNDLETOOL_INSTALL_PATH/bundletool-all-version.jar check-transparency \
  --mode=apk \
  --apk-zip=test.zip \
  --transparency-key-certificate=signing_cert_pubkey.pem

APK signature is valid. SHA-256 fingerprint of the apk signing key certificate (must be compared with the developer's public key manually): D9 E1 73 5B 2A 39 51 27 3A 87 35 B7 66 9E F1 9E F5 3A F1 C1 27 5C BA 31 39 3C 18 40 8B 03 79 D0
Code transparency signature verified for the provided code transparency key certificate.
Code transparency verified: code related file contents match the code transparency file.

您应确保上述命令的输出表明 代码公开透明签名代码公开透明机制均已 通过验证。 如果未通过验证,例如,如果您看到类似 Code transparency verification failed because the provided public key certificate does not match the transparency file的输出,则表示相关 APK 的代码完整性可能已受到 破坏,您不应 信任该 APK。 请务必仔细检查您是否针对正确的公钥证书进行验证。 否则,如果其他一切都通过检查,则表示您正在验证的 APK 的代码签名真实性已通过验证。

验证软件包包含情况(包含证明)

使用您之前构建的载荷 earlier,您现在可以测试相关软件包是否已包含在透明度日志中。

包含证明工具已发布在 avb 代码库 中 的 Android 开源项目中。如需运行该工具,请执行以下操作:

computer:external/avb/tools/transparency/verify$ PAYLOAD_PATH=PATH_TO_PAYLOAD_DIR/payload.txt
computer:external/avb/tools/transparency/verify$ go build cmd/verifier/verifier.go
computer:external/avb/tools/transparency/verify$ ./verifier --payload_path=${PAYLOAD_PATH} --log_type=google_1p_code

验证程序使用相应的检查点和日志内容(位于 tile 目录中)来检查您的 APK 载荷是否在透明度 日志中,以验证该载荷是否确实由 Google 发布。

该命令的输出将写入 stdout:

  • 如果软件包的代码包含在日志中,则输出 OK. inclusion check success!
  • 否则输出 FAILURE