1. 程式人生 > >android安全學習之5—apk中的META-INF目錄

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。