将apk的后缀改名为.zip再解压,我们会看到产生了如下文件,存放在META-INF这个文件夹中:
这些签名后产生的文件有什么用呢?
META-INF
当对apk文件进行签名时就会生成这个文件夹,它包含了一些验证信息,它包括了在这个apk文件里每一个文件的指纹信息。所谓的文件指纹就是文件的摘要——通过相应的hash算法(md5,sha1,sha128,sha256)计算得到。只要有一个文件有改变,哪怕是一丁点,重新hash就会产生一个完全不一样的hash值(指纹)。
文件的指纹就是人的指纹一样是独一无二的。这就意味着,一旦apk文件里有一丁点的改动都要求重新签名,否则Android系统将拒绝安装该apk文件,当我重新签名apk文件时,META-INF文件夹里的文件都会被更新。
MANIFEST.MF
MANIFEST.MF存储了apk中所资源文件的信息摘要。这样做是为防止这些资源文件的完整性受到破坏。简单一点来说,资源文件包括就是apk里的所有与应用有关的文件,如类文件、图片文件等等,完整性就是指我原先打包时,是什么样的,用户安装时也是什么样的,多一个少一个比特位,都让文件与我们想要提供的不一样了,这就是文件完整性被篡改了。MANIFEST.MF里的Name就是对应的文件,SHA-256-Digest对应的就是这份文件的信息摘要经过Base64编码后的值。整个apk里的文件都会被枚举在这里,先计算文件的信息摘要,再将信息摘要进行Base64编码。每一个文件都这样做。
这个文件提供了应用的每一个组件的hash值,如AndroidManifest.xm等。它用于验证zip中每个文件的有效性。一旦更改了此文件或zip容器中的任何其他文件,将会撤销签名并使其无效。这些文件有一点点改变,所得到的hash值也会完全不同。
KYUNBAN.SF
这个文件的名字是因为我的.jks文件名是kyunban.jks,所以这个.sf的文
件名就是KYUNBAN.SF。
这份文件存储了MANIFEST.MF文件的信息摘要的Base64编码后的值。它同时也存储了保存在MANIFEST.MF文件中所有信息摘要的信息摘要。
我们暂且这么说这个文件是用于保护MANIFEST.MF文件的。MANIFEST.MF文件每个入口都由三行组成:Name、SHA-256-Digest、空行这三行。KYUNBAN.SF这个文件存在了对MANIFEST.MF每三行的hash值再进行Base64编码后的值。对MANIFEST.MF文件每个入口都执行这样的操作。
KYUNBAN.RSA
这个文件的名字是因为我的.jks文件名是kyunban.jks,所以这个.rsa的文件名就是KYUNBAN.RSA。这个RSA后缀是跟使用的加密算法有关,比如用的是DSA加密算法,那么这个文件名就是KYUNBAN.DSA,
这份文件存储了KYUNBAN.SF这份文件的数字签名和它的签名证书。
验证过程
Android verification flow is shown in Fig 2, where .md stands for message digest
and .signature stands for digital signature.
- 首先,枚举所有的源文件除了META-INF文件夹以外,计算它们的信息摘要,并与MANIFEST.MF对应的记录作比较,信息摘要值相同则验证通过,否则不通过。依次对所有的文件做这样的比较操作,如果都通过,这是在做文件完整性的检查。所有都通过就会进行下一步的验证;
- 上一步验证通过后,说明根据MANIFEST.MF的信息,好像所有的文件都完整没有被篡改,为什么说好像呢?因为目前没有验证MANIFEST.MF这个文件的完整性,只有它的完整性得到验证那么上步确的文件完整性就得到进一步确认了。我们通过KYUNBAN.SF验证MANIFEST.MF的完整性。方法就是在计算后,对比MANIFEST.MF中每个入口的信息摘要,与KYUNBAN.SF中记录的值进行比较,相同则通过,否则不通过。
- 当第二步通过后,就好像MANIFEST.MF的完整性得到了验证,但是,KYUNBAN.SF是否完整,我们也还尚不清楚,所以要再进行KYUNBAN.SF的完整性验证。验证的过程就是从KYUNBAN.RSA的证书中取出公钥,然后验证KYUNBAN.SF签名的有效性。如果有效,则KYUNBAN.SF是完整的,再依次证明了MANIFEST.MF是完整的,进而确定了所有文件都是完整的。
- 只有所有验证都成功完成,apk才能安装在用户的
android设备。当涉及到应用程序升级时,公钥从KYUNBAN.RSA被取出来必须与现有版本保持一致。否则,升级将会失败。
这里提供多一些信息来告之最后一步.sf的文件签名是如何被验证的,我们先来年看KYUNBAN.RSA是的签名:
meta-inf % openssl pkcs7 -inform DER -in KYUNBAN.RSA -noout -print_certs -text
Certificate:
Data:
Version: 1 (0x0)
Serial Number: 1 (0x1)
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=kyunban, OU=kyunban, O=kyunban, L=GZ, ST=GD, C=CN
Validity
Not Before: Jan 6 04:58:51 2024 GMT
Not After : Dec 30 04:58:51 2048 GMT
Subject: CN=kyunban, OU=kyunban, O=kyunban, L=GZ, ST=GD, C=CN
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:c8:27:ec:f0:85:cb:2b:a3:e3:0e:8f:a0:41:94:
d7:18:7a:b8:aa:63:68:9e:28:d3:8f:a3:42:87:88:
78:b2:db:96:e9:2e:b9:01:6f:f3:96:30:04:7d:00:
33:b4:dc:33:f9:8a:dc:5e:5e:d8:2f:83:b1:ac:53:
b8:e1:38:fb:5a:f8:6b:89:cf:01:73:f5:70:c1:3e:
96:67:9e:fe:35:8e:ad:3b:d9:8c:13:e6:7b:61:9b:
5f:ba:0f:67:65:6b:39:cd:17:59:53:46:e4:41:83:
48:52:9f:cf:85:ac:bc:dd:e4:e1:5d:7b:8a:17:9e:
f2:71:ac:24:88:3b:ea:ae:36:90:e0:6e:a4:33:b7:
11:f3:ac:00:df:83:95:1c:e4:ec:be:17:4f:b8:e5:
59:8a:5c:e5:1d:a9:4b:a3:93:ad:9f:56:76:d8:11:
05:99:51:d5:63:56:8b:f4:58:af:6c:4c:96:a5:8e:
af:d7:f0:fa:e4:40:11:8b:c9:79:dc:77:e4:17:b4:
12:97:f5:fd:6c:d5:07:00:7d:ce:b3:1a:fe:10:b9:
47:7f:bf:4a:49:bd:77:e9:89:f9:3a:d3:d8:04:a4:
26:e7:c5:b9:2d:6f:00:3e:52:86:89:9f:fc:66:71:
86:62:ce:9f:73:5b:9c:f5:6b:ef:b0:a7:be:0d:c4:
d5:63
Exponent: 65537 (0x10001)
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
a7:04:70:e3:b6:88:c1:37:0a:ea:90:bc:47:e5:51:f8:6c:42:
22:18:0f:1e:d6:04:63:7a:8f:db:40:b3:e4:96:70:f3:3c:29:
41:e0:5d:4c:78:bf:f7:76:4a:c1:46:f3:b5:4e:2c:7d:5f:8c:
89:53:47:e7:a4:7c:09:63:5d:bc:db:21:5e:20:8c:53:7f:d2:
24:53:f9:e4:3e:52:1d:9d:1a:1f:54:30:d4:de:f5:1c:51:56:
b5:86:0d:13:7f:e7:4e:1c:0b:61:c4:13:bc:48:9a:8c:62:72:
97:b9:02:84:3c:fd:62:a1:07:65:1a:4c:54:b6:a6:39:5b:39:
6f:2f:e4:a0:ae:3c:ca:35:67:f8:01:a3:9b:bd:15:d4:e7:94:
c7:77:e7:c7:79:b6:97:a6:f0:35:39:51:16:56:63:72:32:0f:
0c:89:75:bb:98:eb:b4:ab:9f:0e:60:56:93:37:7a:5d:d0:e3:
ea:84:7b:6a:71:e4:33:a7:34:2d:e6:81:58:a6:82:90:d7:c1:
53:6a:c1:c0:ce:8d:c4:bd:fa:0a:72:81:c4:74:6b:30:f0:7a:
dd:b5:a3:f0:80:0a:ff:d7:ce:cf:90:c7:6d:35:f0:3c:f3:b8:
17:e7:c2:e4:5d:73:97:06:7b:e5:43:8b:9e:32:77:5b:cc:9c:
b0:32:26:20
通过上面的信息,我们可以知道Signature Value的值,也就是用akpsigner对apk签名时,对KYUNBAN.SF文件的签名,它对计算KYUNBAN.SF得到的信息摘要,用私钥进行了加密。那么当它要验证KYUNBAN.SF时,只需要进行如下的步骤即可完成:
- 计算KYUNBAN.SF的信息摘要
- 用KYUNBAN.RSA里的证书的公钥对Signature Value(数字签名)进行解密,也得到一个信息摘要的值。
- 对比前面两步得到的信息摘要,如果相同则验证通过,否则不通过。
本文到这里,也就完成了我的描述。其实,这种验证也不是完备的。这里的公钥是有可能被换掉的,这样就可以伪造一个app,例如重新打包签名,就好像一个新app一样,但是前提是设备上没有安装过同包名的应用,否则会因为签名问题而被拒。其他问题,稍后再分享。