APK V2.0签名架构

references: https://source.android.google.cn/security/apksigning/v2

Jar签名

Android中支持两种签名方式:
* 基于JAR签名的方案(V1)
* Android7.0引入的APK签名方案(V2)

V1版本中存在两个问题:
* V1签名并不保护APk中的所有数据,如ZIP元数据就不被保护。APK验证程序要验证大量的不可信的数据结构,然后舍弃未受签名保护的数据。这就存在了很大的攻击面。
* APK验证程序必须解压所有已压缩的条目,而这需要花费更多的时间和内存,这些V2版本已基本改进或解决。

V2版本签名

# 概述

V2签名是一种全文件签名方案,该方案能够发现对APK的受保护部分进行的所有更改,从而有助于加快验证速度并增强完整性保护。

使用 APK 签名方案 v2 进行签名时,会在 APK 文件中插入一个 APK 签名分块,该分块位于“ZIP 中央目录”部分之前并紧邻该部分。在“APK 签名分块”内,v2 签名和签名者身份信息会存储在 APK 签名方案 v2 分块中。

  • Before signing:[Contents of ZIP entries] [Central Directory] [End of Central Directory]
  • After signing:[Contents of ZIP entries] [APK Signing Block] [Central Directory] [End of Central Directory]

APK 签名方案 v2 是在 Android 7.0 (Nougat) 中引入的。为了使 APK 可在 Android 6.0 (Marshmallow) 及更低版本的设备上安装,应先使用 JAR 签名功能对 APK 进行签名,然后再使用 v2 方案对其进行签名。

# APK签名分块

“APK签名分块”是为了支持V2签名而引入的新容器。该分块包含多个“ID-值”对,所采用的封装方式有助于更轻松地在 APK 中找到该分块。

APK 由一个或多个签名者/身份签名,每个签名者/身份均由一个签名密钥来表示。该信息会以“APK 签名方案 v2 分块”的形式存储。对于每个签名者,都会存储以下信息:
* (签名算法、摘要、签名)元组。摘要会存储起来,以便将签名验证和 APK 内容完整性检查拆开进行。
* 表示签名者身份的 X.509 证书链。
* 采用键值对形式的其他属性。

对于每位签名者,都会使用收到的列表中支持的签名来验证 APK。签名算法未知的签名会被忽略。如果遇到多个支持的签名,则由每个实现来选择使用哪个签名。这样一来,以后便能够以向后兼容的方式引入安全系数更高的签名方法。建议的方法是验证安全系数最高的签名。

Signature-Version: 1.0
Created-By: 1.0 (Android)
SHA-256-Digest-Manifest: JmVIb0OvMdgBYmmFwbImxjPuiNstTo705nQVKdfHw/0=
X-Android-APK-Signed: 2

Name: AndroidManifest.xml
SHA-256-Digest: 4Cf4bVSX/ReTgu/L040t7QfJ4v9awh6BABOO4FPLdTM=

Name: classes.dex
SHA-256-Digest: k6hzCKU/xExCm3SqpQjAiLdPaw5CGvcqWEWrwQa0LQs=

如上为“APK 签名方案 v2 分块”的格式,针对每个文件均生成一个签名。Android平台支持的签名算法包括:

  • 0x0101 - 采用 SHA2-256 摘要、SHA2-256 MGF1、32 个字节的盐且尾部为 0xbc 的 * RSASSA-PSS 算法
  • 0x0102 - 采用 SHA2-512 摘要、SHA2-512 MGF1、64 个字节的盐且尾部为 0xbc 的 RSASSA-PSS 算法
  • 0x0103 - 采用 SHA2-256 摘要的 RSASSA-PKCS1-v1_5 算法。此算法适用于需要确定性签名的编译系统。
  • 0x0104 - 采用 SHA2-512 摘要的 RSASSA-PKCS1-v1_5 算法。此算法适用于需要确定性签名的编译系统。
  • 0x0201 - 采用 SHA2-256 摘要的 ECDSA 算法
  • 0x0202 - 采用 SHA2-512 摘要的 ECDSA 算法
  • 0x0301 - 采用 SHA2-256 摘要的 DSA 算法

# 受完整性保护的内容

