<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>


       To build a better world !

    Android APK 簽名比對


    轉(zhuǎn)載請注明出處:http://www.tkk7.com/zh-weir/archive/2011/07/19/354663.html

    Android APK 簽名比對

    發(fā)布過Android應(yīng)用的朋友們應(yīng)該都知道,Android APK的發(fā)布是需要簽名的。簽名機(jī)制在Android應(yīng)用和框架中有著十分重要的作用。

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

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


    Android簽名機(jī)制

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

    APK Crack一文中,我們了解到,要簽名一個沒有簽名過的APK,可以使用一個叫作Auto-sign的工具。Auto-sign工具實(shí)際運(yùn)行的是一個叫做Sign.bat的批處理命令。用文本編輯器打開這個批處理文件,我們可以發(fā)現(xiàn),實(shí)現(xiàn)簽名功能的命令主要是這一行命令:

        java -jar signapk.jar testkey.x509.pem testkey.pk8 update.apk update_signed.apk

        這條命令的意義是:通過signapk.jar這個可執(zhí)行jar包,以“
    testkey.x509.pem”這個公鑰文件和“testkey.pk8”這個私鑰文件對“update.apk”進(jìn)行簽名,簽名后的文件保存為“update_signed.apk”。

        對于此處所使用的私鑰和公鑰的生成方式,這里就不做進(jìn)一步介紹了。這方面的資料大家可以找到很多。我們這里要講的是signapk.jar到底做了什么。

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

    對比一個沒有簽名的APK和一個簽名好的APK,我們會發(fā)現(xiàn),簽名好的APK包中多了一個叫做META-INF的文件夾。里面有三個文件,分別名為MANIFEST.MFCERT.SFCERT.RSA。signapk.jar就是生成了這幾個文件(其他文件沒有任何改變。因此我們可以很容易去掉原有簽名信息)。

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


    1、 
    生成MANIFEST.MF文件:

    程序遍歷update.apk包中的所有文件(entry),對非文件夾非簽名文件的文件,逐個生成SHA1的數(shù)字簽名信息,再用Base64進(jìn)行編碼。具體代碼見這個方法:

        private static Manifest addDigestsToManifest(JarFile jar)

    關(guān)鍵代碼如下:

     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文件。關(guān)鍵代碼如下:

    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數(shù)字簽名。簡單地說,它就是一種安全哈希算法,類似于MD5算法。它把任意長度的輸入,通過散列算法變成固定長度的輸出(這里我們稱作“摘要信息”)。你不能僅通過這個摘要信息復(fù)原原來的信息。另外,它保證不同信息的摘要信息彼此不同。因此,如果你改變了apk包中的文件,那么在apk安裝校驗(yàn)時,改變后的文件摘要信息與MANIFEST.MF的檢驗(yàn)信息不同,于是程序就不能成功安裝。


    2、 
    生成CERT.SF文件:

    對前一步生成的Manifest,使用SHA1-RSA算法,用私鑰進(jìn)行簽名。關(guān)鍵代碼如下:

    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算法對摘要信息進(jìn)行加密。在安裝時只能使用公鑰才能解密它。解密之后,將它與未加密的摘要信息進(jìn)行對比,如果相符,則表明內(nèi)容沒有被異常修改。


    3、 
    生成CERT.RSA文件:

    生成MANIFEST.MF沒有使用密鑰信息,生成CERT.SF文件使用了私鑰文件。那么我們可以很容易猜測到,CERT.RSA文件的生成肯定和公鑰相關(guān)。

    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簽名機(jī)制其實(shí)是對APK包完整性和發(fā)布機(jī)構(gòu)唯一性的一種校驗(yàn)機(jī)制。

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

    3、 APK包加密的公鑰就打包在APK包內(nèi),且不同的私鑰對應(yīng)不同的公鑰。換句話言之,不同的私鑰簽名的APK公鑰也必不相同。所以我們可以根據(jù)公鑰的對比,來判斷私鑰是否一致。


    APK簽名比對的實(shí)現(xiàn)方式

        好了,通過Android簽名機(jī)制的分析,我們從理論上證明了通過APK公鑰的比對能判斷一個APK的發(fā)布機(jī)構(gòu)。并且這個發(fā)布機(jī)構(gòu)是很難偽裝的,我們暫時可以認(rèn)為是不可偽裝的。

        有了理論基礎(chǔ)后,我們就可以開始實(shí)踐了。那么如何獲取到APK文件的公鑰信息呢?因?yàn)锳ndroid系統(tǒng)安裝程序肯定會獲取APK信息進(jìn)行比對,所以我們可以通過Android源碼獲得一些思路和幫助。

        源碼中有一個隱藏的類用于APK包的解析。這個類叫PackageParser,路徑為frameworks\base\core\java\android\content\pm\PackageParser.java。當(dāng)我們需要獲取APK包的相關(guān)信息時,可以直接使用這個類,下面代碼就是一個例子函數(shù):

     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, 00);
    16     }

        其中參數(shù)archiveFilePath指定APK文件路徑;flags需設(shè)置PackageManager.GET_SIGNATURES位,以保證返回證書簽名信息。

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

        緊接著,我們可以通過packageInfo.signatures來訪問到APK的簽名信息。還需要說明的是 Android中Signature和Java中Certificate的對應(yīng)關(guān)系。它們的關(guān)系如下面代碼所示:

    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方法獲取公鑰信息。所以比對簽名證書本質(zhì)上就是比對公鑰信息。

        OK,獲取到APK簽名證書之后,就剩下比對了。這個簡單,功能函數(shù)如下所示:

     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簽名比對的應(yīng)用場景

        經(jīng)過以上的論述,想必大家已經(jīng)明白簽名比對的原理和我的實(shí)現(xiàn)方式了。那么什么時候什么情況適合使用簽名對比來保障Android APK的軟件安全呢?

        個人認(rèn)為主要有以下三種場景:

    1、 程序自檢測。在程序運(yùn)行時,自我進(jìn)行簽名比對。比對樣本可以存放在APK包內(nèi),也可存放于云端。缺點(diǎn)是程序被破解時,自檢測功能同樣可能遭到破壞,使其失效。

    2、 可信賴的第三方檢測。由可信賴的第三方程序負(fù)責(zé)APK的軟件安全問題。對比樣本由第三方收集,放在云端。這種方式適用于殺毒安全軟件或者APP Market之類的軟件下載市場。缺點(diǎn)是需要聯(lián)網(wǎng)檢測,在無網(wǎng)絡(luò)情況下無法實(shí)現(xiàn)功能。(不可能把大量的簽名數(shù)據(jù)放在移動設(shè)備本地)。

    3、 系統(tǒng)限定安裝。這就涉及到改Android系統(tǒng)了。限定僅能安裝某些證書的APK。軟件發(fā)布商需要向系統(tǒng)發(fā)布上申請證書。如果發(fā)現(xiàn)問題,能追蹤到是哪個軟件發(fā)布商的責(zé)任。適用于系統(tǒng)提供商或者終端產(chǎn)品生產(chǎn)商。缺點(diǎn)是過于封閉,不利于系統(tǒng)的開放性。

    以上三種場景,雖然各有缺點(diǎn),但缺點(diǎn)并不是不能克服的。例如,我們可以考慮程序自檢測的功能用native method的方法實(shí)現(xiàn)等等。軟件安全是一個復(fù)雜的課題,往往需要多種技術(shù)聯(lián)合使用,才能更好的保障軟件不被惡意破壞。


    參考資料

    Android源碼

    《Android中的簽名機(jī)制》


    轉(zhuǎn)載請注明出處:http://www.tkk7.com/zh-weir/archive/2011/07/19/354663.html

    posted on 2011-07-19 23:35 zh.weir 閱讀(49450) 評論(6)  編輯  收藏 所屬分類: Android軟件安全

    評論

    # re: Android APK 簽名比對 2012-05-21 16:58 hardPass

    樓主您好!
    我最近在開發(fā)中遇到一個與SHA1數(shù)字簽名有關(guān)的問題,希望您能幫我解惑。
    我的project中有個圖片,其在apk中的SHA1數(shù)字簽名總是不正確。
    其在apk中的數(shù)字簽名是4ss2KZ3FzkmfE6HAAsVu0aJKx1U=,
    但是我用我的代碼生成的卻是sjmKOs4BYDXg7COdeTc8tIfPBR0=
    我生成數(shù)字簽名的代碼大致如下:
    [code="java"]
    public static void main(String[] args) throws NoSuchAlgorithmException, Exception {
    MessageDigest md = MessageDigest.getInstance("sha-1");
    FileInputStream in = new FileInputStream("./ic_launcher.png");
    int bytes = 0;
    while ((bytes = in.read()) != -1) {
    md.update((byte)bytes);
    }
    in.close();
    byte[] thedigest = md.digest();
    System.out.println(Base64Encoder.encode(thedigest));
    }
    [/code]
    同時,無法在avd(android 2.2)中安裝含有該圖片的apk。
    在實(shí)際手機(jī)中的情況更怪異,有的手機(jī)能夠正常安裝,有的則是Application not installed.
    請樓主能抽空幫我看下問題,不甚感激!
    另外我的問題在這邊有更詳細(xì)的描述,并且還有相關(guān)附件
    http://www.iteye.com/topic/1123803  回復(fù)  更多評論   

    # re: Android APK 簽名比對 2012-11-29 23:38 r_y

    私鑰是keystore,然后可以通過代碼獲得與私鑰對應(yīng)的公鑰。
    問題:怎么直接通過私鑰獲得公鑰?有工具嗎?  回復(fù)  更多評論   

    # re: Android APK 簽名比對 2013-02-04 17:26 sv_gn

    應(yīng)用在運(yùn)行中查詢自己的簽名信息,可以做到嗎?  回復(fù)  更多評論   

    # re: Android APK 簽名比對 2013-03-12 14:13 andye

    為什么要傳進(jìn)去apk的文件路徑啊?這個很困難的。誰知道用戶會下載完放到哪里啊
      回復(fù)  更多評論   

    # re: Android APK 簽名比對 2013-07-05 11:15 eieihihi

    很好很強(qiáng)大 , 有空按你說的試試@~~  回復(fù)  更多評論   

    # re: Android APK 簽名比對 2013-08-13 23:51 freerabbit

    作者中對CERT.SF和CERT.RSA的分析似乎并不太準(zhǔn)確,請看看http://blog.csdn.net/u011688064/article/details/9956785  回復(fù)  更多評論   

    公告

    大家好!歡迎光臨我的 Android 技術(shù)博客!



    本博客旨在交流與 Android 操作系統(tǒng)相關(guān)的各種技術(shù)及信息。

    博客內(nèi)的文章會盡量以開源的形式提供給大家,希望我們能相互交流,共同提高!

    有不足之處,請不吝賜教!

    我的郵箱:zh.weir@gmail.com
    我的新浪微博:@囧虎張建偉

     

    導(dǎo)航

    <2011年7月>
    262728293012
    3456789
    10111213141516
    17181920212223
    24252627282930
    31123456

    統(tǒng)計

    留言簿(19)

    隨筆分類(24)

    隨筆檔案(18)

    文章檔案(1)

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 鲁啊鲁在线视频免费播放| 免费国产a理论片| 亚洲午夜爱爱香蕉片| a毛片基地免费全部视频| a毛片在线看片免费| 特级毛片A级毛片100免费播放 | 一级女人18片毛片免费视频| 亚洲国产成a人v在线| 亚洲va中文字幕无码久久不卡| 亚洲国产精品国产自在在线| 毛片网站免费在线观看| 在线成人爽a毛片免费软件| 免费看无码特级毛片| 无遮挡呻吟娇喘视频免费播放| 亚洲日韩国产欧美一区二区三区| 亚洲视频一区在线| 久久久亚洲欧洲日产国码是AV| 久久久久亚洲AV片无码| 在线观看午夜亚洲一区| 亚洲综合激情另类专区| 亚洲国产精品综合久久一线 | 一级毛片免费毛片毛片| 鲁啊鲁在线视频免费播放| 黑人粗长大战亚洲女2021国产精品成人免费视频 | 亚洲午夜福利在线观看| 亚洲综合区小说区激情区| 亚洲女人被黑人巨大进入| 免费人成网站在线高清| 免费一看一级毛片全播放| 日韩一区二区三区免费体验| 免费看的黄色大片| 免费的涩涩视频在线播放 | 日日躁狠狠躁狠狠爱免费视频| 噜噜噜亚洲色成人网站| 老司机午夜性生免费福利| 男人和女人高潮免费网站| 久香草视频在线观看免费| 亚洲精品视频免费观看| 久久精品免费大片国产大片| 特级做A爰片毛片免费看无码| 一区二区三区无码视频免费福利|