APK 签名方案 v3

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

Android 9 支持APK 密钥轮换,这使应用能够在 APK 更新中更改其签名密钥。为了使轮换切实可行,APK 必须指明新旧签名密钥之间的信任级别。为了支持密钥轮换,我们将APK 签名方案从 v2 更新到 v3,以允许使用新旧密钥。 V3 向 APK 签名块添加了有关支持的 SDK 版本和旋转证明结构的信息。

APK 签名块

为了保持与 v1 APK 格式的向后兼容性,v2 和 v3 APK 签名存储在 APK 签名块中,位于 ZIP 中央目录之前。

v3 APK 签名块格式与 v2 相同。 APK 的 v3 签名以 ID 值对的形式存储,ID 为 0xf05368c0。

APK 签名方案 v3 块

v3 方案的设计与v2 方案非常相似。它具有相同的通用格式,并支持相同的签名算法 ID 、密钥大小和 EC 曲线。

但是,v3 方案添加了有关支持的 SDK 版本和旋转证明结构的信息。

格式

APK 签名方案 v3 块存储在 APK 签名块中,ID 0xf05368c0

APK 签名方案 v3 块的格式遵循 v2 的格式:

  • 长度前缀signer的长度前缀序列:
    • 以长度为前缀的有signed data
      • 长度前缀digests的长度前缀序列:
        • signature algorithm ID (4字节)
        • digest (长度前缀)
      • X.509 certificates的长度前缀序列:
        • 以长度为前缀的 X.509 certificate (ASN.1 DER 形式)
      • minSDK (uint32) - 如果平台版本低于此数字,则应忽略此签名者。
      • maxSDK (uint32) - 如果平台版本高于此数字,则应忽略此签名者。
      • 以长度为前缀的additional attributes的长度前缀序列:
        • ID (uint32)
        • value (可变长度:附加属性的长度 - 4 个字节)
        • ID - 0x3ba06f8c
        • value -旋转证明结构
    • minSDK (uint32) - 签名数据部分中 minSDK 值的副本 - 如果当前平台不在范围内,则用于跳过此签名的验证。必须匹配带符号的数据值。
    • maxSDK (uint32) - 签名数据部分中 maxSDK 值的副本 - 如果当前平台不在范围内,则用于跳过此签名的验证。必须匹配带符号的数据值。
    • 长度前缀signatures的长度前缀序列:
      • signature algorithm ID (uint32)
      • 签名signed data上的长度前缀signature
    • 长度前缀public key (SubjectPublicKeyInfo,ASN.1 DER 形式)

旋转证明和自信任旧证书结构

轮换证明结构允许应用轮换其签名证书,而不会被与其通信的其他应用程序阻止。为此,应用签名包含两条新数据:

  • 向第三方断言应用程序的签名证书可以在其前身受信任的任何地方被信任
  • 应用程序本身仍然信任的应用程序的旧签名证书

签名数据部分中的旋转证明属性由一个单链表组成,每个节点都包含一个签名证书,用于对应用程序的先前版本进行签名。此属性旨在包含概念性的旋转证明和自信任旧证书数据结构。该列表按版本排序,其中最早的签名证书对应于根节点。旋转证明数据结构是通过让每个节点中的证书签署列表中的下一个证书来构建的,从而为每个新密钥注入证据,证明它应该与旧密钥一样受信任。

self-trusted-old-certs 数据结构是通过向每个节点添加标志来构建的,这些标志指示其在集合中的成员资格和属性。例如,可能存在一个标志,指示给定节点上的签名证书是可信的,以获得 Android 签名权限。此标志允许由旧证书签名的其他应用程序仍被授予由使用新签名证书签名的应用程序定义的签名权限。因为整个循环证明属性驻留在 v3 signer字段的已签名数据部分中,所以它受到用于签署包含 apk 的密钥的保护。

这种格式排除了多个签名密钥不同祖先签名证书聚合为一个(多个起始节点到一个公共接收器)。

格式

