数字签名解决了什么?
校验apk文件的完整性,检查是否有损坏或篡改。
数字签名格式
我们在对Apk签名时并没有直接指定私钥、公钥和数字证书,而是使用keystore文件,这些信息都包含在了keystore文件中。根据编码不同,keystore文件分为很多种,Android使用的是Java标准keystore格式JKS(Java Key Storage),所以通过Android Studio导出的keystore文件是以.jks结尾的。
keystore使用的证书标准是X.509,X.509标准也有多种编码格式,常用的有两种:pem(Privacy Enhanced Mail)和der(Distinguished Encoding Rules)。jks使用的是der格式,Android也支持直接使用pem格式的证书进行签名。
v1签名
v1签名也就是jar签名机制
签名过程:
- 先对apk中每个文件用SHA-1哈希算法求得摘要,再用BASE64编码,将这些信息保存在apk解压后的META-INF目录下的MANIFEST.MF里。
- 再对MANIFEST.MF整个文件以及MANIFEST.MF每个条目用SHA-1求得摘要,再用BASE64编码,存入META-INF/CERT.SF。
- 对CERT.SF计算摘要,用Android签名文件中的私钥加密这个摘要,作为签名存入CERT.RSA
MANIFEST.MF和CERT.SF保证apk数据的完整性,CERT.RSA从整体上保证了数据的完整性和来源可靠。
MANIFEST.MF和CERT.SF可能会被篡改,但最终CERT.RSA中的签名是由私钥加密生成的,没有私钥就不可能生成同样的签名。
CERT.RSA还存储了签名文件(keystore)的公钥,用于解密CERT.RSA中的签名,校验apk时,如果CERT.SF求得摘要后跟签名不一致,说明CERT.SF被篡改过。
v1签名缺点:
- META-INF不在校验访问,这样可以在这个目录下任意放置文件。
- 每一个文件计算摘要,比较耗时。
CERT.RSA中存储了什么?
签名文件的公钥、签名、签名算法、签名所有者信息。
v2签名
- v1签名验证的是每一个文件的完整性。
- v2签名验证的是整个apk的字节流。
APK签名信息存储在“APK签名分块”。
ZIP包原本的数据从头到尾分为三块:文件数据区、目录数据区、目录结尾数据区。
APK签名分块是位于文件数据区和目录数据区之间的,目录结尾数据区中记录了目录数据区的地址位置(也就是相当于开头的偏移量),加入APK签名分块后,仅需要修改目录数据区的地址,不影响ZIP其他原来的数据。
APK 签名方案 v2 负责保护第 1、3、4 部分的完整性,以及第 2 部分包含的“APK 签名方案 v2 分块”中的 signed data 分块的完整性。
优势:
- 对整个zip分块计算哈希摘要,计算量减少,同时可以并行,速度更快。
- 验证了整个zip文件,不会漏校验zip中的数据。
v3签名
android系统里不同的keystore签名的apk不能覆盖安装,但是keystore可能因为各种不可控的原因泄露了,按照这样的机制app的发布者就束手无策,别人就可以用恶意的apk覆盖安装发布者发布的正常的apk。如果能用新的keystore签名重新签名app,并能覆盖安装用旧的keystore签名的apk,就可以解决keystore泄露的问题了。
但是也不能让任意的keystore签名的apk可以覆盖安装任意apk,这样keystore签名就失去了校验开发者身份的功能,覆盖安装apk时,android系统得知道新的apk用的新的签名也是同一个发布者签上的,那怎样保证这点?
v3签名使用密钥轮转机制,让apk可以变更签名,在签名数据库新增了一个块,专门存储证书链,建立一个签名信任链,老的证书在前,新的证书在后,新的证书中的签名是由老的数字证书对应的keystore的私钥加密的,所以在覆盖安装apk时,就可以用旧apk中的老的数字证书中的公钥解密新的数字证书中的签名,得到新的数字证书的内容哈希摘要,再根据新证书中指明的哈希算法,计算新证书内容的哈希摘要,对比两个哈希摘要是否一致,一致则说明可信赖,可以覆盖安装。