Android签名原理介绍(1)--schemev1
前言
因为某些不知名的原因,让我感觉很有必要学习一下apk签名的方式,于是。。。便有了这篇
(真的好久没写了哇,近期烦心事真的太多了)
我在网上搜了各种大大小小关于apk签名的文章,但始终没有能让我满意的,而且也感觉一直缺乏一篇能从字节上分析对比新旧两种签名方式的文章,so……
传统的安卓签名方式是通过jar的签名方式实现的,在apk包下,会有一个META-INF
的文件夹,META-INF的文件夹下,会有MANIFEST.MF
CERT.SF
CERT.RSA
三个文件,均是用于apk签名认证的。
而在Android7.0后,新增了一个APK Signature Scheme v2的签名方式,更为强效。而原来的签名方式则是 JAR-signed APK verification (v1 scheme)
详细可看官方的文档
https://source.android.com/security/apksigning/v2#v1-verification
JAR-signed APK verification (v1 scheme)
分析一下原版的签名方式
├─META-INF
│ CERT.RSA
│ CERT.SF
│ MANIFEST.MF
MANIFEST.MF
:储存了apk底下除了META-INF文件夹外其余文件的SHA1后对应的base64的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Manifest-Version: 1.0 Built-By: Generated-by-ADT Created-By: Android Gradle 2.3.0
Name: res/anim/design_snackbar_in.xml SHA1-Digest: QPiYcwJRp7LvVEneHabhK9QbisA=
Name: res/layout/design_text_input_password_icon.xml SHA1-Digest: jJEvq6P1Soe1bsUay8j+cdxWKGk=
Name: res/drawable-hdpi-v4/abc_list_longpressed_holo.9.png SHA1-Digest: KQunCQh0E4bP0utgN0cHdQr9OwA=
Name: res/drawable-xxhdpi-v4/abc_ic_star_half_black_16dp.png SHA1-Digest: EikVyBT5I7pmbJO2k8qF0V5hUc0=
...
|
CERT.SF
:在MAINFEST.MF文件的基础上,增加了对MANIFEST.MF的SHA1的值,并在base64编码后放到开头。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Signature-Version: 1.0 SHA1-Digest-Manifest: rYILhq+GQ4rnRO9NeNUFTLzDiFs= Created-By: 1.0 (Android)
Name: res/anim/design_snackbar_in.xml SHA1-Digest: QPiYcwJRp7LvVEneHabhK9QbisA=
Name: res/layout/design_text_input_password_icon.xml SHA1-Digest: jJEvq6P1Soe1bsUay8j+cdxWKGk=
Name: res/drawable-hdpi-v4/abc_list_longpressed_holo.9.png SHA1-Digest: KQunCQh0E4bP0utgN0cHdQr9OwA=
Name: res/drawable-xxhdpi-v4/abc_ic_star_half_black_16dp.png SHA1-Digest: EikVyBT5I7pmbJO2k8qF0V5hUc0=
...
|
CERT.RSA
:这个则是RSA证书文件了,文本文件打开肯定是乱码,我们可以通过openssl查看其信息。
应该算挺标准的X509格式的证书吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| $ openssl pkcs7 -inform DER -in CERT.RSA -noout -print_certs -text Certificate: Data: Version: 3 (0x2) Serial Number: 2064162882 (0x7b08a042) Signature Algorithm: sha256WithRSAEncryption Issuer: OU=gifish, CN=lin.giglf Validity Not Before: Feb 28 14:20:15 2017 GMT Not After : Feb 22 14:20:15 2042 GMT Subject: OU=gifish, CN=lin.giglf Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:af:8a:df:e1:7f:70:b4:97:85:71:1b:ca:b8:9e: 48:80:7a:4e:10:5a:a7:d7:aa:85:3a:7c:0a:99:42: ec:db:01:d7:28:ad:69:72:04:e6:9e:f3:0a:56:30: f1:d8:1e:8b:bd:d9:94:2e:ac:b0:64:83:71:89:24: e4:8e:63:f1:24:7f:73:d7:e4:48:d6:82:11:f6:9c: 1f:de:4a:22:52:f1:b3:7b:0b:7a:01:09:fc:8e:36: 85:2c:79:b3:25:60:93:aa:61:d2:d4:7c:e1:ab:70: 40:5b:bb:cd:30:9a:cd:40:89:df:8d:73:25:87:ce: 49:1e:63:42:47:d7:f4:66:40:56:db:67:6b:89:b2: 4f:8e:9f:4a:f3:04:f4:40:58:02:83:c0:81:0b:5e: 79:e6:d3:7f:3b:32:22:47:ed:f2:42:1f:7c:94:41: b1:d2:4b:f7:df:d0:61:68:1f:74:60:07:56:05:e4: 44:27:5d:15:24:66:02:3e:51:06:7b:13:9c:f1:31: f3:15:39:6f:de:22:27:44:f9:3a:42:59:c8:20:f4: 41:5a:4e:36:b0:8e:25:eb:a4:a8:72:01:e6:81:56: 86:67:01:e5:bd:43:28:99:14:57:90:ff:b3:e9:f5: c6:85:d7:36:cb:05:c0:31:c6:b0:f5:b9:ec:54:ae: 83:85 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 30:06:A9:7C:68:78:A4:7A:50:52:4F:B6:75:DE:5D:88:86:AD:DD:4F Signature Algorithm: sha256WithRSAEncryption 26:2b:cf:a3:fa:4d:81:c5:a7:37:6f:4a:95:2c:ba:f4:73:cc: 57:69:8c:0e:4e:07:ad:6a:df:9e:ad:c1:62:68:82:c9:89:35: 9f:17:32:4c:98:a3:f5:45:fb:54:75:8e:ea:74:5f:ad:b4:56: c6:4c:c1:e9:f0:c5:0d:12:37:b1:83:35:16:af:de:43:e7:1b: 58:26:eb:86:c5:3c:da:88:6a:28:fa:65:cf:49:c6:ff:84:d7: dd:1e:ad:3f:01:a0:4e:ae:52:a4:98:8f:4f:94:84:04:5f:93: 43:01:39:80:69:02:46:ac:bc:b4:12:f9:b2:fd:4b:ed:50:71: cc:6d:1f:5f:5e:5d:e0:67:a2:a3:65:af:bf:e0:bd:26:8e:51: 56:d1:76:6a:e7:56:90:4e:aa:57:90:60:d0:47:38:3f:2f:9c: 38:01:e0:2d:4a:6c:9e:e5:5a:e5:33:fe:a6:46:f2:a9:03:ed: 28:2e:7a:de:e3:a8:33:3f:1c:3d:ca:eb:dd:7d:28:82:6f:2a: b1:09:37:7f:b8:2c:59:75:e0:0c:2b:5f:87:9a:24:f3:2b:88: 5e:bb:71:60:51:d2:a4:68:c2:8f:ba:8e:46:3d:14:d9:21:13: 68:23:c0:f2:05:3a:21:5e:10:20:05:59:f9:06:33:9c:c4:7d: 95:91:ce:43
|
接着就结合源码看一看
因为网上有关这部分的已经很多了,这里贴一个链接,我就不自己贴代码了
http://www.2cto.com/kf/201601/456020.html
大致说一下其中的主要流程
- apk包安装,解压得到
META-INF
下的文件
- 计算apk内所有文件的hash+base64的值,与
MANIFEST.MF
的内容进行比较,查看是否一致
- 使用
CERT.RSA
证书文件(可以是.DSA .RSA .EC
)校验签名文件CERT.SF
文件,两个文件名称必须相同
- 验证通过后,再用
CERT.SF
文件与MANIFEST.MF
文件对比,确认没被篡改。
这部分就这样被我咸鱼地搞了一天,说几点要点。
刚开始我是结合android 7.1.1的源码看的,结果源码太多,里面各种自定义的类,看着没什么耐心,而且因为是7.0以上的源码,当中还结合了v2签名的代码,看着很复杂(主要是今天太懒没耐心)。后来就看SignApk
这个工具的代码去了,路径是build/tools/signapk/src/com/android/signapk/SignApk.java
由于是一个签名工具,关键代码比较集中,比较容易理解。
刚开始的时候比较困惑,说的是用CERT.RSA
校验签名文件CERT.SF
,但是我在对比过MANIFEST.MF
和CERT.SF
后发现,这两个文件除了开头部分,后面是完全一样的,那么所谓的签名文件是什么意思?
看过SignApk的代码后,我明白了。(因为代码网上都能找到讲解,我就直接说思路了)
通过工具签名,其流程是这样的
- 得到签名的信息,公钥、私钥等(可能包含多个签名)
- 通过jar(zip/apk)的入口点(即包内的各个文件)计算出hash+base64的值,hash用的sha1还是sha256取决于安卓sdk的最低版本,结果存到
MANIFEST.MF
里
- 取出一对公钥私钥
- 对
MANIFEST.MF
计算hash+base64,把MANIFEST.MF
剩余部分复制过去,得到CERT.SF
,这就是为何后面是完全一样的了。
- 调用
writeSignatureBlock()
方法,用私钥对CERT.SF
当中生成的hash+base64进行加密,生成的密文放到证书最后生成CERT.RSA
。
- 如果还有别的签名重复3-5步。
我对两个不同的apk进行了签名,对比CERT.RSA文件可以看到就最后存在差异
通过openssl输出的证书信息都是一样的。
由此便可推出Android在验证apk签名信息时做的步骤。
接下来下一篇介绍新一代的签名方式