Android APK簽名對比及說明)

來源:互聯網
上載者:User

發布過Android應用的朋友們應該都知道,Android APK的發布是需要簽名的。簽名機制在Android應用和架構中有著十分重要的作用。

例如,Android系統禁止更新安裝簽名不一致的APK;如果應用需要使用system許可權,必須保證APK簽名與Framework簽名一致,等等。在《APK Crack》一文中,我們瞭解到,要破解一個APK,必然需要重新對APK進行簽名。而這個簽名,一般情況無法再與APK原先的簽名保持一致。(除非APK原作者的私密金鑰泄漏,那已經是另一個層次的軟體安全問題了。)

簡單地說,簽名機制標明了APK的發行機構。因此,站在軟體安全的角度,我們就可以通過比對APK的簽名情況,判斷此APK是否由“官方”發行,而不是被破解篡改過重新簽名打包的“盜版軟體”。

Android簽名機制
    為了說明APK簽名比對對軟體安全的有效性,我們有必要瞭解一下Android APK的簽名機制。為了更易於大家理解,我們從Auto-Sign工具的一條批處理命令說起。

在《APK Crack》一文中,我們瞭解到,要簽名一個沒有簽名過的APK,可以使用一個叫作Auto-sign的工具。Auto-sign工具實際啟動並執行是一個叫做Sign.bat的批處理命令。用文字編輯器開啟這個批次檔,我們可以發現,實現簽名功能的命令主要是這一行命令:

 

    java -jar signapk.jar testkey.x509.pem testkey.pk8 update.apk update_signed.apk
    這條命令的意義是:通過signapk.jar這個可執行jar包,以“testkey.x509.pem”這個公開金鑰檔案和“testkey.pk8”這個私密金鑰檔案對“update.apk”進行簽名,簽名後的檔案儲存為“update_signed.apk”。 
    對於此處所使用的私密金鑰和公開金鑰的產生方式,這裡就不做進一步介紹了。這方面的資料大家可以找到很多。我們這裡要講的是signapk.jar到底做了什麼。

    signapk.jar是Android源碼包中的一個簽名工具。由於Android是個開源項目,所以,很高興地,我們可以直接找到signapk.jar的源碼!路徑為/build/tools/signapk/SignApk.java。

對比一個沒有簽名的APK和一個簽名好的APK,我們會發現,簽名好的APK包中多了一個叫做META-INF的檔案夾。裡面有三個檔案,分別名為MANIFEST.MF、CERT.SF和CERT.RSA。signapk.jar就是產生了這幾個檔案(其他檔案沒有任何改變。因此我們可以很容易去掉原有簽名資訊)。

    通過閱讀signapk源碼,我們可以理清簽名APK包的整個過程。

 

1、 產生MANIFEST.MF檔案:

程式遍曆update.apk包中的所有檔案(entry),對非檔案夾非簽名檔案的檔案,逐個產生SHA1的數位簽章資訊,再用Base64進行編碼。具體代碼見這個方法:

 

    private static Manifest addDigestsToManifest(JarFile jar)
關鍵代碼如下:

 1     for (JarEntry entry: byName.values()) {
 2         String name = entry.getName();
 3         if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) &&
 4             !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) &&
 5                (stripPattern == null ||!stripPattern.matcher(name).matches())) {
 6                 InputStream data = jar.getInputStream(entry);
 7                 while ((num = data.read(buffer)) > 0) {
 8                     md.update(buffer, 0, num);
 9                 }
10                 Attributes attr = null;
11                 if (input != null) attr = input.getAttributes(name);
12                 attr = attr != null ? new Attributes(attr) : new Attributes();
13                 attr.putValue("SHA1-Digest", base64.encode(md.digest()));
14                 output.getEntries().put(name, attr);
15           }
16     }

    之後將產生的簽名寫入MANIFEST.MF檔案。關鍵代碼如下:

 

