Android 11 通过 APK 签名方案 v4 支持与流式传输兼容的签名方案。v4 签名基于根据 APK 的所有字节计算得出的 Merkle 哈希树。它完全遵循 fs-verity 哈希树的结构(例如,对盐进行零填充,以及对最后一个分块进行零填充。)Android 11 将签名存储在单独的 <apk name>.apk.idsig
文件中。v4 签名需要 v2 或 v3 签名作为补充。
文件格式
所有数字字段均采用小端字节序。所有字段占据的字节数均与其 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; };
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
是第一个可用的内容摘要(按顺序排列):
- V3、1MB 分块、SHA2-512 (CONTENT_DIGEST_CHUNKED_SHA512)、
- V3、4KB 分块、SHA2-256 (CONTENT_DIGEST_VERITY_CHUNKED_SHA256)、
- V3、1MB 分块、SHA2-256 (CONTENT_DIGEST_CHUNKED_SHA256)、
- V2、SHA2-512、
- V2、SHA2-256。
请参阅 APK 签名方案 v3 中的带长度前缀的签名的(带长度前缀)序列。
![APK 验证流程 v4](https://source.android.google.cn/static/docs/security/images/apk-validation-process-v4.png?authuser=4&hl=lt)
验证和测试
使用功能单元测试和 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