Android APP签名机制

通过应用签名,开发者可以标识应用创作者并更新其应用,而无需创建复杂的接口和权限。在 Android 平台上运行的每个应用都必须要开发者签名。Google Play 或 Android 设备上的软件包安装程序会拒绝没有经过签名就尝试安装的应用。

Android 支持以下四种应用签名方案:

APK签名方案 v1

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

  1. 每个签名者均由一个包含 META-INF/CERT.SF 和 META-INF/CERT.(RSA|DSA|EC) 的 JAR 条目表示。
  2. CERT.(RSA|DSA|EC) 是具有 SignedData 结构的 PKCS #7 CMS ContentInfo,其签名通过 CERT.SF 进行验证。
  3. CERT.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 条目的 CERT.(RSA|DSA|EC) -> CERT.SF -> MANIFEST.MF -> 内容。

MANIFEST.MF

该文件中保存的内容其实就是逐一遍历 APK 中的所有条目,如果是目录就跳过,如果是一个文件,就用 SHA256消息摘要算法提取出该文件的摘要然后进行 BASE64 编码后,作为「SHA-256-Digest」属性的值写入到 MANIFEST.MF 文件中的一个块中

image-20250104161140991

CERT.SF

SHA-256-Digest-Manifest:对整个 MANIFEST.MF 文件做SHA256后再用 Base64 编码
SHA-256-Digest:对 MANIFEST.MF 的各个条目做 SHA256后再用 Base64 编码

image-20250104161304173

CERT.RSA

把 CERT.SF 文件,用私钥计算出签名, 然后将签名以及包含公钥信息的数字证书一同写入 CERT.RSA 中保存。Android APK 中的 CERT.RSA 证书是自签名的,并不需要这个证书是第三方权威机构发布或者认证的,用户可以在本地机器自行生成这个自签名证书。

可以使用如下命令查看证书:

1
openssl pkcs7 -inform DER -in CERT.RSA -text -noout -print_certs

windows 的openssl可以在这个网站下载:Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions

image-20250104161856975

APK签名方案 v2

APK signature scheme v2 | Android Open Source Project

APK签名方案v2是一种全文件签名方案,该方案可以防止对APK受保护部分进行的更改;使用v2签名方案时会在APK文件中插入一个APK签名分块。APK 签名方案 v2 分块存储在 APK 签名分块内,ID 为 0x7109871a

签名前和签名后的 APK

v2签名后的apk包含四个部分:

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

其中,应用签名方案的签名信息会被保存在 区块 2(APK Signing Block)中,而区块 1(Contents of ZIP entries)、区块 3(ZIP Central Directory)、区块 4(ZIP End of Central Directory)是受保护的,在签名后任何对区块 1、3、4 的修改都逃不过新的应用签名方案的检查。

签名

第 1、3 和 4 部分的摘要采用以下计算方式,类似于两级 Merkle 树。每个部分都会被拆分成多个大小为 1MB的连续块。每个部分的最后一个块可能会短一些。每个块的摘要均通过字节 0xa5 的串联、块的长度(采用小端字节序的 uint32 值,以字节数计)和块的内容进行计算。顶级摘要通过字节 0x5a 的串联、块数(采用小端字节序的 uint32 值)以及块的摘要的串联(按照块在 APK 中显示的顺序)进行计算。摘要以分块方式计算,以便通过并行处理来加快计算速度:

APK 摘要

由于第 4 部分(ZIP 中央目录结尾)包含“ZIP 中央目录”的偏移量,因此该部分的保护比较复杂。当 APK 签名分块的大小发生变化(例如,添加了新签名)时,偏移量也会随之改变。因此,在通过“ZIP 中央目录结尾”计算摘要时,必须将包含“ZIP 中央目录”偏移量的字段视为包含 APK 签名分块的偏移量。

验签

  1. 找到 APK 签名分块并验证以下内容:

    1. APK 签名分块的两个大小字段包含相同的值。
    2. “ZIP 中央目录结尾”紧跟在“ZIP 中央目录”记录后面。
    3. “ZIP 中央目录结尾”之后没有任何数据。
  2. 找到 APK 签名分块中的第一个 APK 签名方案 v2 分块。如果 v2 分块存在,则继续执行第 3 步。否则,回退至使用 v1 方案验证 APK。

  3. 先使用公钥验证第二个区块的签名,保证第二个区块没有被篡改,然后去计算1、3、4部分的摘要值是否与签名的摘要值一致

APK签名方案 v3

APK签名方案 v4


Android APP签名机制
https://chujian521.github.io/blog/2025/01/04/Android-APP签名机制/
作者
Encounter
发布于
2025年1月4日
许可协议