Android签名的问题

今天下载Android Studio 4.2 Canary16,本意是想了解和使用一下Jetpack Compose,但是在新特新中看到支持V3和V4签名格式。瞬间有时代脱节的感觉,要不先来了解一下签名格式的变动好了。

然后在官方文档我又看到了
Android Gradle 插件 4.2 现在支持 v3 和 v4 APK 签名格式。如需在 build 中启用这两种格式之一或同时启用这两种格式,请将以下属性添加到模块级 build.gradle 或 build.gradle.kts 文件中:

??? build.gradle.kts这是啥摸不着头脑Kotlin DSL。哎,继续了解吧。

KOTLIN
// build.gradle.kts

android {
   ...
   signingConfigs {
      config {
          ...
          enableV3Signing(true)
          enableV4Signing(true)
      }
   }
}
GROOVY
// build.gradle

android {
  ...
  signingConfigs {
    config {
        ...
        enableV3Signing true
        enableV4Signing true
    }
  }
}

Android签名的方案

  • v1方案:基于JAR签名
  • v2方案:Android 7.0加入
  • v3方案:Android 9.0加入

一、v1签名的过程

解压一个APK会在里面看见一个META-INF的文件夹。
目录结构大致如下:

xxxxx/
    |- META-INF/
    |        |- CERT.RSA
    |        |- CERT.SF
    |        |- MANIFEST.MF
    | ......

MANIFEST.MF

Manifest-Version: 1.0
Built-By: Signflinger
Created-By: Android Gradle 4.1.1

Name: AndroidManifest.xml
SHA-256-Digest: Z7PLvLjebGnXnpd9lKFoEprfDAZblEKKT5lDH3sRDL8=

Name: DebugProbesKt.bin
SHA-256-Digest: q21I1dhoxzPNAzRfiquuPZBb1vD8ZhEXqL3+psUbknQ=

Name: META-INF/androidx.activity_activity.version
SHA-256-Digest: WYVJhIUxBN9cNT4vaBoV/HkkdC+aLkaMKa8kjc5FzgM=

文件里面储存了APK中所有Name:文件的路径和SHA-256-Digest的值
SHA-256-Digest计算方式

shasum -a 256 xxx.xx

然后计算base64的值

CERT.SF

Signature-Version: 1.0
Created-By: Android Gradle 4.1.1
SHA-256-Digest-Manifest: Wkyr/WgaCc7qm2F+eEfAQkGPoFUxZk/Y8Ix5qAmZN3w=

Name: AndroidManifest.xml
SHA-256-Digest: v5I/yMuPghplj0AMllLy3ono40APuAf1p/G28Bg6QuU=

Name: DebugProbesKt.bin
SHA-256-Digest: lGNfZfenjO/ig36HyxY3o4SsvfGGgP0dK+OSY20ejcw=

Name: META-INF/androidx.activity_activity.version
SHA-256-Digest: Yu1eiqd7wti3kPabgLC0lsO+1ns/UAhiPGUExHOxH/w=

SF文件的内容和MF文件非常相似,同样包含了APK的所以文件摘要。
区别:

  • SF文件记录了整个MF文件的摘要
  • SF文件其余部分记录MF相应条目的摘要

CERT.RSA

CERT.REA里面是二进制内容,里面保存了签名的证书信息,以及对CERT.SF文件的签名。

通过下面的命令可以查看证书里面的信息

$ openssl pkcs7 -inform DER -in XXX/META-INF/CERT.RSA -text -noout -print_certs

