我们知道,一款Android 要发布的话,必须经过签名,Android目前支持的签名方式包括三种:
为了最大限度地提高兼容性,请按照 v1、v2、v3 的先后顺序采用所有方案对应用进行签名。与只通过 v1 方案签名的应用相比,还通过 v2+ 方案签名的应用能够更快速地安装到 Android 7.0 及更高版本的设备上。更低版本的 Android 平台会忽略 v2+ 签名,这就需要应用包含 v1 签名。
在v1方案中,签名只保护apk中的元数据,也就是单个文件。apk其实就是一个zip文件,我们将打包签名好的apk文件,用解压缩文件解压,就可以看到一个名称为META-INF的文件夹里面。
在META-INF文件夹中,存在3个文件,MANIFEST.MF , CERT.SF,CERT.RSA。这些就是v1版本的apk在安装时候,进行签名校验 很重要的文件。
MANIFEST.MF
这个文件中保存着apk解压之后,所有文件(非文件夹)的信息,看下面的一段内容:
Manifest-Version: 1.0
Built-By: Generated-by-ADT
Created-By: Android Gradle 3.2.0
Name: AndroidManifest.xml
SHA1-Digest: g+psNXzyZSKU8DMfQr8FdR0KjXI=
Name: LICENSES
SHA1-Digest: +7g2kf8cIIZknpabOMYpXi8vrK8=
Name: META-INF/android.arch.core_runtime.version
SHA1-Digest: OxxKFJcpzAROGjnfMbNijNv1+JU=
Name: META-INF/android.arch.lifecycle_extensions.version
SHA1-Digest: BeF7ZGqBckDCBhhvlPj0xwl01dw=
在每个文件块中,其中 name就是解压之后,文件的名称(包含路径),而SHA1-Digets就是对文件的内容提取摘要之后 进行Base64之后生成的字符串内容。
CERT.SF
先看一段CERT.SF的内容:
Signature-Version: 1.0
Created-By: 1.0 (Android)
SHA1-Digest-Manifest: 00yHda1FOBSkUAwjlTfipwkY+0k=
X-Android-APK-Signed: 2
Name: AndroidManifest.xml
SHA1-Digest: BNl/B2KAFMNJ1keEnqQ5+vkw8mY=
Name: LICENSES
SHA1-Digest: 9irOepAoIoOn8huusmI9KxjOT6c=
Name: META-INF/android.arch.core_runtime.version
SHA1-Digest: gfKdTxzy15iV0z7ZogHp7+qdek8=
SHA1-Digest-Manifest :是对整个MANIFEST.MF使用 SHA1算法之后,生成的Base64的编码的字符串。
Name:也是对应解压之后的文件的名称
SHA1-Digest:是对MANIFEST.MF中对应名称(Name)做SHA1之后生成再生成的Base64编码的字符串
CERT.RSA
这里会把之前生成的CERT.SF文件,用我们私钥计算出签名,然后将签名以及包含公钥信息的数字证书一同写入CERT.RSA中保存。
在mac电脑中,我们可以在终端中使用以下命令查看CERT.SF的内容:
openssl pkcs7 -inform DER -in CERT.RSA -text -noout -print_certs
其中CERT.RSA 表示的是文件CERT.RSA ,如果不是在当前文件夹中,应该填写CERT.RSA 的相对路径或者绝对路径。
可以用一张图来解释签名的过程:
安装校验过程。
我们从激活成功教程 sign.apk的过程来讲解校验的过程。首先由于v1是针对单一文件进行提取摘要的方式进行校验。
1.假如,我们破坏或者修改了apk中的某一个文件,那么我们必须修改MANIFEST.MF中的对应文件的摘要值,才能通过sdk对MANIFEST.MF的校验。
2.就算我们修改某个文件之后,并且修改了对应的MANIFEST.MF里面的摘要之后,由于CERT.SF中保存的是未修改之前的MANIFEST.MF中每个条目的SHA1之后的Base64编码值,因此前后比对,不一样。第二步对CERT.SF的校验,也不会通过。只有我们在修改MANIFEST.MF 摘要的同时,也修改CERT.SF对应条目的摘要值,才能通过第二步对CERT.SF的检测。
3前面两步通过伪装修改的方式对MANIFEST.MF和CERT.SF的校验都通过了的话,那么第三步CERT.RSA 的校验,就不可能伪装了,因为CERT.RSA 使用的是开发者的私钥信息进行的签名。用户无法获得其私钥信息,没法伪装。
既然上面的签名校验方式,不允许我们对apk解压之后的某个文件的修改。 我们就可以不破坏这种校验机制增加文件,来记录apk包的渠道信息。或者利用zip的文件格式来做文章存储我们的渠道信息。市面上有2种对v1签名生成多渠道包的方式。
方式1:们可以在不改变原来apk中任何单个文件的情况下,通过对apk增加文件的方式来记录相关的渠道信息。实现多渠道打包。
我们可以在META-INF,或者apk包解压的任何文件夹位置添加一个我们自己的文件,文件名称如上面channel_xiaomi.txt,以文件名称作为渠道名,然后再重新生成apk的 zip文件。因为上面的MANIFEST.MF ,CERT.SF ,CERT.RSA是对apk解压之后的每个单独文件进行校验,但是我们增减的channel_xiaomi.txt是在生成渠道包之后,重新加入进去的,所以android系统安装apk的时候,我们增加的channel_xiaomi.txt并不在我们android系统校验的范围内,我们就可以在app内,读取这个文件的名称来获取对应的渠道。这里主要耗费的时间是对apk解压之后生成channel_xxx.txt的空文件,然后重新打包成.apk文件。
方式二:利用zip文件的文件格式,我们可以在文件最后的部分的comment(注释)中加入自己的渠道信息,然后再app中去读去zip文件的comment里面的内容,读去到对应的渠道号。这里主要是对zip文件的操作。
Android 7.0(Nougat)引入一项新的应用签名方案APK Signature Scheme v2,它是一个对全文件进行签名的方案,能提供更快的应用安装、对未授权APK文件的更改提供更多保护,在默认情况下,Android Gradle 2.2.0插件会使用APK Signature Scheme v2和传统签名方案来签署你的应用。
目前该方案不是强制性的,在 build.gradle
添加 v2SigningEnabled false
,就能使用传统签名方案来签署我们的应用(见下面的代码片段)
android {
...
defaultConfig { ... }
signingConfigs {
release {
storeFile file("xxx.keystore")
storePassword "password"
keyAlias "MyReleaseKey"
keyPassword "password"
v2SigningEnabled false
}
}
}
APK 签名方案 v2 是一种全文件签名方案,该方案能够发现对 APK 的受保护部分进行的所有更改,从而有助于加快验证速度并增强完整性保护。使用 APK 签名方案 v2 进行签名时,会在 APK 文件中插入一个 APK签名分块,该分块位于“ZIP 中央目录”部分之前并紧邻该部分。在“APK 签名分块”内,v2 签名和签名者身份信息会存储在 APK 签名方案 v2 分块中。
新的签名方案会在ZIP文件格式的 Central Directory 区块所在文件位置的前面添加一个APK Signing Block区块。整个APK(ZIP文件格式)会被分为以下四个区块: 1. Contents of ZIP entries(from offset 0 until the start of APK Signing Block) 2. APK Signing Block 3. ZIP Central Directory 4. ZIP End of Central Directory。
之前的渠道包生成方案是通过在META-INF目录下添加空文件,用空文件的名称来作为渠道的唯一标识,之前在META-INF下添加文件是不需要重新签名应用的,这样会节省不少打包的时间,从而提高打渠道包的速度。但在新的应用签名方案下META-INF已经被列入了保护区了,向META-INF添加空文件的方案会对区块1、3、4都会有影响,v2签名方案签署的应用经过我们旧的生成渠道包方案处理后,在安装时会报以下错误:
Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES: Failed to collect certificates from base.apk: META-INF/CERT.SF indicates base.apk is signed using APK Signature Scheme v2, but no such signature was found. Signature stripped?]
目前V1签名另外一种比较流行的往APK中添加ZIP Comment,生成多渠道包的方案,也因为上述原因,无法在新的应用签名方案下进行正常工作。
既然v1签名生成多渠道包的方式对于v2不能使用,那么就需要找另外的出路了。
既然v2签名保护1,3,4块数据区,不能对1,3,4区快的数据做修改,那么就可以在2 数据区 Apk Signing block做修改,在这块数据加入我们的渠道信息。
为了保持与 v1 APK 格式向后兼容,v2 及更高版本的 APK 签名会存储在“APK 签名分块”内,该分块是为了支持 APK 签名方案 v2 而引入的一个新容器。在 APK 文件中,“APK 签名分块”位于“ZIP 中央目录”(位于文件末尾)之前并紧邻该部分。
该分块包含多个“ID-值”对,所采用的封装方式有助于更轻松地在 APK 中找到该分块。APK 的 v2 签名会存储为一个“ID-值”对,其中 ID 为 0x7109871a。
数据 | 字节数 | 描述 |
---|---|---|
size of blcok | 8个字节 | 除此字段外 block的总长度 |
id-value-pairs | 8个字节 | 此id与value 数据的总长度 |
id | 4个字节 | id数据 |
value | n个字节 | value数据 |
… | ||
size of block | 8个字节 | 与第一个字段相同 |
magic | 16个字节 | 魔数,标记block的数据格式 |
在解析 APK 时,首先要通过以下方法找到“ZIP 中央目录”的起始位置:在文件末尾找到“ZIP 中央目录结尾”记录,然后从该记录中读取“中央目录”的起始偏移量。通过 magic
值,可以快速确定“中央目录”前方可能是“APK 签名分块”。然后,通过 size of block
值,可以高效地找到该分块在文件中的起始位置。在解译该分块时,应忽略 ID 未知的“ID-值”对.
在 Android 7.0 及更高版本中,可以根据 APK 签名方案 v2+ 或 JAR 签名(v1 方案)验证 APK。更低版本的平台会忽略 v2 签名,仅验证 v1 签名。
通过上图可以看出新的应用签名方案的验证过程:
1. 寻找APK Signing Block,如果能够找到,则进行验证,验证成功则继续进行安装,如果失败了则终止安装
2. 如果未找到APK Signing Block,则执行原来的签名验证机制,也是验证成功则继续进行安装,如果失败了则终止安装。
在参考文章中,zip的文件格式中我们可以知道End of Central Directory的这块的数据格式,如下:
Offset | Bytes | Description | 译 |
---|---|---|---|
0 | 4 | End of central directory signature = 0x06054b50 | 核心目录结束标记,固定为0x06054b50 |
4 | 2 | Number of this disk | 当前的磁盘编号 |
6 | 2 | Disk where central directory starts | 核心目录开始的磁盘编号 |
8 | 2 | Number of central directory records on this disk | 磁盘上所记录的核心目录数量 |
10 | 2 | Total number of central directory records | 核心目录的总数 |
12 | 4 | Size of central directory (bytes) | 核心目录的大小 |
16 | 4 | Offset of start of central directory, relative to start of archive | 核心目录开始位置相对于archive的偏移 |
20 | 2 | Comment length (n) | 注释的长度 |
22 | n | Comment | 注释的内容 |
知道apk的文件结构,这样可以从apk(实质就是zip文件) EOCD,反推出Central Directory的位置,最后确定是否有APK signing Block,然后确认是否是v2签名,同时我们也可以在APK signing Block这块内容增加一个固定的id(不与ID 0x7109871a重复就行
)-value对,来存放我们apk的channel信息,这样就不会破坏整个apk的签名信息,又可以增加我们的渠道信息。
在使用美团打包walle的时候,如果你的build-tools 版本较高的话,可能打出来的包,无法在Android P 上安装。因为 android p 需要 apksigningblock 的长度确保为 4096 的倍数。具体的解决方案在这里。
Android 9 支持 APK秘钥轮替,这使应用能够在 APK 更新过程中更改其签名密钥。为了实现轮替,APK 必须指示新旧签名密钥之间的信任级别。为了支持密钥轮替,google将 APK签名从 v2 更新为 v3,以允许使用新旧密钥。v3 在 APK 签名分块中添加了有关受支持的 SDK 版本和 proof-of-rotation 结构的信息。
参考文章:
APK 签名方案 v2 | Android 开源项目 | Android Open Source Project
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/219073.html原文链接:https://javaforall.cn