android安全學習之5—apk中的META-INF目錄
什麼是簽名
在android系統中,不同App之間是依靠包名、數字簽名共同來進行區分的。雖然Google建議我們用自己的域名的反寫作為包名的字首來定義包名(例如com.google.),但是這並不能做到萬無一失,我們不能單單利用包名來區分apk,所以提出了簽名的概念。顧名思義,就是在apk上打上作者的烙印。
先看如何簽名。一般,在android程式碼中,build/target/product/security/目錄下儲存了類似platform.pk8,platform.x509.pem這樣一對一對的數字證書和私鑰。而在編譯android程式碼時,從編譯log中能看到類似下面的命令:
java -jar out/host/linux-x86/framework/signapk.jar build/target/product/security/platform.x509.pem build/target/product/security/platform.pk8 before_sign.apk signed.apk
這句話的意思是,呼叫signapk.jar這個命令,用platform.x509.pem,platform.pk8這兩個檔案對before_sign.apk 進行簽名,簽名完成後的apk為signed.apk,這個signed.apk已經有了我們自己的烙印有了簽名了。
簽名後apk檔案差異
apk檔案本身就是一個archive,是個類似於zip的檔案,可以用解壓軟體直接解壓。對比簽名前後的apk,簽名後的apk中多了META-INF這個資料夾,裡面包含了三個檔案,MANIFEST.MF、CERT.SF、CERT.RSA。
那麼這三個檔案都是幹嘛的?
首先介紹個概念,下面是維基百科中對JAR的解釋中一段:
On the Java platform, a Manifest file is a specific file contained within a JAR archive.It is used to define extension and package-related data. It is a metadata file that contains name-value pairs organized in different sections. If a JAR file is intended to be used as an executable file, the manifest file specifies the main class of the application. The manifest file is named MANIFEST.MF.
java平臺中,在JAR包中有一個Manifest檔案,它用來描述JAR包的Metadata。什麼是Metadata?Metadata is “data about data”,又稱元資料、中介資料、中繼資料,為描述資料的資料,主要是描述資料屬性的資訊,用來支援如指示儲存位置、歷史資料、資源查詢、檔案紀錄等功能。而這個Manifest中包含了的都是name:value這種的類似hashMap的鍵值對。這個manifest被命名為MANIFEST.MF。
- MANIFEST.MF
apk中的這個MANIFEST.MF,列出了apk的所有檔案,以及這些檔案內容所對應的base64-encoded SHA1 雜湊值,例如,
Name: AndroidManifest.xml
SHA1-Digest: 7lLs5fV2H4ttapcDEdtJRTQOzpk=
上述表示AndroidManifest.xml這個檔案的SHA1的雜湊值為7lLs5fV2H4ttapcDEdtJRTQOzpk=
- CERT.SF
CERT.SF和MANIFEST.MF很相似,但是它描述的不是檔案內容的hash值,而是列出了MANIFEST.MF這個檔案中每條資訊的hash值,舉例會明白些:
Name: AndroidManifest.xml
SHA1-Digest-Manifest: 8CVc0D8U2qQKRD+7Fw7+Jmb6Qos=
上面這條hash值‘8CVc0D8U2qQKRD+7Fw7+Jmb6Qos=’對應的是MANIFEST.MF中下面這幾行字串的hash值,明白了嗎?hash函式的輸入是下面的字串。
Name: AndroidManifest.xml
SHA1-Digest: 7lLs5fV2H4ttapcDEdtJRTQOzpk=
注:計算SHA1-Digest-Manifest時,輸入的字串是三行,還要包括一行空白行,即’\r\n’。
- CERT.RSA
這個檔案裡面其實包含了對CERT.SF檔案的數字簽名以及簽名時所用的platform.x509.pem這個數字證書(可以參考下節中對SignApk程式的分析),
可以利用keytool和openssl工具進行讀取相關資訊,但是輸出結果不同,首先利用keytool讀取
keytool -printcert -file ./CERT.RSA
Owner: [email protected], CN=Android, OU=Android, O=Android, L=Mountain View, ST=California, C=US
Issuer: [email protected], CN=Android, OU=Android, O=Android, L=Mountain View, ST=California, C=US
Serial number: b3998086d056cffa
Valid from: Wed Apr 16 06:40:50 CST 2008 until: Sun Sep 02 06:40:50 CST 2035
Certificate fingerprints:
MD5: 8D:DB:34:2F:2D:A5:40:84:02:D7:56:8A:F2:1E:29:F9
SHA1: 27:19:6E:38:6B:87:5E:76:AD:F7:00:E7:EA:84:E4:C6:EE:E3:3D:FA
SHA256: C8:A2:E9:BC:CF:59:7C:2F:B6:DC:66:BE:E2:93:FC:13:F2:FC:47:EC:77:BC:6B:2B:0D:52:C1:1F:51:19:2A:B8
Signature algorithm name: MD5withRSA
Version: 3
Extensions:
#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 4F E4 A0 B3 DD 9C BA 29 F7 1D 72 87 C4 E7 C3 8F O......)..r.....
0010: 20 86 C2 99 ...
]
[[email protected], CN=Android, OU=Android, O=Android, L=Mountain View, ST=California, C=US]
SerialNumber: [ b3998086 d056cffa]
]
#2: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:true
PathLen:2147483647
]
#3: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 4F E4 A0 B3 DD 9C BA 29 F7 1D 72 87 C4 E7 C3 8F O......)..r.....
0010: 20 86 C2 99 ...
]
]
在上面的輸出中,有個Certificate fingerprints,開始以為這就是公鑰的fingerprints,其實並不是,而是簽名apk時的platform.x509.pem數字證書所對應的hash值。
Certificate fingerprints:
MD5: 8D:DB:34:2F:2D:A5:40:84:02:D7:56:8A:F2:1E:29:F9
SHA1: 27:19:6E:38:6B:87:5E:76:AD:F7:00:E7:EA:84:E4:C6:EE:E3:3D:FA
SHA256: C8:A2:E9:BC:CF:59:7C:2F:B6:DC:66:BE:E2:93:FC:13:F2:FC:47:EC:77:BC:6B:2B:0D:52:C1:1F:51:19:2A:B8
Signature algorithm name: MD5withRSA
Version: 3
如何確定的呢?platform.x509.pem為pem格式,首先用openssl將其轉換為DER格式,執行
openssl x509 -in platform.x509.pem -outform DER -out cert.cer
然後對結果cert.cer執行sha雜湊
sha1sum cert.cer
列印的結果即為,和上文Certificate fingerprints中的SHA1完全一樣。
27196e386b875e76adf700e7ea84e4c6eee33dfa cert.cer
那麼Certificate fingerprints中這些hash值有什麼用?
在我理解,這些hash值不是數字證書內容的一部分,而是通過數字證書檔案本身計算得出,主要作用是將apk和相應簽名時的數字證書對應起來。
那麼公鑰等數字證書的資訊在哪?我們繼續用openssl命令檢視證書相關資訊:
openssl pkcs7 -inform DER -in CERT.RSA -noout -print_certs -text
輸入結果為:
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 12941516320735154170 (0xb3998086d056cffa)
Signature Algorithm: md5WithRSAEncryption
Issuer: C=US, ST=California, L=Mountain View, O=Android, OU=Android, CN=Android/[email protected]
Validity
Not Before: Apr 15 22:40:50 2008 GMT
Not After : Sep 1 22:40:50 2035 GMT
Subject: C=US, ST=California, L=Mountain View, O=Android, OU=Android, CN=Android/[email protected]
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:9c:78:05:92:ac:0d:5d:38:1c:de:aa:65:ec:c8:
a6:00:6e:36:48:0c:6d:72:07:b1:20:11:be:50:86:
3a:ab:e2:b5:5d:00:9a:df:71:46:d6:f2:20:22:80:
c7:cd:4d:7b:db:26:24:3b:8a:80:6c:26:b3:4b:13:
75:23:a4:92:68:22:49:04:dc:01:49:3e:7c:0a:cf:
1a:05:c8:74:f6:9b:03:7b:60:30:9d:90:74:d2:42:
80:e1:6b:ad:2a:87:34:36:19:51:ea:f7:2a:48:2d:
09:b2:04:b1:87:5e:12:ac:98:c1:aa:77:3d:68:00:
b9:ea:fd:e5:6d:58:be:d8:e8:da:16:f9:a3:60:09:
9c:37:a8:34:a6:df:ed:b7:b6:b4:4a:04:9e:07:a2:
69:fc:cf:2c:54:96:f2:cf:36:d6:4d:f9:0a:3b:8d:
8f:34:a3:ba:ab:4c:f5:33:71:ab:27:71:9b:3b:a5:
87:54:ad:0c:53:fc:14:e1:db:45:d5:1e:23:4f:bb:
e9:3c:9b:a4:ed:f9:ce:54:26:13:50:ec:53:56:07:
bf:69:a2:ff:4a:a0:7d:b5:f7:ea:20:0d:09:a6:c1:
b4:9e:21:40:2f:89:ed:11:90:89:3a:ab:5a:91:80:
f1:52:e8:2f:85:a4:57:53:cf:5f:c1:90:71:c5:ee:
c8:27
Exponent: 3 (0x3)
X509v3 extensions:
X509v3 Subject Key Identifier:
4F:E4:A0:B3:DD:9C:BA:29:F7:1D:72:87:C4:E7:C3:8F:20:86:C2:99
X509v3 Authority Key Identifier:
keyid:4F:E4:A0:B3:DD:9C:BA:29:F7:1D:72:87:C4:E7:C3:8F:20:86:C2:99
DirName:/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/[email protected]
serial:B3:99:80:86:D0:56:CF:FA
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: md5WithRSAEncryption
57:25:51:b8:d9:3a:1f:73:de:0f:6d:46:9f:86:da:d6:70:14:
00:29:3c:88:a0:cd:7c:d7:78:b7:3d:af:cc:19:7f:ab:76:e6:
21:2e:56:c1:c7:61:cf:c4:2f:d7:33:de:52:c5:0a:e0:88:14:
ce:fc:0a:3b:5a:1a:43:46:05:4d:82:9f:1d:82:b4:2b:20:48:
bf:88:b5:d1:49:29:ef:85:f6:0e:dd:12:d7:2d:55:65:7e:22:
e3:e8:5d:04:c8:31:d6:13:d1:99:38:bb:89:82:24:7f:a3:21:
25:6b:a1:2d:1d:6a:8f:92:ea:1d:b1:c3:73:31:7b:a0:c0:37:
f0:d1:af:f6:45:ae:f2:24:97:9f:ba:6e:7a:14:bc:02:5c:71:
b9:81:38:ce:f3:dd:fc:05:96:17:cf:24:84:5c:f7:b4:0d:63:
82:f7:27:5e:d7:38:49:5a:b6:e5:93:1b:94:21:76:5c:49:1b:
72:fb:68:e0:80:db:db:58:c2:02:9d:34:7c:8b:32:8c:e4:3e:
f6:a8:b1:55:33:ed:fb:e9:89:bd:6a:48:dd:4b:20:2e:da:94:
c6:ab:8d:d5:b8:39:92:03:da:ae:2e:d4:46:23:2e:4f:e9:bd:
96:13:94:c6:30:0e:51:38:e3:cf:d2:85:e6:e4:e4:83:53:8c:
b8:b1:b3:57
從上面我們看到 Subject Public Key Info項,猜想這應該就是公鑰的相關資訊了吧。
下面是RFC3280描述的Certificate的資料結構
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING }
TBSCertificate ::= SEQUENCE {
version [0] EXPLICIT Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,
issuer Name,
validity Validity,
subject Name,
subjectPublicKeyInfo SubjectPublicKeyInfo,
issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
-- If present, version MUST be v2 or v3
subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
-- If present, version MUST be v2 or v3
extensions [3] EXPLICIT Extensions OPTIONAL
-- If present, version MUST be v3
}
Version ::= INTEGER { v1(0), v2(1), v3(2) }
CertificateSerialNumber ::= INTEGER
Validity ::= SEQUENCE {
notBefore Time,
notAfter Time }
Time ::= CHOICE {
utcTime UTCTime,
generalTime GeneralizedTime }
UniqueIdentifier ::= BIT STRING
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING }
Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
Housley, et. al. Standards Track [Page 15]
RFC 3280 Internet X.509 Public Key Infrastructure April 2002
Extension ::= SEQUENCE {
extnID OBJECT IDENTIFIER,
critical BOOLEAN DEFAULT FALSE,
extnValue OCTET STRING }
上面結構,完整的表現了一個certificate包含的所有資訊,首先結構中是一個tbsCertificate 結構體,
tbsCertificate TBSCertificate
然後是對這個結構體,即tbsCertificate進行簽名的演算法和值,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING
如果簽名的演算法為SHA1 with RSA,則計算出一個SHA-1雜湊值,然後利用apk的簽發者的RSA private key對這個雜湊值進行簽名。當然這個簽名和上述keytool展示的fingerprint沒任何關係,因為它計算的只是TBSCertificate這部分的雜湊值。
而在TBSCertificate結構中,我們看到了SubjectPublicKeyInfo 這個結構,它就代表公鑰相關的演算法以及公鑰值。
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING }
同時利用以下命令讀取簽名時的數字證書,和上述CERT.RSA資訊進行比對:
openssl x509 -inform pem -in platform.x509.pem -noout -text
結果如下,發現完全一樣,也證實了上面的說法,CERT.RSA中包含了簽名時的數字證書。
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 12941516320735154170 (0xb3998086d056cffa)
Signature Algorithm: md5WithRSAEncryption
Issuer: C=US, ST=California, L=Mountain View, O=Android, OU=Android, CN=Android/[email protected]
Validity
Not Before: Apr 15 22:40:50 2008 GMT
Not After : Sep 1 22:40:50 2035 GMT
Subject: C=US, ST=California, L=Mountain View, O=Android, OU=Android, CN=Android/[email protected]
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:9c:78:05:92:ac:0d:5d:38:1c:de:aa:65:ec:c8:
a6:00:6e:36:48:0c:6d:72:07:b1:20:11:be:50:86:
3a:ab:e2:b5:5d:00:9a:df:71:46:d6:f2:20:22:80:
c7:cd:4d:7b:db:26:24:3b:8a:80:6c:26:b3:4b:13:
75:23:a4:92:68:22:49:04:dc:01:49:3e:7c:0a:cf:
1a:05:c8:74:f6:9b:03:7b:60:30:9d:90:74:d2:42:
80:e1:6b:ad:2a:87:34:36:19:51:ea:f7:2a:48:2d:
09:b2:04:b1:87:5e:12:ac:98:c1:aa:77:3d:68:00:
b9:ea:fd:e5:6d:58:be:d8:e8:da:16:f9:a3:60:09:
9c:37:a8:34:a6:df:ed:b7:b6:b4:4a:04:9e:07:a2:
69:fc:cf:2c:54:96:f2:cf:36:d6:4d:f9:0a:3b:8d:
8f:34:a3:ba:ab:4c:f5:33:71:ab:27:71:9b:3b:a5:
87:54:ad:0c:53:fc:14:e1:db:45:d5:1e:23:4f:bb:
e9:3c:9b:a4:ed:f9:ce:54:26:13:50:ec:53:56:07:
bf:69:a2:ff:4a:a0:7d:b5:f7:ea:20:0d:09:a6:c1:
b4:9e:21:40:2f:89:ed:11:90:89:3a:ab:5a:91:80:
f1:52:e8:2f:85:a4:57:53:cf:5f:c1:90:71:c5:ee:
c8:27
Exponent: 3 (0x3)
X509v3 extensions:
X509v3 Subject Key Identifier:
4F:E4:A0:B3:DD:9C:BA:29:F7:1D:72:87:C4:E7:C3:8F:20:86:C2:99
X509v3 Authority Key Identifier:
keyid:4F:E4:A0:B3:DD:9C:BA:29:F7:1D:72:87:C4:E7:C3:8F:20:86:C2:99
DirName:/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/[email protected]
serial:B3:99:80:86:D0:56:CF:FA
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: md5WithRSAEncryption
57:25:51:b8:d9:3a:1f:73:de:0f:6d:46:9f:86:da:d6:70:14:
00:29:3c:88:a0:cd:7c:d7:78:b7:3d:af:cc:19:7f:ab:76:e6:
21:2e:56:c1:c7:61:cf:c4:2f:d7:33:de:52:c5:0a:e0:88:14:
ce:fc:0a:3b:5a:1a:43:46:05:4d:82:9f:1d:82:b4:2b:20:48:
bf:88:b5:d1:49:29:ef:85:f6:0e:dd:12:d7:2d:55:65:7e:22:
e3:e8:5d:04:c8:31:d6:13:d1:99:38:bb:89:82:24:7f:a3:21:
25:6b:a1:2d:1d:6a:8f:92:ea:1d:b1:c3:73:31:7b:a0:c0:37:
f0:d1:af:f6:45:ae:f2:24:97:9f:ba:6e:7a:14:bc:02:5c:71:
b9:81:38:ce:f3:dd:fc:05:96:17:cf:24:84:5c:f7:b4:0d:63:
82:f7:27:5e:d7:38:49:5a:b6:e5:93:1b:94:21:76:5c:49:1b:
72:fb:68:e0:80:db:db:58:c2:02:9d:34:7c:8b:32:8c:e4:3e:
f6:a8:b1:55:33:ed:fb:e9:89:bd:6a:48:dd:4b:20:2e:da:94:
c6:ab:8d:d5:b8:39:92:03:da:ae:2e:d4:46:23:2e:4f:e9:bd:
96:13:94:c6:30:0e:51:38:e3:cf:d2:85:e6:e4:e4:83:53:8c:
b8:b1:b3:57
上面說CERT.RSA中除了有數字證書,還有CERT.SF的數字簽名,這個資訊咋獲取?雖然取出來沒啥大用,只是在安裝apk時對簽名驗證的時候才用到,但是對我們理解原理有幫助。android在安裝apk的時候肯定要把這些資訊讀取出來,那麼我們可以通過android的packagemanager獲取。
getPackageManager().getPackageInfo(packageName,PackageManager.GET_SIGNATURES).signatures
下面是一個例子,利用CERT.RSA獲取一個數字證書的例項:
InputStream in = new FileInputStream("CERT.RSA");
CertificateFactory factory = CertificateFactory.getInstance("X.509")
X509Certificate cert = (X509Certificate) factory.generateCertificate(in);
但是在實際使用時並不這麼使用,因為apk一般是一個jar檔案,可以利用JarFile來解析,然後迴圈 JarEntry,呼叫getCertifiates()方法獲取數字證書,然後cast 為X509Certifiate,就能獲得certificate的所有資訊,而不用去先解壓apk檔案獲取到CERT.RSA。