内容如下:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1715685340 (0x664347dc)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=1, ST=1, L=1, O=1, OU=11, CN=1
        Validity
            Not Before: Apr  7 05:55:19 2020 GMT
            Not After : Apr  1 05:55:19 2045 GMT
        Subject: C=1, ST=1, L=1, O=1, OU=11, CN=1
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:9d:65:5c:da:fd:50:3c:81:b7:70:45:47:bb:94:
                    73:07:06:cc:96:76:c8:a1:86:1d:6a:ee:f9:ff:1e:
                    59:1a:8d:0f:21:e1:b0:7a:c8:c3:11:32:c3:9d:6f:
                    85:e3:51:74:35:aa:16:df:52:97:1d:a5:c7:26:6a:
                    a3:96:92:e2:89:31:76:5e:62:10:2a:46:a6:70:dd:
                    40:48:72:cb:33:70:fc:e0:de:5c:fd:59:5c:ce:bc:
                    ca:c4:8a:68:da:4d:40:ac:49:1e:21:dc:a8:51:7c:
                    56:9b:6e:ae:93:67:2c:9d:ed:ca:2d:03:6a:19:84:
                    0d:7c:3d:28:b1:bf:a3:86:38:ca:cb:18:29:2d:77:
                    bb:6e:ee:10:70:af:e7:ff:67:36:a6:3c:dc:06:63:
                    b8:27:be:7f:c0:2f:98:66:00:cd:69:5f:24:7f:24:
                    3f:d6:21:36:7b:7c:2b:57:ea:2a:4b:8d:d4:43:bb:
                    ae:c7:91:56:83:3e:f8:61:fa:e8:89:76:89:ad:ca:
                    a8:9d:c6:d9:db:89:69:5b:6f:56:3d:7f:c9:91:51:
                    ee:bf:2e:76:94:5b:a8:4e:da:99:b5:bd:00:ac:ee:
                    c0:72:02:0d:ae:14:9c:53:30:5f:cc:55:07:1c:a6:
                    eb:e3:b6:b6:dc:01:1e:8a:7e:1e:01:a1:95:bb:21:
                    02:a5
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                E1:FD:95:4D:84:C9:20:E8:BA:17:68:42:9A:F4:B9:C3:80:1F:15:7E
    Signature Algorithm: sha256WithRSAEncryption
         23:09:e0:4e:85:bb:7e:ac:9b:3e:20:21:9d:7d:85:d7:f9:6b:
         49:d3:02:cd:3e:13:af:bc:24:34:98:1f:a9:fc:22:7e:5a:dd:
         35:60:33:e6:9b:e5:58:b4:fe:cf:84:1b:50:8c:83:b0:74:8e:
         a8:44:fc:cf:20:28:20:d1:d2:76:26:d7:0f:56:29:55:34:8e:
         bd:8a:84:68:09:5c:3c:da:3f:44:f7:e0:2b:17:13:e6:72:65:
         b4:07:b2:e3:57:99:2a:e4:26:12:20:74:3c:21:b5:b6:f5:49:
         8b:9d:6d:25:0b:e3:d0:02:10:f8:33:95:3f:e7:76:87:57:57:
         30:5c:f5:1e:6b:b3:a5:2d:96:0e:1d:9a:29:2b:44:67:ae:e3:
         cf:13:7f:be:c3:e6:ec:6d:e0:3d:b3:10:d4:a3:d0:6f:c1:7b:
         17:16:5c:09:a7:e8:d0:aa:4c:dd:ec:7f:25:eb:f4:0d:bd:a6:
         0e:20:94:7f:53:ec:89:39:06:70:3b:f2:31:45:6f:d9:30:92:
         85:df:ec:9f:14:ec:d2:57:c1:45:51:f6:ec:78:71:cb:48:94:
         80:ae:3e:24:a1:6f:7a:ee:dd:1d:4c:23:72:ff:89:0a:51:93:
         7e:23:d0:b6:a6:f7:b2:82:b2:85:5d:f2:4a:c0:26:6b:3b:bb:
         60:f9:69:9d

APK校验过程简述

  • 首先校验CERT.SF文件的签名
  • 校验MINIFEST.MF文件的完整性
  • 校验APK中每个文件的完整性
  • 校验签名的一致性(app升级时)

V1签名机制的劣势

  • 签名校验速度慢

    校验过程中需要对所有文件进行摘要计算,apk资源越多机器性能越差安装速度越慢

  • 完整性保障不够

    META-INF没有计入签名校验过程中,可以随意在这个目录中添加文件(比如可以添加渠道标识)

为了解决这些问题从Android 7.0开始,Android支持了一套新的v2签名机制。

二、V2签名机制

前言:

了解v2签名的机制之前需要先简单了解一下zip文件的组成

