1、签名是什么
要知道签名是什么,先来看为什么需要签名 。大家都知道,在消息通信时,必须至少解决两个问题:一是确保消息来源的真实性,二是确保消息不会被第三方篡改。在安装Apk时,同样需要确保Apk来源的真实性,以及Apk没有被第三方篡改。如何解决这两个问题呢?方法就是开发者对Apk进行签名:在Apk中写入一个“指纹”。指纹写入以后,Apk中有任何修改,都会导致这个指纹无效,系统在安装Apk进行签名校验时就会不通过,从而保证了安全性。进行签名校验,校验通过后才能安装成功。那在这个过程中签名校验的机制是什么?具体校验的是什么内容?我们APP是如何进行签名管理、签名以及发布验证呢?本篇文章将从这几个方面进行介绍。
要了解如何实现签名,需要了解两个基本概念:数字摘要和数字证书。
1.1数字摘要
数字摘要是将任意长度的消息变成固定长度的短消息,它类似于一个自变量是消息的函数,也就是Hash函数。数字摘要就是采用单向Hash函数将需要加密的明文“摘要”成一串固定长度的密文,这一串密文又称为数字指纹,它有固定的长度,而且不同的明文摘要成密文,其结果总是不同的,而同样的明文其摘要必定一致。
1.2签名大概过程
前面已经说到,可以通过签名来确保数据来源的可靠性和数据的不可篡改性。签名就是在摘要的基础上再进行一次加密,对摘要加密后的数据就可以当作数字签名,在安装Apk需要对签名进行验证,验证通过才能继续安装。
1.3证书
接收方必须要知道发送方的公钥和所使用的算法。如果数字签名和公钥一起被篡改,接收方无法得知,还是会校验通过。如何保证公钥的可靠性呢?答案是数字证书,系统在安装Apk时并没有校验证书本身的合法性,只是从证书中提取公钥和加密算法,这也正是对第三方Apk重新签名后,还能够继续在没有安装这个Apk的系统中继续安装的原因。
2.签名校验
Android与iOS签名实现和校验方式有所不同,以下将分平台介绍两端各自签名校验过程。
2.1Android签名方式
Android 的签名方案,发展到现在,已经支持三种应用签名方案:
- v1 方案:基于 JAR 签名。
- v2 方案:APK 签名方案v2,在 Android 7.0 引入。
- v3 方案:APK 签名方案v3,在 Android 9.0 引入。
目前我们APP大多采用V1+V2签名方式,由于V2方式不支持低于7.0的版本,因此,为了低版本正常安装,先使用v1签名再使用v2签名。在实际应用中,优先校验v2签名,没有或不存在校验机制时,校验v1签名。在主工程build.gradle中定义了签名配置。
2.1.1V1签名
其中由于V1签名生成的文件是META-INF中MAINIFEST.MF CERT.RSA CERT.SF,那这三个文件之间是如何对APK进行签名的呢,一张图足以说明整个签名过程:
根据上面签名过程,V1签名校验流程具体是:
- 首先校验cert.sf文件的签名
计算cert.sf文件的摘要,与通过签名者公钥解密签名得到的摘要进行对比,如果一致则进入下一步;
- 校验manifest.mf文件的完整性
计算manifest.mf文件的摘要,与cert.sf主属性中记录的摘要进行对比,如一致则逐一校验mf文件各个条目的完整性;
- 校验apk中每个文件的完整性
逐一计算apk中每个文件(META-INF目录除外)的摘要,与mf中的记录进行对比,如全部一致,刚校验通过;
- 校验签名的一致性
如果是升级安装,还需校验证书签名是否与已安装app一致。
在校验步骤中,任何一步校验出错,都会导致APP签名校验不通过,无法安装APP或无法覆盖安装,例如:
- 如果篡改apk内容,会导致校验apk每个文件完整性失败,因为此文件hash值会被更改,与.mf和.sf中不同;
- 如果篡改apk内容,同时篡改.mf以及.sf摘要信息,在校验.sf签名是也会失败;
- 如果将apk内容和签名信息一同篡改,相当于对apk进行重新签名,他会相当于一个新APP安装到系统中,无法进行覆盖安装。
2.1.2V2签名
通过V1签名我们可以知道,V1签名是在apk文件中增加META-INF目录,而V2签名是全文件签名方式,可以对.apk所有受保护的内容进行签名保护。APK文件结构上来看,由3个部分构成:ZIP 条目的内容、ZIP 中央目录、ZIP 中央目录结尾。V2方案为加强数据完整性保证,不在ZIP 条目的内容和ZIP 中央目录中插入数据,选择在两者之间插入一个APK签名分块,从而保证了原始zip(apk)数据的完整性。
第1、3、4部分的完整性是通过内容摘要来保护的,这些摘要保存在signed data分块中,而signed data分块的完整性是通过签名来保证的。
根据上面签名方式原理,V2签名校验流程具体是:
- 找到APK签名分块并验证以下内容:
APK 签名分块的两个大小字段包含相同的值。
ZIP 中央目录结尾紧跟在ZIP 中央目录记录后面。
ZIP 中央目录结尾之后没有任何数据。
- 找到APK 签名分块中的第一个APK 签名方案 v2 分块。如果 v2 分块存在,则继续执行第 3 步。否则,回退至使用 v1 方案验证 APK。
-
对APK 签名方案 v2 分块中的每个signer执行以下操作:
从 signatures 中选择安全系数最高的受支持 signature algorithm ID。安全系数排序取决于各个实现/平台版本。
使用公钥并对照signed data 验证 signatures 中对应的 signature。
验证 digests 和 signatures 中的签名算法 ID 列表(有序列表)是否相同。这是为了防止删除/添加签名。
使用签名算法所用的同一种摘要算法计算 APK 内容的摘要。
验证计算出的摘要是否与 digests 中对应的 digest 相同。
验证 certificates 中第一个 certificate 的 SubjectPublicKeyInfo 是否与公钥相同。
- 如果找到了至少一个 signer,并且对于每个找到的 signer,第 3 步都取得了成功,APK 验证将会成功。
2.1.3V1、V2签名对比
我们APP目前都向支持V2签名转变,那为什么要支持新的签名机制呢?根据两种签名机制的原理分析,我们不难看出,V2签名是优于V1签名,具体对比如图所示:
签名方式 | 效率方面 | 安全方面 |
V1签名 | 低 需对所有文件进行hash校验,速度较慢 |
不够安全 只保证了APK内各文件的完整性,APK其它内容的完整性未保证 |
V2签名 | 高 只需进行一次hash校验,速度快 |
较安全 除保证了APK内各文件的完整性,APK中数据区、中央目录和中央目录结尾记录的完整性均得到了保证 |
签名校验耗时,体现在安装耗时上,同一部手机,安卓7,安装同样的包,v2签名的apk安装耗时是v1签名的1/3 |
2.2iOS签名方式
iOS采用双层代码签名校验
生成签名:
在Mac开发机器生成一对公私钥,这里称公钥L,私钥L;
把公钥L上传到苹果后台,用苹果后台里的私钥A去签名公钥L。得到一份数据包含了公钥L以及其签名,把这份数据称为证书,和一份描述文件;
编译完一个 APP 后,用本地的私钥M对这个APP进行签名,同时把从苹果服务器得到的 Provisioning Profile 文件打包进APP里,文件名为embedded.mobileprovision。
认证过程:
在安装时,iOS 系统取得证书,通过系统内置的公钥 A,去验证证书的数字签名是否正确;
通过系统内置的公钥 A,解密拿到描述文件,查看里面的设备列表时候包含当前设备;
验证证书后确保了公钥 L 是苹果认证过的,再用公钥 L 去验证 APP 的签名;
验证描述文件里的AppID是否与当前App的AppID一致,App申请的功能时候与描述文件一致。
3.签名管理与发布验证
3.1签名管理
目前android与iOS端证书文件均由RD提供,QA统一严格管理。
android证书布置在打包机器,代码工程配置使用,打出不同类型包。
iOS证书存放打包机器,在运行打包命令时使用,打出不同类型包。
3.2发布验证
目前打包平台所包含包类型如下:
3.2.1Android端
证书类别 | 特点 | 有效期 | 说明 | 使用场景 |
debug证书 | 不同app使用同一debug证书,但不能覆盖安装 | 1年 | 可进行debug调试,debug包未做混淆,不得外传 | 单元测试,debug调试 |
release证书 | 不同app可能相同release证书 | 根据生成配置 | 调试麻烦,集成时需要验证代码混淆是否造成bug | 集成测试,发布市场 |
使用release包发布,首先验证已开启代码混淆,另外需要验证覆盖安装,保证证书一致。
3.2.2iOS端
证书类别 | 特点 | 有效期 | 说明 | 使用场景 |
App Development | 可以制作多个副本分发给多台设备 | 1年 | 只可以使用Xcode进行真机调试 | 本地调试 |
InHouse | 只能有一个,不能制作副本分发到多台电脑 | 3年 | 可以在任意一台iOS设备上安装,肆意的安装可能会遭到封号 | 单元测试,通过第三方托管发布内部渠道 |
AdHoc | 非越狱的设备也能够安装,该设备的UDID已经添加到开发者账号所在的组中 | 单元测试,集成测试 | ||
AppStore | 1年 | 可直接提交App Store或在越狱机上使用 | 集成测试,发布App Store |
使用release或者inhouse包发布,需要验证覆盖安装,保证证书一致。