1     Manifest manifest = addDigestsToManifest(inputJar);
2     je = new JarEntry(JarFile.MANIFEST_NAME);
3     je.setTime(timestamp);
4     outputJar.putNextEntry(je);
5     manifest.write(outputJar);
    這裡簡單介紹下SHA1數位簽章。簡 單地說,它就是一種安全雜湊演算法,類似於MD5演算法。它把任意長度的輸入,通過散列演算法變成固定長度的輸出(這裡我們稱作“摘要資訊”)。你不能僅通過這 個摘要資訊複原原來的資訊。另外,它保證不同資訊的摘要資訊彼此不同。因此,如果你改變了apk包中的檔案,那麼在apk安裝校正時,改變後的檔案摘要信 息與MANIFEST.MF的檢驗資訊不同,於是程式就不能成功安裝。

2、 產生CERT.SF檔案:

對前一步產生的Manifest,使用SHA1-RSA演算法,用私密金鑰進行簽名。關鍵代碼如下:

1     Signature signature = Signature.getInstance("SHA1withRSA");
2     signature.initSign(privateKey);
3     je = new JarEntry(CERT_SF_NAME);
4     je.setTime(timestamp);
5     outputJar.putNextEntry(je);
6     writeSignatureFile(manifest,
7     new SignatureOutputStream(outputJar, signature));
    RSA是一種非對稱式加密演算法。用私密金鑰通過RSA演算法對摘要資訊進行加密。在安裝時只能使用公開金鑰才能解密它。解密之後,將它與未加密的摘要資訊進行對比,如果相符,則表明內容沒有被異常修改。

3、 產生CERT.RSA檔案:

產生MANIFEST.MF沒有使用密鑰資訊,產生CERT.SF檔案使用了私密金鑰檔案。那麼我們可以很容易猜測到,CERT.RSA檔案的產生肯定和公開金鑰相關。

CERT.RSA檔案中儲存了公開金鑰、所採用的密碼編譯演算法等資訊。核心代碼如下:

 

1     je = new JarEntry(CERT_RSA_NAME);
2     je.setTime(timestamp);
3     outputJar.putNextEntry(je);
4     writeSignatureBlock(signature, publicKey, outputJar);
    其中writeSignatureBlock的代碼如下:

 

 1     private static void writeSignatureBlock(
 2         Signature signature, X509Certificate publicKey, OutputStream out)
 3             throws IOException, GeneralSecurityException {
 4                 SignerInfo signerInfo = new SignerInfo(
 5                 new X500Name(publicKey.getIssuerX500Principal().getName()),
 6                 publicKey.getSerialNumber(),
 7                 AlgorithmId.get("SHA1"),
 8                 AlgorithmId.get("RSA"),
 9                 signature.sign());
10 
11         PKCS7 pkcs7 = new PKCS7(
12             new AlgorithmId[] { AlgorithmId.get("SHA1") },
13             new ContentInfo(ContentInfo.DATA_OID, null),
14             new X509Certificate[] { publicKey },
15             new SignerInfo[] { signerInfo });
16 
17         pkcs7.encodeSignedData(out);
18     }
    好了,分析完APK包的簽名流程,我們可以清楚地意識到:

1、 Android簽名機制其實是對APK包完整性和發布機構唯一性的一種校正機制。

2、 Android簽名機制不能阻止APK包被修改,但修改後的再簽名無法與原先的簽名保持一致。(擁有私密金鑰的情況除外)。

3、 APK包加密的公開金鑰就打包在APK包內,且不同的私密金鑰對應不同的公開金鑰。換句話言之,不同的私密金鑰簽名的APK公開金鑰也必不相同。所以我們可以根據公開金鑰的對比,來判斷私密金鑰是否一致。

