APK 签名方案 v4

Android 11 通过 APK 签名方案 v4 支持与流式传输兼容的签名方案。v4 签名基于根据 APK 的所有字节计算得出的 Merkle 哈希树。它完全遵循 fs-verity 哈希树的结构(例如,对盐进行零填充,以及对最后一个分块进行零填充。)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 文档中所述。

生产方和使用方

如果您使用默认参数运行 v4 签名文件,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 会将剥离的 v4 签名文件(以 Base64 编码)作为添加的每个文件的一项参数加以接受。相应 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 验证流程 v4
图 1:APK 验证流程 v4

验证和测试

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

  • CtsIncrementalInstallHostTestCases
    • /android/cts/hostsidetests/incrementalinstall

测试签名格式

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

$ atest PackageManagerShellCommandTest
PackageManagerShellCommandIncrementalTest

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

若要使用 Android SDK 测试签名格式,请设置开发环境,并确保您已完成 IncFS 的实现。然后,在目标实体设备或模拟器上刷写相应 build。您需要生成或获取现有的 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