压缩文件数据区+压缩源文件目录区+压缩源文件目录结束标志

  1. 数据区(压缩文件数据区)
    包含zip文件中所有文件的记录,一个列表,每条记录包含:文件名、压缩前后size、压缩后的数据等
  2. 中央目录(压缩源文件目录区)
    存放目录信息,一个列表,每条记录包含:文件名、压缩前后size、本地文件头的起始偏移量等。通过本地文件头的起始便宜量即可找到压缩后的数据。
  3. 中央目录结尾记录(压缩源文件目录结束标记)
    标识压缩文件的目录结尾,包含:中央目录条目数、size、起始偏移量、zip文件注释内容等。

通过压缩源文件中央目录起始偏移量可和size可以定位到中央目录,遍历文件目录条目,根据本地文件头的起始偏移量即可在数据区中找到相应的压缩数据。

2.1 V2签名原理
  • jar签名:是在apk文件中添加META-INF目录,即需要修改数据区中央目录中插入数据,添加文件后会导致中央目录大小和偏移量发生变化,还需要修改目录结束标记。
  • v2签名方案:在数据区和中央目录中间插入数据,一个APK签名分块,从而保证原始zip(apk)数据的完整性。

v2签名块负责保护数据区、中央目录、中央目录结尾记录部分的完整性,以及APK 签名方案 v2分块中的singed data分块的完整性。

APK 签名方案 v2分块是一个签名序列,包含三个内容:

  • 带长度前缀的signed data
    包含了一系列算法计算的摘要列表、证书信息,以及extra信息(可选)
  • 带长度前缀的signatures序列
    通过一系列算法对singed data的签名列表。签名时使用了多个签名算法,在签名校验时吗,选择系统支持的安全系数最大的签名进行校验。
  • 证书公钥
2.2 保证完整性摘要的计算过程

v2签名块负责保护数据区、中央目录、中央目录结尾记录部分的完整性,以及APK 签名方案 v2分块中的signed data分块的完整性

保证完整性打的具体措施:

  • 通过内容摘要的方式保护数据区、中央目录、中央目录结尾记录的完整性->保存在singed data分块中
  • 通过签名来保证signed data分块的完整性
摘要的计算过程
  • 拆封chunk
    将每个部分拆分成多个大小为1MB大小的chunk,最后的可能小于1MB。分块是为了通过并行计算摘要加快计算速度。
  • 计算chunk摘要
    计算方式:字节0xa5 + 块的长度(字节数)+ 块的内容 计算摘要
  • 计算整体摘要
    计算放肆:字节0x5a + chunk数 + 块的摘要的连接(按块在APK中的顺序)进行计算

注意:中央目录结尾记录中包含了中央目录的起始偏移量,插入APK签名分块后,中央目录的起始偏移量将发生变化。故在校验签名计算摘要时,需要把中央目录的起始偏移量当做APK签名分块的起始偏移量。

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

v2签名机制是在Android 7.0引入的,为了使用v2签名的APK可以在Android 7.0以下版本中安装,应该先用JAR签名对APK进行签名,再用v2方案进行签名。注意先用JAR签名再v2签名,因为JAR签名会修改zip文件数据区和中央目录的内容破坏v2签名的完整性。

防回滚机制
  • v2签名的APK中同时带有JAR签名,攻击者可能直接删除APK的v2签名吗,使系统只校验JAR签名。为了防止此类攻击,v2方案规定。

措施:

v2签名的APK如果还带JAR签名,META-INF/.SF文件的首部必须包含X-Android-APK-Signed属性。

[图片上传失败...(image-4d0a01-1606978403300)]

  • 攻击者还肯能试图删除APK签名方案v2分块中安全系数较高的签名,使系统验证安全系数较低的签名。

措施:

对APK进行签名时使用的签名算法ID列表会储存在signed data分块中。

v2签名的优势
  • 只需进行一次hash校验,速度快
  • 保证了APK内个文件的完整性

三、v3签名机制

3.1 简述

Android 9.0中加入了v3签名方案,新增了对APK Signature Scheme v3的支持。

使应用能够在APK更新过程中更改其签名秘钥。

为了实现轮替,APK必须指示新旧签名秘钥之间的信任级别。为了支持秘钥轮替,APK签名方案从v2更新为v3,以允许使用新旧秘钥。

v3在APK签名分块中添加了有关受支持的SDK版本和proof-of-rotation结构的信息。

官方提示:

注意:在 Android 11 发布之前,建议不要使用 APK 密钥轮替。开发者可以选择针对新安装应用使用 Google Play 的密钥升级。注册了 Play 应用签名的开发者可以通过 Play 管理中心请求升级。

轮替签名密钥

轮替签名证书世系或新签名序列的语法如下:

$ apksigner rotate --in /path/to/existing/lineage \
  --out /path/to/new/file \
  --old-signer --ks old-signer-jks \
  --new-signer --ks new-signer-jks
3.2 v3分块

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

区别:

v3方案增添了有关受支持的SDK版本和profo-of-rotation结构的信息。

Proof-of-rotation和self-trusted-certs结构

proof-of-rotation结构允许应用轮替其签名证书,而不会使这些证书在与这些应用通信的其他应用上被屏蔽。
为此应用签名需要包含两个新数据块:

  • 告知第三方应用的签名证书可信(只要其先前证书可信)的断言
  • 应用的旧签名证书(应用本身仍信任这些证书)

签名部分中的proof-of-rotation属性包含一个单链表,其中每个节点都包含用于为之前版本的应用签名的签名证书。此属性旨在包含概念性proof-of-rotation和self-trusted-old-certs数据结构。
单链表按照版本排序,最旧的签名证书对应于根节点。
在构建proof-of-rotation数据结构时,系统会让每个节点中的证书为列表中的下一个证书签名,从而为每个新秘钥提供证据来证明它应该与旧秘钥一样可信。

格式

“APK 签名方案 v2 分块”存储在“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 - Proof-of-rotation 结构
    • minSDK (uint32) - 签名数据部分中 minSDK 值的副本 - 用于在当前平台不在相应范围内时跳过对此签名的验证。必须与签名数据值匹配。
    • maxSDK (uint32) - 签名数据部分中 maxSDK 值的副本 - 用于在当前平台不在相应范围内时跳过对此签名的验证。必须与签名数据值匹配。
    • 带长度前缀的 signatures(带长度前缀)序列:
      • signature algorithm ID (uint32)
      • signed data 上带长度前缀的 signature
    • 带长度前缀的 public key(SubjectPublicKeyInfo,ASN.1 DER 形式)
v3验证

在Android 9.0及更高版本中,可以根据APK签名方案v3、v2或v1验证APK。比较旧的平台会忽略v3签名而尝试验证v2,然后尝试验证v1签名。

  1. 找到APK签名分块并验证一下内容
    a. APK签名分块的两个大小字段包含相同的值
    b. ZIP中央目录结尾紧跟在ZIP中央目录记录后面
    c. ZIP中央目录结尾之后没有任何数据
  2. 找到APK签名分块中的第一个APK 签名方案 v3分块。如果存在v3分块,则继续执行第3步。否则,回退至使用v2方案验证APK。
  3. APK签名方案 v3分块中的每个signer(minSDK和maxSDK范围内)执行以下操作:
    a. 从signatures中选择安全系数最高的受支持signature algorithm ID。安全系数取决于各个实现/平台版本。
    b. 使用public key并对照signed data验证signatures中对应。
    c. 验证签名数据中的最低和最高SDK版本是否与为signer指定的版本匹配。
    d. 验证digestssignatures中的签名算法ID列表是否相同(防止删除/添加签名。)
    e. 使用签名算法所用的同一种摘要算法计算APK内容的摘要。
    f. 验证计算出的摘要是否与digests中对应的digest一致。
    g. 验证certificates中第一个certificate的SubjectPublicKeyInfo是否与public key相同。
    h. 如果signer存在proof-of-rotation属性,验证结构是否有效,以及此singer是否为列表中的最后一个证书。
  4. 如果在当前平台范围内仅找到了一个signer,并且对该signer成功执行第3步,则验证成功。
v3签名的优势
  • v3签名支持秘钥轮替,可最大限度减少秘钥丢失的影响。
v4签名

Android 11 添加了对APK 签名方案 v4的支持。
此方案会在单独的文件(apk-name.apk.idsing)中生成一种新的签名,但在其他方面与v2和v3类似。没有对APK进行任何更改。此方案支持ADB 增量APK安装,这样会加快APK安装速度。

优势:

  • 通过APK v4签名,可以使用Android 11中的ADB 增量APK安装快速部署大型APK。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容