旋转证明存储在 ID 0x3ba06f8c下的 APK 签名方案 v3 块中。它的格式是:

  • 长度前缀levels的长度前缀序列:
    • 以长度为前缀的signed data (由先前的证书 - 如果存在)
      • 以长度为前缀的 X.509 certificate (ASN.1 DER 形式)
      • signature algorithm ID (uint32) - 证书在上一级使用的算法
    • flags (uint32) - 指示此证书是否应位于 self-trusted-old-certs 结构中以及用于哪些操作的标志。
    • signature algorithm ID (uint32) - 必须与下一级签名数据部分中的 ID 匹配。
    • 上述signed data的长度前缀signature

多个证书

Android 当前将使用多个证书签名的 APK 视为具有与组成证书分开的唯​​一签名身份。因此,签名数据部分中的旋转证明属性形成了一个有向无环图,可以更好地将其视为一个单链表,给定版本的每一组签名者代表一个节点。这为旋转证明结构(下面的多签名者版本)增加了额外的复杂性。特别是,排序成为一个问题。更重要的是,不再能够独立签署 APK,因为旋转证明结构必须让旧的签名证书签署新的证书集,而不是一个接一个地签署它们。例如,由密钥 A 签名的 APK 希望由两个新密钥 B 和 C 签名,B 签名者不能只包含 A 或 B 的签名,因为这与 B 和 C 的签名身份不同。这将意味着签名者必须在构建这样的结构之前进行协调。

多签名者旋转证明属性

  • 长度前缀sets的长度前缀序列:
    • signed data (由前一组 - 如果存在)
      • 以长度为前缀的certificates序列
        • 以长度为前缀的 X.509 certificate (ASN.1 DER 形式)
      • signature algorithm IDs序列 (uint32) - 一个用于前一组中的每个证书,顺序相同。
    • flags (uint32) - 指示这组证书是否应该在 self-trusted-old-certs 结构中以及针对哪些操作的标志。
    • 长度前缀signatures的长度前缀序列:
      • signature algorithm ID (uint32) - 必须与签名数据部分中的匹配
      • 上述signed data的长度前缀signature

旋转证明结构中的多个祖先

v3 方案也不处理两个不同的密钥轮换到同一个应用程序的同一个签名密钥。这与收购的情况不同,收购公司希望移动被收购的应用程序以使用其签名密钥来共享权限。此次收购被视为受支持的用例,因为新应用程序将通过其包名称来区分,并且可能包含其自己的旋转证明结构。不支持的情况,即同一个应用程序有两条不同的路径来获得相同的证书,打破了密钥轮换设计中的许多假设。

确认

在 Android 9 及更高版本中,可以根据 APK 签名方案 v3、v2 方案或 v1 方案验证 APK。旧平台忽略 v3 签名并尝试验证 v2 签名,然后是 v1。

APK签名验证流程

图 1. APK 签名验证流程

APK 签名方案 v3 验证

  1. 找到 APK 签名块并验证:
    1. APK 签名块的两个大小字段包含相同的值。
    2. ZIP Central Directory 后紧跟 ZIP End of Central Directory 记录。
    3. 中央目录的 ZIP 结尾后面没有更多数据。
  2. 在 APK 签名块内找到第一个 APK 签名方案 v3 块。如果存在 v3 块,请继续执行步骤 3。否则,回退到使用 v2 方案验证 APK。
  3. 对于具有当前平台范围内的最小和最大 SDK 版本的 APK 签名方案 v3 块中的每个signer
    1. signatures中选择支持的最强signature algorithm ID 。强度排序取决于每个实现/平台版本。
    2. 使用public key验证signatures signed data中的相应signature 。 (现在解析signed data是安全的。)
    3. 验证签名数据中的最小和最大 SDK 版本是否与为signer指定的版本匹配。
    4. 验证digestssignatures中签名算法 ID 的有序列表是否相同。 (这是为了防止签名剥离/添加。)
    5. 使用与签名算法使用的摘要算法相同的摘要算法计算 APK 内容的摘要。
    6. 验证计算出的摘要与来自digests的相应digest相同。
    7. 验证第一个certificatecertificates是否与public key相同。
    8. 如果signer的旋转证明属性存在,则验证该结构是有效的,并且此signer是列表中的最后一个证书。
  4. 如果在当前平台的范围内恰好找到一个signer ,并且该signer的步骤 3 成功,则验证成功。

验证

要测试您的设备是否正确支持 v3,请在cts/hostsidetests/appsecurity/src/android/appsecurity/cts/中运行PkgInstallSignatureVerificationTest.java CTS 测试。