签名机制是Android系统相对于Linux独有的安全机制,本文将对该机制做详细介绍。
Android的签名机制主要用在以下两个场合:App更新包的校验和申请手机权限时的权限检查。
用户在升级一款已经安装过的App时,如果程序的修改来自于同一来源,则允许升级安装,否则会提示签名不一致无法安装的提示。
对于申请权限的 protection level 为 signature 或者 signatureOrSystem 的,会检查权限申请者和权限声明者的证书是否是一致的。
Android 系统不允许安装没有任何数字签名的应用APK程序,所有应用程序必须使用某个证书进行签名(一般为应用开发者自签名证书)。所以在完成Android程序编写后,需要将程序打包成APK安装文件。首先由应用开发者使用自己的私钥,对整个Android程序进行签名,生成数字签名的文件,将打包签名后的APK文件发布到安卓应用市场。
图1 Android证书验证流程图
用户从市场下载APK安装文件,在真正安装APK前,会首先验证数字签名。具体步骤:
(a) 首先计算除META-INF文件夹以外所有文件的SHA1摘要值,同META-INF文件夹内的MANIFEST.MF文件中的摘要值做比对。如果不同,则证明源文件被篡改,验证不通过,拒绝安装。
(b) 计算META-INF文件夹中的MANIFEST.MF的摘要值, 以及MANIFEST.MF中每一个摘要项的摘要值,同.SF文件中的摘要值做比对。如果不同,则证明.SF被篡改,验证不通过,拒绝安装。
从META-INF文件夹中的.RSA 文件中取出开发者证书,然后从证书中提取开发者公钥,用该公钥对.SF文件做数字签名,并将结果同.RSA文件中的.SF签名进行比对。如果不同,则验证不通过,拒绝安装。
对某一个APK文件重名为为RAR压缩文件,打开该压缩文件后就可以看到META-INF文件夹
图2 解压APK文件得到的目录文件
打开META-INF文件夹可以看到三个数字签名文件MANIFEST.MF,CERT.SF,CERT.RSA
图3 META-INF文件夹中的签名文件
首先,如果想要改变了apk包中的文件,插入恶意代码。那么在apk安装校验时,改变后的文件摘要信息与MANIFEST.MF的检验信息不同,于是验证失败,程序就不能成功安装。
其次,如果对更改的过的文件相应的算出新的摘要值,然后更改MANIFEST.MF文件里面对应的属性值,那么必定与CERT.SF文件中算出的摘要值不一样,照样验证失败。
最后,如果继续计算MANIFEST.MF的摘要值,相应的更改CERT.SF里面的值,那么数字签名值必定与CERT.RSA文件中记录的不一样,还是失败。
那么能不能继续伪造数字签名呢?答案是不可能,因为没有数字证书对应的私钥。因为公钥被开发者公开发布了,公钥就确定了,由于公钥与私钥一一对应,所以在修改文件后,没有相应的私钥,无法的到正确的数字证书,那么在应用使用公钥进行校对的时候,就会验证失败。
在应用更新时,或者在安卓应用平台校验要安装的应用是否合法(是否是原开发者签名发布的应用),通过比对CERT.RSA中的公钥。由于RSA算法的加密方式,一个密钥对应一个公钥,不会存在多对一的情况,所以根据此可以意识到:
(a) Android签名机制其实是对APK包完整性和发布机构唯一性的一种校验机制。
(b) Android签名机制不能阻止APK包被修改,但修改后的再签名无法与原先的签名保持一致。(拥有私钥的情况除外)。
(c) APK包加密的公钥就打包在APK包内,且不同的私钥对应不同的公钥。换言之,不同的私钥签名的APK公钥也必不相同。所以我们可以根据公钥的对比,来判断私钥是否一致。
Bluebox在2014年7月30日公布了一个关于APK签名的漏洞——FakeID,黑客可以利用该漏洞可以提升权限,突破沙箱限制。
一般情况下,我们平时发布的Android应用都是采用自签名的方式,所谓自签名是指公钥证书中Issuer(发布者)和Subject(所有者)是相同的,比如Adobe Flash Player的签书信息如下所示:
图4 Adobe Flash Player的签书信息
除了通过自签名的方式,我们还可以采用由CA颁发私钥证书进行签发。采用这种方式,最终APK中的公钥证书中,就会包含证书链。这种方式跟签名名的主要区别是最终的公钥证书中,Issuer和Subject是不相同的,而且会存在多于一个证书,证书与证书之间是通过Issuer与Subject进行关联的,Issuer负责对Subject进行认证。
前面说到,当APK是非自签名时,APK中存在证书链。当安装APK时,系统只会用位于链中最底层的证书对APK文件进行合法性和完整性校验,但并不会验证证书链的有效性。想象这么一个情景:
(a)开发机上生成一个根证书(记作CA),并用这个证书去颁发一个子证书(记作CLIENT);
(b)使用这个子证书对APK签名,这时APK中的RSA文件将包含两个证书,分别是CLIENT和CA,其中系统会使用CLIENT对APK文件进行检验;
(c)对这个RSA文件篡改,把CA修改为Adobe Flash Player的CA(记作FakeCA)。由于系统不会对证书链的合法性进行验证,所以修改后APK,依然可以被正常安装;
产生FakeID漏洞的根本原因是因为安装APK时,系统没有对证书链进行合法性验证,下面分析一下有漏洞的代码。见代码:
图5 FakeID漏洞所利用的漏洞代码
代码中遍历证书文件,只要issuer跟遍历的candidates[i].getSubjectDN()相等即可,这个equal只是简单的做了字符串的对比,就直接认为这个是合法的证书,并返回来。
图6 修补漏洞后的代码
修补后的代码,添加了subjectCert和chainCheck两个参数,添加了证书链的验证。
如果没有修补代码会怎样?我们可以这么构造一个恶意证书:
(a)开发机上生成一个根证书(记为CA),并用这个证书去颁发一个子证书(记为SIGN)
(b)然后使用这个子证书为我们即将发布的apk签名,这时APK中的.Rsa文件将包含两个证书,一个是SIGN,一个是CA。并且APK所有的文件都是可以用这个RSA文件验证其合法性的
(c)对这个RSA文件进行篡改,只修改CA证书的内容(替换后的CA记为FakeCA),不修改证书中的SignerInfo部分,不影响PackageManger在安装前使用SignerInfo.encryptedDigest对APK包数据完整性进行校验
(d)这个APK可以被成功安装,并且包含两个证书,一个是SIGN,一个是FakeCA
上文已经提到过,PackageManger在安装APK时并不校验证书链上所有证书的合法性,只要存在被指定的SIGN能够校验APK中所有文件的合法性即可。
但是签名证书的另外一个功能,验证身份,则受到了这个漏洞的影响。系统中多处使用getPackageInfo获取安装包证书,如果获取到多个证书,通常认为只要有一个证书可信即可。
具体利用方式,见下面链接:
http://blogs.360.cn/360mobile/2014/08/04/all-about-fakeid/
1. http://myeyeofjava.iteye.com/blog/2125348
2. http://www.2cto.com/News/201412/357785.html
3. http://blog.csdn.net/jiangwei0910410003/article/details/50443505
4. http://blog.csdn.net/jiangwei0910410003/article/details/50402000
5. http://blog.csdn.net/l173864930/article/details/38409521