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

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

验证流程

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

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

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

如果您想确信设备上的 APK 是否符合版权主张方模型中提出的声明,请参阅以下说明。

收录证明

Android 用户可以先提取 APK 及其相关元数据,然后将重新计算的根哈希与已发布的检查点中包含的根哈希进行比较,以检查其设备上受保护的 APK 是否在日志中。如果匹配,Android 用户可以放心,因为他们将获得威胁模型中所述的某些保护。

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

如前所述,您可以在“概览”页面中找到当前涵盖的 APK 列表。

验证的前提条件

在继续验证您刚刚从设备中提取的 APK 是否符合我们的声明之前,您需要从已连接到网络的计算机中安装以下工具。

Android 调试桥 (ADB)

ADB 是一款用于与 Android 设备通信的工具,可在 Android SDK 平台工具网站上找到。

bundletool

bundletool 是一种用于构建 Android App Bundle (AAB) 的工具。它还可用于将 AAB 转换为可安装在设备上的 APK。您可以从 GitHub 下载该工具。

Androguard

Androguard 是一组用于分析 APK 的工具。您可以从 Androguard 网站下载并安装该工具。

包含证明验证器

这是我们在名为 avb 的 Android 开源项目 (AOSP) 中的 Git 代码库中发布的 Go 模块。它能够查询 Google 系统服务 APK 透明度日志,并输出软件包是否包含在日志中。如需查看此方法的使用示例,请参阅后面的部分

如需下载此工具,您必须先克隆 avb 代码库。

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

您可以在 avb 代码库内的 tools/transparency/verify 中找到验证程序的源代码。

构建要验证的载荷

如需验证您根据我们的声明从设备中提取的 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 转换为人类可读的形式,我们使用了 androguard 套件中的 axml 工具(如需安装,请参阅前提条件部分)。

computer:/tmp/testdir$ androguard axml extracted/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1413" android:versionName="1.48.688082145" android:compileSdkVersion="35" android:compileSdkVersionCodename="VanillaIceCream" android:requiredSplitTypes="" android:splitTypes="" package="com.google.android.contactkeys" platformBuildVersionCode="35" platformBuildVersionName="VanillaIceCream">

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

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

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 系统密钥验证器)保存到名为 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 的代码签名的真实性已通过验证。

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

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

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

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_system_apk

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

该命令的输出会写入 stdout:

  • OK. inclusion check success!(如果日志中包含软件包的代码)
  • FAILURE