APK簽名比對的實現方式
    好了,通過Android簽名機制的分析,我們從理論上證明了通過APK公開金鑰的比對能判斷一個APK的發布機構。並且這個發布機構是很難偽裝的,我們暫時可以認為是不可偽裝的。

    有了理論基礎後,我們就可以開始實踐了。那麼如何擷取到APK檔案的公開金鑰資訊呢?因為Android系統安裝程式肯定會擷取APK資訊進行比對,所以我們可以通過Android源碼獲得一些思路和協助。

    源碼中有一個隱藏的類用於APK包的解析。這個類叫PackageParser,路徑為frameworks\base\core\java\android\content\pm\PackageParser.java。當我們需要擷取APK包的相關資訊時,可以直接使用這個類,下面代碼就是一個例子函數:

 

 1     private PackageInfo parsePackage(String archiveFilePath, int flags){
 2         
 3         PackageParser packageParser = new PackageParser(archiveFilePath);
 4         DisplayMetrics metrics = new DisplayMetrics();
 5         metrics.setToDefaults();
 6         final File sourceFile = new File(archiveFilePath);
 7         PackageParser.Package pkg = packageParser.parsePackage(
 8                 sourceFile, archiveFilePath, metrics, 0);
 9         if (pkg == null) {
10             return null;
11         }
12         
13         packageParser.collectCertificates(pkg, 0); 
14         
15         return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0);
16     }
    其中參數archiveFilePath指定APK檔案路徑;flags需設定PackageManager.GET_SIGNATURES位,以保證返回認證簽名資訊。

    具體如何通過PackageParser擷取簽名資訊在此處不做詳述,具體代碼請參考PackageParser中的public boolean collectCertificates(Package pkg, int flags)和private Certificate[] loadCertificates(JarFile jarFile, JarEntry je, byte[] readBuffer)方法。至於如何在Android應用開發中使用隱藏的類及方法,可以參看我的這篇文章:《Android應用開發中如何使用隱藏API》。

    緊接著,我們就可以通過packageInfo.signatures來訪問到APK的簽名資訊。還需要說明的是 Android中Signature和Java中Certificate的對應關係。它們的關係如下面代碼所示:

 

1     pkg.mSignatures = new Signature[certs.length];
2     for (int i=0; i<N; i++) {
3         pkg.mSignatures[i] = new Signature(
4         certs[i].getEncoded());
5     }
    也就是說signature = new Signature(certificate.getEncoded()); certificate認證中包含了公開金鑰和認證的其他基本資料。公開金鑰不同,認證肯定互不相同。我們可以通過certificate的getPublicKey方法擷取公開金鑰資訊。所以比對簽署憑證本質上就是比對公開金鑰資訊。

    OK,擷取到APK簽署憑證之後,就剩下比對了。這個簡單,功能函數如下所示:

 1     private boolean IsSignaturesSame(Signature[] s1, Signature[] s2) {
 2             if (s1 == null) {
 3                 return false;
 4             }
 5             if (s2 == null) {
 6                 return false;
 7             }
 8             HashSet<Signature> set1 = new HashSet<Signature>();
 9             for (Signature sig : s1) {
10                 set1.add(sig);
11             }
12             HashSet<Signature> set2 = new HashSet<Signature>();
13             for (Signature sig : s2) {
14                 set2.add(sig);
15             }
16             // Make sure s2 contains all signatures in s1.
17             if (set1.equals(set2)) {
18                 return true;
19             }
20             return false;
21         }

APK簽名比對的應用情境

    經過以上的論述,想必大家已經明白簽名比對的原理和我的實現方式了。那麼什麼時候什麼情況適合使用簽名對比來保障Android APK的軟體安全呢?

    個人認為主要有以下三種情境:

1、 程式自檢測。在程式運行時,自我進行簽名比對。比對樣本可以存放在APK包內,也可存放於雲端。缺點是程式被破解時,自檢測功能同樣可能遭到破壞,使其失效。

2、 可 信賴的第三方檢測。由可信賴的第三方程式負責APK的軟體安全問題。對比樣本由第三方收集,放在雲端。這種方式適用於殺毒安全軟體或者 APP Market之類的軟體下載市場。缺點是需要連網檢測,在無網路情況下無法實現功能。(不可能把大量的簽名資料放在行動裝置本地)。

3、 系統限定安裝。這就涉及到改Android系統了。限定僅能安裝某些認證的APK。軟體發布商需要向系統發布上申請認證。如果發現問題,能追蹤到是哪個軟體發布商的責任。適用於系統供應商或者終端產品生產商。缺點是過於封閉,不利於系統的開放性。

以上三種情境,雖然各有缺點,但缺點並不是不能克服的。例如,我們可以考慮程式自檢測的功能用native method的方法實現等等。軟體安全是一個複雜的課題,往往需要多種技術聯合使用,才能更好的保障軟體不被惡意破壞

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.