前言
书接上回【Android签名机制详解】一:密码学入门,在了解了消息摘要、非对称加密、数字签名、数字证书的基本概念后,我们趁热打铁、直奔主题,讲解签名在Android中的实际应用。
基础知识
Android的数字证书(.der 或 .pem )是存放在密钥库文件(.jks 或 .keystore)当中的,而数字证书作用就是保管公钥。除此之外,密钥库中还存有私钥。
Android的数字证书和传统数字证书有很大的不同,它是根证书,也就是自认证的证书,颁发者和使用人是同一个,也就是说我们自己即是CA机构,又是证书使用者。所以,Android系统在安装APK的过程中并不会校验证书的合法性,只是从中提取公钥和算法。Google这样做的目的是为了降低开发成本,吸引更多的人来开发Android应用,巩固Android生态圈。毕竟正儿八经的证书申请是要花钱的。
但是,这也就会导致一个很严重的问题,就是Android证书没有权威性
,任何人都可以制作。攻击者可以篡改APK里的内容,替换掉原来的签名和证书,重新生成一份自己的签名和证书(所谓的重签名
),再二次发行,谋取利益。那怎么办呢?
其实应用安全是每个Android开发者都应该关心的。大公司会有专门的部门对APP的进行安全加固,小公司或个人也可以购买第三方的加固服务。
我们也可以在APK里面添加校验签名的逻辑,一旦校验失败就停止运行,像支付宝就有这种防止重签名的机制,但道高一尺魔高一丈,Android加固
和Android逆向
了解一下。
言归正传,我们继续讲解Android的签名方案。
目前 Android 支持以下四种应用签名方案:
- v1签名:基于 JAR 签名。
- v2签名:Android 7.0 引入,改动大。
- v3签名:Android 9.0 引入,基于 v2 的优化。
- v4签名:Android 11.0 引入,用来支持 ADB 增量 APK 安装。
V1签名方式也称作Jar签名,顾名思义,Jar 包也是用这种方式进行签名检验的。 V1签名并不是Android独有的签名方式。直到Android 7.0开始才推出V2签名,这个就是Android独创的签名方案,与V1签名方式截然不同,完全颠覆以前签名方案。后面Android 9.0又推出了V3签名,再到Android 11推出了V4签名方案,都是在V2的签名架构进行优化升级。所以我们可以把V2、v3、v3划分为一类,称为V2+方案。
V1签名方案
我们对含有V1签名的APK进行解压,部分压缩软件支持直接解压,有些需要修改APK后缀为.zip进行解压。解压后有一个MATE-INF目录,进入后会看到三个文件,分别是 MANIFEST.MF、*.SF 和 *.RSA 。因为*.SF 和*.RSA 具体的文件名是由签名时上下文环境(参数)决定的,所以这边用通配符*代替。
文件分析
MANIFEST.MF文件
我们先看MANIFEST.MF文件,这里简称MF文件。
Manifest-Version: 1.0
Created-By: 1.8.0_92 (Oracle Corporation)
Name: res/drawable-hdpi-v4/abc_list_longpressed_holo.9.png
SHA1-Digest: KQunCQh0E4bP0utgN0cHdQr9OwA=
Name: res/drawable-xxhdpi-v4/abc_ic_star_half_black_16dp.png
SHA1-Digest: EikVyBT5I7pmbJO2k8qF0V5hUc0=
......
这里面保存了除META-INF目录外的所有文件对应的摘要信息。在签名过程中, jarsigner
(签名工具)首先会遍历APK中所有文件, 采用SHA1(JDK7.0之后采用SHA256)算法计算每个文件的16进制的消息摘要,再用 base64 进行编码,将值写入MF文件的一块中。该块有一个“Name”属性,其值就是该文件在APK包中的路径。我们将Name和其对应的摘要称为一个Entry。
*.SF文件
SF文件的主要作用是对MF做校验,防止MF的数据被篡改。
Signature-Version: 1.0
SHA1-Digest-Manifest: odZIAbrTVCfKGy6HEd5+gdBHw0I= //整个MF文件的消息摘要
Created-By: 1.8.0_92 (Oracle Corporation)
Name: res/drawable-hdpi-v4/abc_list_longpressed_holo.9.png
SHA1-Digest: xcQ0bHWRc+R9tuxQ3wgY1a2eY0k=
Name: res/drawable-xxhdpi-v4/abc_ic_star_half_black_16dp.png
SHA1-Digest: pj+V2r2pJOgJwGGNpeqxnykl0Nc=
......
这里保存了MF整个文件的消息摘要+MF中每个Entry的二次消息摘要。在签名过程中, jarsigner
首先计算MF文件的整体SHA1值,再经过base64 编码后,记录在CERT.SF主属性块(在文件头上)的“SHA1-Digest-Manifest”属性值中;
再逐条计算MF文件中每一个Entry的SHA1,并经过BASE64编码后,记录在CERT.SF中每个Entry中。
*.RSA文件
首先计算CERT.SF文件消息摘要,用私钥计算出签名,然后将签名以及包含公钥信息的数字证书一同写入 CERT.RSA 中,以二进制形式保存。
验签
- 校验SF文件的签名:计算*.SF文件的摘要,然后用*.RSA文件中公钥解密*.RSA中签名得到的摘要,如果两者一致则进入下一步;
- 校验MANIFEST.MF文件的完整性:计算MANIFEST.MF文件的摘要,与*.SF主属性中的摘要进行对比,如一致则逐一校验MANIFEST.MF文件各个条目的完整性;
- 校验APK中每个文件的完整性:逐一计算APK中每个文件(META-INF目录除外)的摘要,与MANIFEST.M中的记录进行对比,如全部一致,刚校验通过;
- 校验签名的一致性:如果是升级安装,还需校验证书签名是否与已安装APP一致,如果应用中存在相同包名但签名不一样的APP,则安装失败;
存在的问题
- 签名和验签速度慢:因为要对APK做每个文件进行签名和验签的过程,导致构建和安装时间变长。
-
完整性保障不够:META-INF目录用来存放签名,自然不计入签名校验过程的。所以可以随意在这个目录中添加文件,比如早期的一些批量打包方案就选择在这个目录里做文章,在这里标记文件,来标识每一个APK所属于的渠道;
为了解决这两个问题,Google在Android 7.0推出了V2签名方案。
V2签名方案
V2签名完全颠覆了V1签名的过程,是一款全新的签名方案。我们知道,APK实际上是一个ZIP格式的压缩包,而ZIP结构如下图所示。
ZIP文件结构
ZIP 文件结构分为三大部分:
- 数据区:存放真正数据的地方。
- 中央目录区:存放关于数据区的元数据的地方。
- 中央目录结尾区:存放关于中央目录区的元数据的地方。
这边咱们只是简单了解关于ZIP文件结构,更加详细内容,可以参考这篇文章:[Zip文件格式详解]。
而V2签名过程,就是在对这数据区、中央目录区和中央目录结尾区,这3块块区域进行切分,切分成一个个大小为1M
的小块,然后并行计算
每个小块的消息摘要,将所有小块的消息摘要拼接在一起,整体计算消息摘要并用私钥进行签名,最后将签名、公钥、证书、算法、签名者信息等相关内容作为一个新的区域,插入到ZIP结构中,该区域位于“中央目录区”部分之前并紧邻该区域。而新插入的区域我们称之为 APK 签名分块(APK Signing Block)
。如下图所示。
APK签名分块解析
APK签名分块由四部分组成,分别是size of block
、ID-VALUE序列
、size of block
、magic
。其中两个size of block
相同,记录着ID-VALUE序列
的长度,ID-VALUE序列
可以理解是一个键值对序列。而magic
里面记录着一个固定值0x7109871a,该值为ID-VALUE序列
的Key值,而其对应的Value值存储一份或者多份APK签名信息,我们称每份签名信息为“ APK 签名方案 v2 分块
”。而ID-VALUE序列
的其他键值对,目前暂未使用。
APK 签名方案 v2 分块
APK 签名方案 v2 分块
也不止一个,多个签名者可以对同一个APK进行签名。每个签名信息
都会形成一个APK 签名方案 v2 分块
。而APK 签名方案 v2 分块
的内部结构又可以细分,由三部分组成。
- 带长度前缀的signed data:使用多种算法对APK进行消息摘要,将摘要值、证书信息,以及额外信息记录在此处。
-
带长度前缀的signatures序列:使用多种算法对
signed data
进行签名,签名算法和结果记录在这里,形成一个signatures序列。 -
证书公钥:存放该签名者的公钥。
计算APK摘要
这个张图咱们从下往上看,V2计算APK摘要时,首先将ZIP中数据区、中央目录区和中央目录结尾区这3块区域拆分成一个个大小为 1MB的小块。每个区域的最后一个小块可能会小于1M。然后并行计算
每个分块的消息摘要,将所有小块的消息摘要拼接在一起,整体计算消息摘要。那么最终值就是整个APK的消息摘要。
验签过程
V2验证过程着实有点复杂,我们直接引用官方文档中的流程描述。
待我有空再用白话翻译一边。这里不得不吐槽一下。部分官方文档写太TM晦涩难懂了,缺乏承上启下的过渡,突然蹦跶出来一堆专有名词,又缺乏通俗易懂的解释,跟看个文言文一样。不过,我有个学习技巧,就是先看前辈们咀嚼后吐露出来的由浅入深的文章,吸收理解七七八八,再去观摩官方文档的磅礴大气。官方文档还是要肯的,毕竟内容都是最新最专业的。
- 找到APK 签名分块并验证以下内容:
a. APK 签名分块的两个大小字段包含相同的值。
b. ZIP 中央目录结尾紧跟在ZIP 中央目录记录后面。
c. ZIP 中央目录结尾之后没有任何数据。 - 找到APK 签名分块中的第一个APK 签名方案 v2 分块。如果 v2 分块存在,则继续执行第 3 步。否则,回退至使用 v1 方案验证 APK。
- 对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 相同。 - 如果找到了至少一个 signer,并且对于每个找到的 signer,第 3 步都取得了成功,APK 验证将会成功。
注意事项
- V2签名是在Android 7.0中引入的。为了使 APK 可在 Android 6.0及更低版本的设备上安装,应先对APK进行V1签名,然后再进行V2签名。
- 在Android 7.0及更高版本中,可以根据 APK 签名方案 v2+ 或 JAR 签名(v1 方案)验证 APK。更低版本的平台会忽略 v2 签名,仅验证 v1 签名。
V3签名方案
V3 签名方案是Android 9.0时引入的签名解决方案,在V2的基础上进行了小的优化升级,其中最大的特色“支持密钥轮替
”。
在以前,如果准备安装的APK与手机里已经安装的APK包名相同,但签名不一样,是无法安装的,会提示“已安装了存在签名冲突的同名数据包”,然后安装失败”。但如果使用V3签名方案,就能安装成功。
具体实现细节和限制,详见官方文档。下面贴一张签名校验的流程图。
V4签名方案
V4签名方案是Android 11.0引入,其目的是为了支持ADB增量APK安装
。
在传统的应用安装方案中,用户需要
等待整个APK下载完毕后才能启动安装
。而增量安装技术是一种流式的安装方案:一旦APK的核心文件传输完成便可启动应用
,但只能使用应用的基本功能,应用的后续功能会在后台流式传输。
在Android 11中,Google在内核中实现了增量文件系统用于对增量安装的支持。这使得 Android OS可以通过ADB流式传输APK。同时,Android 11为了适应增量安装,添加了新的v4签名方案。文章来源:https://www.toymoban.com/news/detail-657208.html
具体实现细节和限制,详见官方文档。下面贴一张签名校验的流程图。
文章来源地址https://www.toymoban.com/news/detail-657208.html
实际应用
到了这里,关于【Android签名机制详解】二:Android V1、V2、V3、V4签名方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!