Android APK 签名、打包笔记

 我们知道,一款Android 要发布的话,必须经过签名,Android目前支持的签名方式包括三种:

为了最大限度地提高兼容性,请按照 v1、v2、v3 的先后顺序采用所有方案对应用进行签名。与只通过 v1 方案签名的应用相比,还通过 v2+ 方案签名的应用能够更快速地安装到 Android 7.0 及更高版本的设备上。更低版本的 Android 平台会忽略 v2+ 签名,这就需要应用包含 v1 签名。

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 使用的是开发者的私钥信息进行的签名。用户无法获得其私钥信息,没法伪装。

v1版本签名下的多渠道包实现   

    既然上面的签名校验方式,不允许我们对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文件的操作。

V2签名方案

         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 分块中。

签名前和签名后的 APK

新的签名方案会在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做修改,在这块数据加入我们的渠道信息。

APK 签名分块

为了保持与 v1 APK 格式向后兼容,v2 及更高版本的 APK 签名会存储在“APK 签名分块”内,该分块是为了支持 APK 签名方案 v2 而引入的一个新容器。在 APK 文件中,“APK 签名分块”位于“ZIP 中央目录”(位于文件末尾)之前并紧邻该部分。

该分块包含多个“ID-值”对,所采用的封装方式有助于更轻松地在 APK 中找到该分块。APK 的 v2 签名会存储为一个“ID-值”对,其中 ID 为 0x7109871a

数据字节数描述
size of blcok8个字节除此字段外 block的总长度
id-value-pairs8个字节此id与value 数据的总长度
id4个字节id数据
valuen个字节value数据
...
size of  block8个字节与第一个字段相同
magic16个字节魔数,标记block的数据格式

在解析 APK 时,首先要通过以下方法找到“ZIP 中央目录”的起始位置:在文件末尾找到“ZIP 中央目录结尾”记录,然后从该记录中读取“中央目录”的起始偏移量。通过 magic 值,可以快速确定“中央目录”前方可能是“APK 签名分块”。然后,通过 size of block 值,可以高效地找到该分块在文件中的起始位置。在解译该分块时,应忽略 ID 未知的“ID-值”对.

验证

在 Android 7.0 及更高版本中,可以根据 APK 签名方案 v2+ 或 JAR 签名(v1 方案)验证 APK。更低版本的平台会忽略 v2 签名,仅验证 v1 签名。

APK 签名验证过程

通过上图可以看出新的应用签名方案的验证过程:

1. 寻找APK Signing Block,如果能够找到,则进行验证,验证成功则继续进行安装,如果失败了则终止安装

2. 如果未找到APK Signing Block,则执行原来的签名验证机制,也是验证成功则继续进行安装,如果失败了则终止安装。

在参考文章中,zip的文件格式中我们可以知道End of Central Directory的这块的数据格式,如下:

OffsetBytesDescription
04End of central directory signature = 0x06054b50核心目录结束标记,固定为0x06054b50
42Number of this disk当前的磁盘编号
62Disk where central directory starts核心目录开始的磁盘编号
82Number of central directory records on this disk磁盘上所记录的核心目录数量
102Total number of central directory records核心目录的总数
124Size of central directory (bytes)核心目录的大小
164Offset of start of central directory, relative to start of archive核心目录开始位置相对于archive的偏移
202Comment length (n)注释的长度
22nComment注释的内容

知道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 的倍数。具体的解决方案在这里

V3签名方案

Android 9 支持 APK秘钥轮替,这使应用能够在 APK 更新过程中更改其签名密钥。为了实现轮替,APK 必须指示新旧签名密钥之间的信任级别。为了支持密钥轮替,google将 APK签名从 v2 更新为 v3,以允许使用新旧密钥。v3 在 APK 签名分块中添加了有关受支持的 SDK 版本和 proof-of-rotation 结构的信息。

APK 签名验证过程

参考文章:

APK 签名方案 v2  |  Android 开源项目  |  Android Open Source Project

zip文件格式

美团自动化打包实践

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值