安卓 签名验证原理
查资料得知MANIFEST.MF装的是apk资源的SHA1,*.SF文件装的是正版apk的SHA1的RSA私钥加密的密文,*.RSA装的是RSA的公钥。当Android系统安装apk时,会把apk资源SHA1一下,用*.RSA装的公钥把*.SF解密后与算得的SHA1对比确保资源的完整性。问题就是,当一个有相同包名的应用被请求安装的时候,Android系统会做哪些事情来决定是替换旧版apk还是反馈包名冲突而拒绝安装?系统如何得知新旧apk是来自同一开发者且没有被修改过? 自己研究了一阵子,大致理解是:系统检测到新apk与某已装某apk包名冲突时,就判断两个apk签名是否一致,如果不一致,则可判断新apk来自不同开发者,拒绝安装。如果签名一致,则使用*.RSA的公钥解密*.SF的密文得到正版apk的hash值并亲自计算验证之,如果相同则证明apk未被修改,反之安装失败。 请高人帮忙看一下上面的理解是否正确,再解答一下比对新旧apk签名的一致性是如何实现的。是直接比对.RSA文件一致性吗?
开始之前先学习一下签名校验的原理和如何绕过签名校验。
Android签名生成步骤和原理:
我们在App开发完成后将其编译为apk包,这个时候包中没有签名信息,如果直接通过adb install安装到手机上,将会报错:[INSTALL_PARSE_FAILED_NO_CERTIFICATES]
解压apk包可以发现未签名的apk中不包含META-INF目录,该目录在签名后生成,其中包含签名信息相关的3个文件CERT.RSA,CERT.SF,MANIFEST.MF,签名过程就是这3个文件的生成过程。
签名第一步:遍历apk中所有文件(entry),逐一生成SHA-1数字签名,然后通过Base64编码,写入MANIFEST.MF文件。
vim查看MANIFEST.MF文件内容如下:
Manifest-Version: 1.0
Created-By: 1.0 (Android)
Name: res/drawable-hdpi/m03_0.png
SHA1-Digest: Odo4l7R6YcmMl9HtbhLpb8hChts=
Name: res/layout/shop_l
SHA1-Digest: CVvGtpAo1cLWo7OQfR3tZQwYOY4=
......
第二步:使用SHA1-RSA算法,用私钥对MANIFEST.MF摘要文件签名,并对其中每个文件摘要签名,生成CERT.SF文件
vim查看其内容如下:
Signature-Version: 1.0
Created-By: 1.0 (Android)
SHA1-Digest-Manifest: 4pIv24erc5QG+i/kLOOhG6n+EdY=
Name: res/drawable-hdpi/m03_0.png
SHA1-Digest: 9ZYIfLFplSPcDEIxLJl0JUzmJLs=
Name: res/layout/shop_l
SHA1-Digest: nqQ+nd6yZn3sB5LfHltWOYlXJMA=
......
(SHA1-Digest-Manifest:是对MANIFEST.MF文件的摘要签名后的值)
第三步:生成CERT.RSA文件,其中记录了RSA公钥,加密算法等信息用于签名校验,该文件是二进制格式,有兴趣可以研究下。
插入学习一下RSA加密和签名原理:
RSA有2种应用方式,加密和验证,这里只用到了验证,为了使整个实现过程更加清晰明了,先学习下其原理,这里我们
可以假定加密和验证过程无法被破解,通过私钥加密的信息唯有公钥才能解密。
RSA加密过程(以客户端A发送一条消息给客户端B为例,其中A持有公钥,B持有私钥):
客户端A消息原文:msg
将msg映射为整数(实际上分为很多段整数):Int(msg)
用公钥进行加密:En( Int(msg) ) )
————发送加密后的信息————
客户端B收到加密信息:En( Int(msg) )
用私钥解密:De( En( Int( msg) ) ) = Int(msg)
还原msg:UnInt( Int(msg) ) = msg
(通过私钥加密的信息唯有公钥才能解密,可以确保信息不会泄漏)
RSA签名与验证过程(客户端A发送一条签名后消息给客户端B,B验证该消息是否为A发出或者未被篡改,其中A持有私钥,B持有公钥):
客户端A消息原文:msg
对msg提取摘要:Dig(msg)
对摘要Base64编码:Base64( Dig(msg) )
用私钥进行签名(把摘要当作已经加密后的内容,用私钥解密):Sign( Base64( Dig(msg) ) )
————发送消息和签名————
客户端B收到信息和签名:msg 和 Sign( Base64( Dig(msg) ) )
对msg提取摘要:Dig(msg)
对摘要Base64编码:Base64( Dig(msg) )
将摘要当作未加密过的内容,用公钥加密:En( Base64( Dig(msg) ) )
安卓软件签名工具
签名验证,即判断En( Base64( Dig(msg) ) ) 和Sign( Base64( Dig(msg) ) )是否相等。(这里的Sign跟上面的De一模一样)
(通过私钥加密的信息唯有公钥才能解密,可以验证这条消息肯定是A发出来的)
在Android里面签名验证的实现:
覆盖安装时用旧版本的公钥对新安装apk包中MANIFEST.MF进行加密,生成新的MANIFEST.MF'文件,再和原始的MANIFEST.MF文件对比所有的摘要信息是否一致,以此确保是否为同一个机构发布的升级app。(实际当然不是生成新的MANIFEST.MF'文件,可以这么理解)