为了保护 APK 内容,APK 包含以下 4 个部分:

  • 1)ZIP 条目的内容(从偏移量 0 处开始一直到“APK 签名分块”的起始位置)
  • 2)APK 签名分块
  • 3)ZIP 中央目录
  • 4)ZIP 中央目录结尾

APK 签名方案 v2 负责保护第 1、3、4 部分的完整性,以及第 2 部分包含的“APK 签名方案 v2 分块”中的 signed data 分块的完整性。

# 防回滚保护

攻击方式1:
攻击者可能会试图在支持对带 v2 签名的 APK 进行验证的 Android 平台上将带 v2 签名的 APK
作为带 v1 签名的 APK 进行验证。

防护措施1:为了防范此类攻击,带 v2 签名的 APK 如果还带 v1 签名,其 META-INF/.SF 文件的主要部分中必须包含 X-Android-APK-Signed 属性。 该属性的值是一组以英文逗号分隔的 APK 签名方案 ID(v2 方案的 ID 为 2)。在验证 v1 签名时,对于此组中验证程序首选的 APK 签名方案(例如,v2 方案),如果 APK 没有相应的签名,APK 验证程序必须要拒绝这些 APK。此项保护依赖于内容 META-INF/.SF 文件受 v1 签名保护这一事实。

攻击方式2:攻击者可能会试图从“APK 签名方案 v2 分块”中删除安全系数较高的签名。

防护措施2:为了防范此类攻击,对 APK 进行签名时使用的签名算法 ID 的列表会存储在通过各个签名保护的 signed data 分块中。

# 验证

如图为Android7.0+版本的APK签名验证过程。
这里写图片描述

# V2验证
  • 1)找到“APK签名分块”并验证以下内容
    • a) “APK 签名分块”的两个大小字段包含相同的值。
    • b) “ZIP 中央目录结尾”紧跟在“ZIP 中央目录”记录后面。
    • c) “ZIP 中央目录结尾”之后没有任何数据。
  • 2)找到“APK 签名分块”中的第一个“APK 签名方案 v2 分块”。如果 v2 分块存在,则继续执行第 3 步。否则,回退至使用 v1 方案验证 APK。
  • 3)对“APK 签名方案 v2 分块”中的每个 signer 执行以下操作:
    • a) 从 signatures 中选择安全系数最高的受支持 signature algorithm ID。安全系数排序取决于各个实现/平台版本。
    • b) 使用 public key 并对照 signed data 验证 signatures 中对应的 signature。(现在可以安全地解析 signed data 了。)
    • c) 验证 digests 和 signatures 中的签名算法 ID 列表(有序列表)是否相同。(这是为了防止删除/添加签名。)
    • d) 使用签名算法所用的同一种摘要算法计算 APK 内容的摘要。
    • e) 验证计算出的摘要是否与 digests 中对应的 digest 相同。
    • f) 验证 certificates 中第一个 certificate 的 SubjectPublicKeyInfo 是否与 public key 相同。
  • 4)如果找到了至少一个 signer,并且对于每个找到的 signer,第 3 步都取得了成功,APK 验证将会成功。
# V1验证

完整性按照以下方式进行验证:

  • 1)每个签名者均由一个包含 META-INF/.SF 和 META-INF/.(RSA|DSA|EC) 的 JAR 条目来表示。
  • 2).(RSA|DSA|EC) 是具有 SignedData 结构的 PKCS #7 CMS ContentInfo,其签名通过 .SF 文件进行验证。
  • 3).SF 文件包含 META-INF/MANIFEST.MF 的全文件摘要和 META-INF/MANIFEST.MF 各个部分的摘要。需要验证 MANIFEST.MF 的全文件摘要。如果该验证失败,则改为验证 MANIFEST.MF 各个部分的摘要。
  • 4)对于每个受完整性保护的 JAR 条目,META-INF/MANIFEST.MF 都包含一个具有相应名称的部分,其中包含相应条目未压缩内容的摘要。所有这些摘要都需要验证。
  • 5)如果 APK 包含未在 MANIFEST.MF 中列出且不属于 JAR 签名一部分的 JAR 条目,APK 验证将会失败。

因此,保护链是每个受完整性保护的 JAR 条目的 .(RSA|DSA|EC) -> .SF -> MANIFEST.MF -> 内容。

猜你喜欢

转载自blog.csdn.net/zjx839524906/article/details/80961603