💡本期话题
签名有什么作用?
数字签名使用的原理
APK签名方案v1
APK签名方案v2
v2签名过程
APK签名方案v3
APK签名方案v4
数字签名v1、v2、v3、v4的区别和验签流程?
签名对打包方案有什么影响
签名过期怎么办?
💡前言
数字签名几乎每天都在用,但是你是否了解它,是否有些似是而非的理解,它有什么好处,会给你留下哪些坑?请带着以下几个问题来阅读本篇文章。
Android APK数字签名的作用是什么?
数字签名的原理了解多少?
数字签名有哪些版本,各版本区别是什么?
选择签名方案对我们的打包方案有什么影响?
签名过期怎么办?
1、防止原作者发布的应用被第三方篡改后覆盖安装。
Android通过数字签名来标识应用程序的作者和在应用程序之间建立信任关系,不是用来决定最终用户可以安装哪些应用程序。而是为了防止别人恶意发布覆盖原作者发布的应用,使用不同的签名来分辨项目。在包名相同的情况下,签名相同,才能进行覆盖安装
2、有利于程序的模块化设计和开发。
Android系统允许拥有同一个数字签名的程序运行在一个进程中,Android会将他们视为同一个程序。所以开发者可以将自己的程序分模块开发,而用户只需要在需要的时候下载适当的模块。
3、可以通过权限(permission)的方式在多个程序间共享数据和代码。
Android提供了基于数字证书的权限赋予机制,应用程序可以和与自己拥有相同数字证书的其他程序共享功能或者数据。
如果某个权限(permission)的protectionLevel是signature,则这个权限就只能授予那些跟该权限所在的包拥有同一个数字证书的程序。
数字签名使用的原理
简单描述:
计算文件hash值,然后对hash值进行签名(使用RSA算法),私钥签名,公钥验签
PS:加密防止信息被泄露 签名防止信息被篡改
总结:公钥加密、私钥解密、私钥签名、公钥验签。
Android的签名,大致的签名原理如下:
1、 对未签名的apk里面的所有文件计算Hash提取摘要,然后保存起来放到MANIFEST.MF中;
2、 然后再对MANIFEST.MF进行全文Hash和逐条计算其中每个条目的Hash,计算Hash摘要保存在CERT.SF,
3、 对CERT.SF计算Hash,然后再通过我们生成的签名文件(keystone或者jks)里面的私钥进行加密,连同公钥和签名证书并保存在CERT.RSA中。
验签时,先将apk包进行解压,然后apk中的文件计算Hash,计算方法与签名过程一样。已签名的 APK 是一种标准的已签名 JAR,其中包含的条目必须与 META-INF/MANIFEST.MF 中列出的条目完全相同,并且所有条目都必须由同一组签名者签名。其完整性按照以下方式进行验证:
1、 每个签名者均由一个包含 META-INF/
2、
3、
4、 对于每个受完整性保护的 JAR 条目,META-INF/MANIFEST.MF 都包含一个具有相应名称的部分,其中包含相应条目未压缩内容的摘要。所有这些摘要都需要验证。
5、 如果 APK 包含未在 MANIFEST.MF 中列出且不属于 JAR 签名一部分的 JAR 条目,APK 验证将会失败。
因此,保护链是每个受完整性保护的 JAR 条目的
Android 应用的签名工具有两种:jarsigner 和 apksigner。它们的签名算法没什么区别,主要是签名使用的文件不同。它们的区别如下:
jarsigner:jdk 自带的签名工具,可以对 jar 进行签名。使用 keystore 文件进行签名。生成的签名文件默认使用 keystore 的别名命名。
keystore:密钥和证书储存在一个所谓的密钥仓库(keystore)中
apksigner:Android sdk 提供的专门用于 Android 应用的签名工具。使用 pk8、x509.pem 文件进行签名。其中 pk8 是私钥文件,x509.pem 是含有公钥的文件。生成的签名文件统一使用“CERT”命名。
jks:SHA256签名指纹,pk8、x509.pem
在 v1 签名方案中,并不会保护 APK 内的所有文件,会存在一些例外部分,即便被修改也不会导致签名失效,例如:ZIP元数据。同时,v1 方案对 APK 内部被保护的原始文件,是单独进行计算数据摘要的,所以在验证时,需要先解压再验证,导致安装时会花费更多的时间,消耗更多的内存。
例如 v1 方案中签渠道的方式就是利用了此特性,将渠道信息写入 META-INF 文件中,这不会破坏 v1 签名。
v2 签名会在原先 APK 块中增加了一个新的块(签名块),新的块存储了签名、摘要、签名算法、证书链和额外属性等信息,这个块有特定的格式。最终的签名APK其实就有四块:头文件区、v2签名块、中央目录、尾部。下图是v1签名和v2签名的组成。
整个签名块的格式如下:
size of block,以字节数(不含此字段)计 (uint64)
带 uint64 长度前缀的“ID-值”对序列:
size of block,以字节数计 - 与第一个字段相同 (uint64)
magic“APK 签名分块 42”(16 个字节)
在多个“ID-值”对中,APK签名信息的 ID 为 0x7109871a,包含的内容如下:带长度前缀的 signer:
带长度前缀的 signed data,包含digests序列,X.509 certificates 序列,additional attributes序列
带长度前缀的 signatures(带长度前缀)序列
带长度前缀的 public key(SubjectPublicKeyInfo,ASN.1 DER 形式)
value可能会包含多个 signer,因为Android允许多个签名
总结:一个签名块,可以包含多个ID-VALUE,APK的签名信息会存放在 ID 为 0x7109871a的键值对里。他的内容可以包含多个签名者的签名信息,每个签名信息下包含signed data、signatures、public key,其中,signed data主要存放摘要序列、证书链、额外属性,signatures包含多个签名算法计算出来的签名值,public key表示签名者公钥,用于校验的时候验证签名的。
v2签名过程
首先,说一下 APK 摘要计算规则,对于每个摘要算法,计算结果如下:
将 APK 文件 ZIP头文件内容、ZIP中央目录、ZIP中央目录结尾按照 1MB 大小分割成一些小块。
计算每个小块的数据摘要,数据内容是 0xa5 + 块字节长度 + 块内容。
计算整体的数据摘要,数据内容是 0x5a + 数据块的数量 + 每个数据块的摘要内容
总之,就是把 APK 按照 1M 大小分割,分别计算这些分段的摘要,最后把这些分段的摘要进行计算,得到最终的摘要也就是 APK 的摘要。然后将 APK 的摘要 + 数字证书 + 其他属性生成签名数据写入到 APK Signing Block 区块。
所以,v2 签名将验证归档中的所有字节,而不是单个包内文件,因此,在签署后无法再运行 zipalign(必须在签名之前执行)。Android Studio打包将压缩、对齐和签名合并成一步完成。
总结:v2 签名提供了更强大的 APK 文件验证,它不再检查包内单个文件,而是检查整个 APK。它在 APK 文件中,插入一个额外的签名块,覆盖 APK 文件中的其余部分,更安全更快。
从安全的角度, v2 会比 v1 更安全,对 APK 文件做“任何”改动都会破坏签名。注意这里的“任何”是带引号的,v2 签名的签名块其实是一个 K-V 的结构,可以向其中插入一些简单的数据而不破坏 v2 签名,这就是 v2 方案下,多渠道的方案思路。
用了v2签名,也不是万事大吉了。在实际情况下,还可能会出现以下一些问题。
1、公司被收购,应用转移到其他主体怎么办?
2、v2或者v1的默认有效期是25年,签名过期了怎么办?
3、签名被泄露了怎么办?
这些都需要更改签名,但更改后,如何保证旧版本安装包能被新签名的安装包正常覆盖。一起看看v3签名方案。
APK签名方案v3
Android 9.0 中引入了新的签名方式-APK签名方案v3,它的格式大体和 v2 类似,在 v2 插入的签名块(Apk Signature Block v2)中,又添加了一个新块(Attr块)。
在这个新块中,会记录我们之前的签名信息以及新的签名信息,以密钥转轮的方案,来做签名的替换和升级。这意味着,只要旧签名证书在手,我们就可以通过它在新的 APK 文件中,更改签名。
v3 签名新增的新块(attr)存储了所有的签名信息,由更小的 Level 块,以链表的形式存储。
其中每个节点都包含用于之前版本应用签名的签名证书,最旧的签名证书对应根节点,系统会让每个节点中的证书为列表中下一个证书签名,从而为每个新密钥提供证据来证明它应该像旧密钥一样可信。
这个过程有点类似 CA 证书的证明过程,已安装的 App 的旧签名与根节点校验,确保覆盖安装的 APK 的新签名正确,将信任传递下去。
该架构提供的选择可以在其签名块中为每个签名证书加入一条轮转证据记录。利用此功能,应用可以通过将 APK 文件过去的签名证书链接到现在签署应用时使用的证书,从而使用新签名证书来签署应用。
轮转签名语法和命令
$ apksigner rotate --in /path/to/existing/lineage \
--out /path/to/new/file \
--old-signer --ks old-signer-jks \
--new-signer --ks new-signer-jks
解决完签名更换问题,还需要对安装速度进一步优化。因此谷歌推出v4签名方案。
APK签名方案v4
Android 11 添加了对 APK 签名方案 v4 的支持。此方案会在单独的文件 (apk-name.apk.idsig) 中生成一种新的签名,所以APK v4的签名文件.apk.idsig并不会打包进apk文件中。
v4在其他方面与 v2 和 v3 类似。没有对 APK 进行任何更改。此方案支持 ADB 增量 APK 安装,从而加快 APK 安装速度。
在传统的应用安装方案中,开发者通过ADB(Android Debug Bridge)以有线或无线的方式与终端用户连接,或者用户从软件商店直接下载,然而该方案需要用户等待完整的安装包传输结束后才能启动安装,在这期间产生了不良的用户体验。
增量安装技术是一种流式的安装方案:一旦安装包的核心文件传输完成便可启动应用。流式安装意味着允许优先传输核心数据以启动应用,并在后台流式传输剩余数据。
对于APK而言,其核心数据包括可执行文件和重要的资源文件等。在数据传输开始前,ADB筛选出安装包的核心文件优先传输,一旦移动设备接收到启动应用所需的核心数据块后,该应用程序便可在虚拟文件系统上启动。
v4的签名方式:
1、我们首先将APK的源数据划分为多个4KB的数据块,如果源文件最后部分不足4KB,则进行零填充来凑足4KB;
2、然后对这些4KB的数据块进行SHA256计算得到32B的哈希值,这部分哈希值就组成了Merkle树的第一层。
3、对于Merkle树的第二层,需要将第一层的进行组合,组合方式是依次将第一层的128个哈希值组合成4KB的数据块,如不足4KB则进行零填充,最后对这些4KB块进行SHA256计算得到Merkle树的第二层。
4、后面的部分以此类推,直到计算出Merkle数的根哈希。生成的哈希树以V4Signature的结构保存在.idsig文件内。
由根哈希计算出Hashinfo结构,根据Hashinfo和APK摘要生成V4签名。
当ADB请求增量安装时,PMS从.idsig文件中获取原生签名封装在V4Signature对象中。进行验证时,则从V4Signature获得签名数据和公钥,验证的过程与上一代签名方案类似。
这里签名用到Merkle树。Merkle树的作用:
a、快速比较大量数据;b、快速定位修改。
当两个Merkle树的根哈希值相同时,说明所代表的的数据都相同。如果某个节点不同,那么只需要比较该节点以及其子节点,以此类推。因此快速找到修改过的数据块,得以实现快速签名和增量安装。
数字签名v1、v2、v3、v4的区别和验签流程?
Android 的签名方案,发展到现在,不是一蹴而就的。Android 现在已经支持四种应用签名方案:
v1 方案:基于 JAR 签名。防止APK被篡改,支持基于同签名的模块开发和权限管理。
v2 方案:APK 签名方案 v2,在 Android 7.0 引入。颠覆性的签名方式,解决安全和安装速度问题;
v3 方案:APK 签名方案 v3,在 Android 9.0 引入。与v2的结构基本相同,是v2的升级版,使用密钥转轮的方案,实现签名的替换和升级。解决签名不可更改的问题,保证签名的可过渡性。
v4 方案:APK 签名方案 v4,在 Android 11.0 引入。签名方式变化不大,支持使用流式安装方案,进行增量安装。提升ADB安装速度,提升安装体验。
因为签名方案的升级,做到向下/向后兼容,所以只要使用得当,这个过程对开发者是友好的。使用各方案的签名,不需要开发者做额外的工作,只需要使用新版签名方案时,也要同时使用旧版本,就能兼容安卓各个版本。
签名对打包方案有什么影响
1、Android studio4.2后,签名时,默认选择同时签v1和v2,无法在界面中选择签名版本。如果需要修改签名方式,可以在gradle用下边的代码
v1SigningEnabled true
v2SigningEnabled true
2、目前Android不支持v3版本的签名,所以在AS里面看不到v3。但是在SDK中有个签名工具apksigner.jar。只有9.0以上签名工具才能签v3版本的签名。
3、采用v1方案时,可以将渠道信息写入 META-INF 文件中,v2和v3则可以在签名块里插入数据。v2 签名的签名块其实是一个 K-V 的结构,可以向其中插入一些简单的数据而不破坏 v2 签名,这就是 v2 方案下,多渠道的方案思路。
签名过期怎么办?
Android 签名是自证明的,并不会对证书进行 CA 认证。也就是我们可以使用工具自行生成签名证书,只要是一个正确的签名,系统就会承认,并且允许安装。
生成签名的时,可以指定一个有效时间,这个时间默认为 25 年,并且 Google Play 也有硬性规定,上架的 App 签名有效期必须在 2033-10-22 日期之后。所以只要不是有意修改了这个有效期,在当下这个时刻,是不会有问题,毕竟到现在还没有一款 App 存在 25 年。
有些问题不在眼前,却是真实存在的。对于一款上架的 App,最重要的就是用户,而当签名失效之后,我们只能被迫换签名,此时因为签名校验无法通过,就会导致旧用户无法覆盖安装。这些历史用户唯一的选择,就是卸载后重新安装。
解决方案:过期前,首先对旧的签名进行轮转,然后使用v3方案的新签名对apk进行签名。
参考资料:
APK 签名方案 v2 | Android 开源项目 | Android Open Source Project (google.cn)
APK 签名方案 v3 | Android 开源项目 | Android Open Source Project (google.cn)
apksigner | Android 开发者 | Android Developers
apksigner | Android 开发者 | Android Developers (google.cn)