APK 签名方案 v4

Android 11 通过 APK 签名方案 v4 支持流式兼容的签名方案。v4 签名基于对 APK 的所有字节计算的 Merkle 哈希树。它完全遵循 fs-verity 哈希树的结构(例如,对 salt 进行零填充和对最后一个块进行零填充)。Android 11 将签名存储在单独的文件 <apk name>.apk.idsig 中。v4 签名需要补充的 v2v3 签名。

文件格式

所有数字字段均为小端字节序。所有字段占用的字节数与其 sizeof() 完全相同,不添加隐式填充或对齐。

以下是一个辅助结构体,用于简化定义。

template <class SizeT>
struct sized_bytes {
        SizeT size;
        byte bytes[size];
};

主文件内容

struct V4Signature {
        int32 version; // only version 2 is supported as of now
        sized_bytes<int32> hashing_info;
        sized_bytes<int32> signing_info;
        sized_bytes<int32> merkle_tree;  // optional
};

hashing_info 是用于哈希树生成的参数 + 根哈希

struct hashing_info.bytes {
    int32 hash_algorithm;    // only 1 == SHA256 supported
    int8 log2_blocksize;     // only 12 (block size 4096) supported now
    sized_bytes<int32> salt; // used exactly as in fs-verity, 32 bytes max
    sized_bytes<int32> raw_root_hash; // salted digest of the first Merkle tree page
};

signing_info 是以下结构体

struct signing_info.bytes {
    sized_bytes<int32> apk_digest;  // used to match with the corresponding APK
    sized_bytes<int32> x509_certificate; // ASN.1 DER form
    sized_bytes<int32> additional_data; // a free-form binary data blob
    sized_bytes<int32> public_key; // ASN.1 DER, must match the x509_certificate
    int32 signature_algorithm_id; // see the APK v2 doc for the list
    sized_bytes<int32> signature;
};
  • apk_digest 取自 APK 的 v3 签名块,如果不存在,则取自 v2 块(请参阅 apk_digest

要创建和验证 signature,代码必须将以下数据序列化为二进制 blob,并将其作为签名数据传递到签名/验证算法中

struct V4DataForSigning {
        int32 size;
        int64 file_size; // the size of the file that's been hashed.
        hashing_info.hash_algorithm;
        hashing_info.log2_blocksize;
        hashing_info.salt;
        hashing_info.raw_root_hash;
        signing_info.apk_digest;
        signing_info.x509_certificate;
        signing_info.additional_data;
};
  1. merkle_tree 是 APK 的完整 Merkle 树,其计算方法如 fs-verity 文档中所述。

生产者和消费者

apksigner Android SDK 工具现在会在您使用默认参数运行时生成 v4 签名文件。可以像禁用其他签名方案一样禁用 v4 签名。它还可以验证 v4 签名是否有效。

运行 adb install --incremental 命令时,adb 期望 .apk.idsig 文件与 .apk 文件位于同一目录下
默认情况下,它还将使用 .idsig 文件尝试增量安装,如果该文件丢失或无效,则会回退到常规安装。

创建安装会话时,PackageInstaller 中的新流式安装 API 在向会话添加文件时,接受剥离的 v4 签名作为单独的参数。此时,signing_info 作为整个 blob 传递到 incfs 中。Incfs 从 blob 中提取根哈希。

在提交安装会话时,PackageManagerService 执行 ioctl 以从 incfs 检索 signing_info blob,解析它并验证签名。

增量数据加载器组件预计将通过数据加载器原生 API 流式传输签名的 Merkle 树部分。
package 服务 shell 命令 install-incremental 接受编码为 base64 的剥离 v4 签名文件作为每个添加文件的参数。相应的 Merkle 树必须发送到命令的 stdin 中。

apk_digest

apk_digest 是按顺序排列的第一个可用的内容摘要

  1. V3,1MB 块,SHA2-512 (CONTENT_DIGEST_CHUNKED_SHA512),
  2. V3,4KB 块,SHA2-256 (CONTENT_DIGEST_VERITY_CHUNKED_SHA256),
  3. V3,1MB 块,SHA2-256 (CONTENT_DIGEST_CHUNKED_SHA256),
  4. V2,SHA2-512,
  5. V2,SHA2-256。

请参阅 APK 签名方案 v3 中的长度前缀的签名序列

apk validation process v4
图 1:APK 验证过程 v4

验证和测试

使用功能单元测试和 CTS 验证实现。

  • CtsIncrementalInstallHostTestCases
    • /android/cts/hostsidetests/incrementalinstall

测试签名格式

要测试签名格式,设置开发环境 并运行以下手动测试

$ atest PackageManagerShellCommandTest
PackageManagerShellCommandIncrementalTest

使用 Android SDK(ADB 和 apksigner)测试签名格式

要使用 Android SDK 测试签名格式,设置开发环境 并确保您已完成 IncFS 的实施。然后将构建刷入目标物理设备或模拟器。您需要生成或获取现有 APK,然后创建 调试签名密钥。最后,从 build-tools 文件夹中使用 v4 签名格式签名并安装 apk。

签名

$ ./apksigner sign --ks debug.keystore game.apk

安装

$ ./adb install game.apk

这些测试在哪里可以找到?

/android/cts/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java