1. 程式人生 > >Android簽名機制之---簽名過程具體解釋

Android簽名機制之---簽名過程具體解釋

先來 文件內容 rfi eating general class stat ket 寫文章

一、前言

又是過了好長時間,沒寫文章的雙手都有點難受了。今天是聖誕節,還是得上班。由於前幾天有一個之前的同事,在申請微信SDK的時候,遇到簽名的問題,問了我一下,結果把我難倒了。。我說Android中的簽名大家都會熟悉的,就是為了安全,不讓別人改動你的apk,可是我們真正的有了解多少呢?所以準備兩篇文章好好介紹一下Android中簽名機制。

在說道Android簽名之前,我們須要了解的幾個知識點

1、數據摘要(數據指紋)、簽名文件,證書文件

2、jarsign工具簽名和signapk工具簽名

3、keystore文件和pk8文件,x509.pem文件的關系

4、怎樣手動的簽名apk

上面介紹的四個知識點,就是今天介紹的核心,我們來一一看這些問題。


二、準備知識

首先來看一下數據摘要。簽名文件。證書文件的知識點

1、數據摘要

這個知識點非常好理解,百度百科就可以,事實上他也是一種算法,就是對一個數據源進行一個算法之後得到一個摘要,也叫作數據指紋。不同的數據源。數據指紋肯定不一樣。就和人一樣。

消息摘要算法(Message Digest Algorithm)是一種能產生特殊輸出格式的算法,其原理是依據一定的運算規則對原始數據進行某種形式的信息提取。被提取出的信息就被稱作原始數據的消息摘要。


著名的摘要算法有RSA公司的MD5算法和SHA-1算法及其大量的變體。
消息摘要的主要特點有:
1)不管輸入的消息有多長,計算出來的消息摘要的長度總是固定的。比如應用MD5算法摘要的消息有128個比特位,用SHA-1算法摘要的消息終於有160比特位的輸出。
2)一般來說(不考慮碰撞的情況下),僅僅要輸入的原始數據不同,對其進行摘要以後產生的消息摘要也必不同樣。即使原始數據稍有改變,輸出的消息摘要便全然不同。可是,同樣的輸入必會產生同樣的輸出。
3)具有不可逆性,即僅僅能進行正向的信息摘要,而無法從摘要中恢復出不論什麽的原始消息。



2、簽名文件和證書

簽名文件和證書是成對出現了,二者不可分離,並且我們後面通過源代碼能夠看到,這兩個文件的名字也是一樣的,僅僅是後綴名不一樣。

事實上數字簽名的概念非常easy。

大家知道,要確保可靠通信,必須要解決兩個問題:首先,要確定消息的來源確實是其申明的那個人;其次,要保證信息在傳遞的過程中不被第三方篡改,即使被篡改了。也能夠發覺出來。


所謂數字簽名,就是為了解決這兩個問題而產生的,它是對前面提到的非對稱加密技術與數字摘要技術的一個具體的應用。
對於消息的發送者來說,先要生成一對公私鑰對。將公鑰給消息的接收者。


假設消息的發送者有一天想給消息接收者發消息,在發送的信息中,除了要包括原始的消息外。還要加上另外一段消息。這段消息通過例如以下兩步生成:
1)對要發送的原始消息提取消息摘要;
2)對提取的信息摘要用自己的私鑰加密。


通過這兩步得出的消息。就是所謂的原始信息的數字簽名。
而對於信息的接收者來說,他所收到的信息,將包括兩個部分,一是原始的消息內容。二是附加的那段數字簽名。

他將通過以下三步來驗證消息的真偽:
1)對原始消息部分提取消息摘要,註意這裏使用的消息摘要算法要和發送方使用的一致。
2)對附加上的那段數字簽名,使用預先得到的公鑰解密;
3)比較前兩步所得到的兩段消息是否一致。假設一致,則表明消息確實是期望的發送者發的。且內容沒有被篡改過。相反。假設不一致。則表明傳送的過程中一定出了問題,消息不可信。
通過這樣的所謂的數字簽名技術,確實能夠有效解決可靠通信的問題。假設原始消息在傳送的過程中被篡改了,那麽在消息接收者那裏。對被篡改的消息提取的摘要肯定和原始的不一樣。

並且,由於篡改者沒有消息發送方的私鑰,即使他能夠又一次算出被篡改消息的摘要。也不能偽造出數字簽名。
所以。綜上所述。數字簽名事實上就是僅僅有信息的發送者才幹產生的別人無法偽造的一段數字串。這段數字串同一時候也是對信息的發送者發送信息真實性的一個有效證明。
不知道大家有沒有註意,前面講的這樣的數字簽名方法,有一個前提,就是消息的接收者必須要事先得到正確的公鑰。假設一開始公鑰就被別人篡改了,那壞人就會被你當成好人,而真正的消息發送者給你發的消息會被你視作無效的。並且,非常多時候根本就不具備事先溝通公鑰的信息通道。

那麽怎樣保證公鑰的安全可信呢?這就要靠數字證書來攻克了。
所謂數字證書,一般包括以下一些內容:
證書的公布機構(Issuer)
證書的有效期(Validity)
消息發送方的公鑰
證書全部者(Subject)
數字簽名所使用的算法
數字簽名
能夠看出,數字證書事實上也用到了數字簽名技術。僅僅只是要簽名的內容是消息發送方的公鑰,以及一些其它信息。

但與普通數字簽名不同的是,數字證書中簽名者不是隨隨便便一個普通的機構,而是要有一定公信力的機構。

這就好像你的大學畢業證書上簽名的一般都是德高望重的校長一樣。一般來說。這些有公信力機構的根證書已經在設備出廠前預先安裝到了你的設備上了。所以,數字證書能夠保證數字證書裏的公鑰確實是這個證書的全部者的,或者證書能夠用來確認對方的身份。數字證書主要是用來解決公鑰的安全發放問題。
綜上所述,總結一下,數字簽名和簽名驗證的大體流程例如以下圖所看到的:

技術分享


3、jarsign和signapk工具

了解到完了簽名中的三個文件的知識點之後。以下繼續來看看Android中簽名的兩個工具:jarsign和signapk

關於這兩個工具開始的時候非常easy混淆。感覺他們兩究竟有什麽差別嗎?

事實上這兩個工具非常好理解。jarsign是Java本生自帶的一個工具,他能夠對jar進行簽名的。而signapk是後面專門為了Android應用程序apk進行簽名的工具,他們兩的簽名算法沒什麽差別。主要是簽名時使用的文件不一樣。這個就要引出第三個問題了。


4、keystore文件和pk8,x509.pem文件的差別

我們上面了解到了jarsign和signapk兩個工具都能夠進行Android中的簽名。那麽他們的差別在於簽名時使用的文件不一樣

jarsign工具簽名時使用的是keystore文件

signapk工具簽名時使用的是pk8,x509.pem文件

當中我們在使用Eclipse工具敲代碼的時候,出Debug包的時候。默認用的是jarsign工具進行簽名的。並且Eclipse中有一個默認簽名文件:

技術分享

我們能夠看到這個默認簽名的keystore文件,當然我們能夠選擇我們自己指定的keystore文件。

這裏另一個知識點:

我們看到上面有MD5和SHA1的摘要,這個就是keystore文件裏私鑰的數據摘要,這個信息也是我們在申請非常多開發平臺賬號的時候須要填入的信息,比方申請百度地圖,微信SDK等,會須要填寫應用的MD5或者是SHA1信息


5、手動的簽名Apk包

1》使用keytool和jarsigner來進行簽名

當然,我們在正式簽名處release包的時候,我們須要創建一個自己的keystore文件:

技術分享

技術分享

這裏我們能夠對keystore文件起自己的名字,並且後綴名也是無關緊要的。創建完文件之後,也會生成MD5和SHA1的值。這個值能夠不用記錄的,能夠通過命令查看keystore文件的MD5和SHA1的值。

keytool -list -keystore debug.keystore

技術分享

當然我們都知道這個keytstore文件的重要性。說白了就相當於你的銀行卡password。

你懂得。

這裏我們看到用Eclipse自己主動簽名和生成一個keystore文件,我們也能夠使用keytool工具生成一個keystore文件。這種方法網上有,這裏就不做太多的介紹了。然後我們能夠使用jarsign來對apk包進行簽名了。

我們能夠手動的生成一個keystore文件:

keytool -genkeypair -v -keyalg DSA -keysize 1024 -sigalg SHA1withDSA -validity 20000 -keystore D:\jiangwei.keystore -alias jiangwei -keypass jiangwei -storepass jiangwei

技術分享

這個命令有點長,有幾個重要的參數須要說明:

-alias是定義別名。這裏為debug

-keyalg是規定簽名算法,這裏是DSA,這裏的算法直接關系到後面apk中簽名文件的後綴名,到後面會具體說明


在用jarsigner工具進行簽名

jarsigner -verbose -sigalg SHA1withDSA -digestalg SHA1 -keystore D:\jiangwei.keystore -storepass jiangwei D:\123.apk jiangwei

技術分享

這樣我們就成功的對apk進行簽名了。

簽名的過程中遇到的問題:

1》證書鏈找不到的問題

技術分享

這個是由於最後一個參數alias,是keystore的別名輸錯了。


2》生成keystore文件的時候提示password錯誤技術分享
這個原因是由於在當前文件夾已經有debug.ketystore了。在生成一個debug.keystore的話。就會報錯
3》找不到別名的問題技術分享
這個問題的解決辦法是由於我們在使用keytool生成keystore的時候。起了debug的別名,這個問題困擾了我非常久。最後做了非常多樣例才發現的。就是僅僅要我們的keystore文件的別名是debug的話,就會報這樣的錯誤。

這個應該和系統默認的簽名debug.keystore中的別名是debug有關系吧?沒有找到jarsigner的源代碼,所以僅僅能推測了,可是這三個問題在這裏標註一下,以防以後在遇到。


註意:Android中是同意使用多個keystore對apk進行簽名的。這裏我就不在粘貼命令了,我又創建了幾個keystore對apk進行簽名:

技術分享

這裏我把簽名之後的apk進行解壓之後。發現有三個簽名文件和證書(.SF/.DSA)

這裏我也能夠註意到,我們簽名時用的是DSA算法,這裏的文件後綴名就是DSA

並且文件名稱是keystore的別名

哎,這裏算是理清楚了我們上面的怎樣使用keytool產生keystore以及。用jarsigner來進行簽名。


2》使用signapk來進行簽名

以下我們再來看看signapk工具進行簽名:

java -jar signapk.jar .testkey.x509.pem testkey.pk8 debug.apk debug.sig.apk

這裏須要兩個文件:.pk8和.x509.pem這兩個文件

pk8是私鑰文件

x509.pem是含有公鑰的文件

這裏簽名的話就不在演示了,這裏沒什麽問題的。

可是這裏須要註意的是:signapk簽名之後的apk中的META-INF文件夾中的三個文件的名字是這樣的。由於signapk在前面的時候不像jarsigner會自己主動使用別名來命名文件,這裏就是寫死了是CERT的名字,只是文件名稱不影響的,後面分析Android中的Apk校驗過程中會說道。僅僅會通過後綴名來查找文件。

技術分享


3》兩種的簽名方式有什麽差別

那麽問題來了。jarsigner簽名時用的是keystore文件。signapk簽名時用的是pk8和x509.pem文件,並且都是給apk進行簽名的,那麽keystore文件和pk8,x509.pem他們之間是不是有什麽聯系呢?答案是肯定的,網上搜了一下。果然他們之間是能夠轉化的,這裏就不在分析怎樣進行轉化的,網上的樣例貌似非常多。有專門的的工具能夠進行轉化:

技術分享

那麽到這裏我們就弄清楚了這兩個簽名工具的差別和聯系。


三、分析Android中簽名流程機制

以下我們開始從源代碼的角度去看看Android中的簽名機制和原理流程

由於網上沒有找到jarsigner的源代碼。可是找到了signapk的源代碼,那麽以下我們就來看看signapk的源代碼吧:

源代碼位置:com/android/signapk/sign.java

通過上面的簽名時我們能夠看到,Android簽名apk之後,會有一個META-INF文件夾,這裏有三個文件:

MANIFEST.MF

CERT.RSA

CERT.SF

以下來看看這三個文件究竟是幹啥的?

1、MANIFEST.MF

技術分享

我們來看看源代碼:

public static void main(String[] args) {
    if (args.length != 4) {
        System.err.println("Usage: signapk " +
                "publickey.x509[.pem] privatekey.pk8 " +
                "input.jar output.jar");
        System.exit(2);
    }

    JarFile inputJar = null;
    JarOutputStream outputJar = null;

    try {
        X509Certificate publicKey = readPublicKey(new File(args[0]));

        // Assume the certificate is valid for at least an hour.
        long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;

        PrivateKey privateKey = readPrivateKey(new File(args[1]));
        inputJar = new JarFile(new File(args[2]), false);  // Don‘t verify.
        outputJar = new JarOutputStream(new FileOutputStream(args[3]));
        outputJar.setLevel(9);

        JarEntry je;

        // MANIFEST.MF
        Manifest manifest = addDigestsToManifest(inputJar);
        je = new JarEntry(JarFile.MANIFEST_NAME);
        je.setTime(timestamp);
        outputJar.putNextEntry(je);
        manifest.write(outputJar);

        // CERT.SF
        Signature signature = Signature.getInstance("SHA1withRSA");
        signature.initSign(privateKey);
        je = new JarEntry(CERT_SF_NAME);
        je.setTime(timestamp);
        outputJar.putNextEntry(je);
        writeSignatureFile(manifest,
                new SignatureOutputStream(outputJar, signature));

        // CERT.RSA
        je = new JarEntry(CERT_RSA_NAME);
        je.setTime(timestamp);
        outputJar.putNextEntry(je);
        writeSignatureBlock(signature, publicKey, outputJar);

        // Everything else
        copyFiles(manifest, inputJar, outputJar, timestamp);
    } catch (Exception e) {
        e.printStackTrace();
        System.exit(1);
    } finally {
        try {
            if (inputJar != null) inputJar.close();
            if (outputJar != null) outputJar.close();
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
}
在main函數中,我們看到須要輸入四個參數,然後就做了三件事:

寫MANIFEST.MF

//MANIFEST.MF
Manifest manifest = addDigestsToManifest(inputJar);
je = new JarEntry(JarFile.MANIFEST_NAME);
je.setTime(timestamp);
outputJar.putNextEntry(je);
manifest.write(outputJar);
在進入方法看看:

/** Add the SHA1 of every file to the manifest, creating it if necessary. */
private static Manifest addDigestsToManifest(JarFile jar)
        throws IOException, GeneralSecurityException {
    Manifest input = jar.getManifest();
    Manifest output = new Manifest();
    Attributes main = output.getMainAttributes();
    if (input != null) {
        main.putAll(input.getMainAttributes());
    } else {
        main.putValue("Manifest-Version", "1.0");
        main.putValue("Created-By", "1.0 (Android SignApk)");
    }

    BASE64Encoder base64 = new BASE64Encoder();
    MessageDigest md = MessageDigest.getInstance("SHA1");
    byte[] buffer = new byte[4096];
    int num;

    // We sort the input entries by name, and add them to the
    // output manifest in sorted order.  We expect that the output
    // map will be deterministic.

    TreeMap<String, JarEntry> byName = new TreeMap<String, JarEntry>();

    for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) {
        JarEntry entry = e.nextElement();
        byName.put(entry.getName(), entry);
    }

    for (JarEntry entry: byName.values()) {
        String name = entry.getName();
        if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) &&
            !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) &&
            (stripPattern == null ||
             !stripPattern.matcher(name).matches())) {
            InputStream data = jar.getInputStream(entry);
            while ((num = data.read(buffer)) > 0) {
                md.update(buffer, 0, num);
            }

            Attributes attr = null;
            if (input != null) attr = input.getAttributes(name);
            attr = attr != null ? new Attributes(attr) : new Attributes();
            attr.putValue("SHA1-Digest", base64.encode(md.digest()));
            output.getEntries().put(name, attr);
        }
    }

    return output;
}
代碼邏輯還是非常easy的。主要看那個循環的意思:

除了三個文件(MANIFEST.MF,CERT.RSA,CERT.SF),其它的文件都會對文件內容做一次SHA1算法,就是計算出文件的摘要信息,然後用Base64進行編碼就可以,以下我們用工具來做個案例看看是不是這樣:

首先安裝工具:HashTab

下載地址:http://www.baidu.com/s?wd=hashtab&rsv_spt=1&issp=1&f=8&rsv_bp=0&ie=utf-8&tn=baiduhome_pg&bs=hashtable

然後另一個站點就是在線計算Base64:http://tomeko.net/online_tools/hex_to_base64.php?lang=en

那以下就開始我們的驗證工作吧:

我們就來驗證一下AndroidManifest.xml文件,首先在MANIFEST.MF文件裏找到這個條目。記錄SHA1的值

技術分享

然後我們安裝HashTab之後,找到AndroidManifest.xml文件,右擊,選擇Hashtab:

技術分享

復制SHA-1的值:9C64812DE7373B201C294101473636A3697FD73C。到上面的那個Base64轉化站點,轉化一下:

技術分享

nGSBLec3OyAcKUEBRzY2o2l/1zw=

和MANIFEST.MF中的條目內容一模一樣啦啦

那麽從上面的分析我們就知道了。事實上MANIFEST.MF中存儲的是:

逐一遍歷裏面的全部條目,假設是文件夾就跳過。假設是一個文件,就用SHA1(或者SHA256)消息摘要算法提取出該文件的摘要然後進行BASE64編碼後,作為“SHA1-Digest”屬性的值寫入到MANIFEST.MF文件裏的一個塊中。該塊有一個“Name”屬性。其值就是該文件在apk包中的路徑。


2、以下再來看一下CERT.SF文件內容

技術分享

這裏的內容感覺和MANIFEST.MF的內容幾乎相同,來看看代碼吧:

//CERT.SF
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initSign(privateKey);
je = new JarEntry(CERT_SF_NAME);
je.setTime(timestamp);
outputJar.putNextEntry(je);
writeSignatureFile(manifest,new SignatureOutputStream(outputJar, signature));
進入到writeSignatureFile方法中:

/** Write a .SF file with a digest the specified manifest. */
private static void writeSignatureFile(Manifest manifest, OutputStream out)
        throws IOException, GeneralSecurityException {
    Manifest sf = new Manifest();
    Attributes main = sf.getMainAttributes();
    main.putValue("Signature-Version", "1.0");
    main.putValue("Created-By", "1.0 (Android SignApk)");

    BASE64Encoder base64 = new BASE64Encoder();
    MessageDigest md = MessageDigest.getInstance("SHA1");
    PrintStream print = new PrintStream(
            new DigestOutputStream(new ByteArrayOutputStream(), md),
            true, "UTF-8");

    // Digest of the entire manifest
    manifest.write(print);
    print.flush();
    main.putValue("SHA1-Digest-Manifest", base64.encode(md.digest()));

    Map<String, Attributes> entries = manifest.getEntries();
    for (Map.Entry<String, Attributes> entry : entries.entrySet()) {
        // Digest of the manifest stanza for this entry.
        print.print("Name: " + entry.getKey() + "\r\n");
        for (Map.Entry<Object, Object> att : entry.getValue().entrySet()) {
            print.print(att.getKey() + ": " + att.getValue() + "\r\n");
        }
        print.print("\r\n");
        print.flush();

        Attributes sfAttr = new Attributes();
        sfAttr.putValue("SHA1-Digest", base64.encode(md.digest()));
        sf.getEntries().put(entry.getKey(), sfAttr);
    }

    sf.write(out);
}

首先我們能夠看到,須要對之前的MANIFEST.MF文件整個內容做一個SHA1放到SHA1-Digest-Manifest字段中:

技術分享

我們看看出入的manifest變量就是剛剛寫入了MANIFEST.MF文件的

技術分享
這個我們能夠驗證一下:

技術分享

然後轉化一下

技術分享

看到了吧,和文件裏的值是一樣的啦啦

技術分享

以下我們繼續看代碼。有一個循環:

Map<String, Attributes> entries = manifest.getEntries();
for (Map.Entry<String, Attributes> entry : entries.entrySet()) {
    // Digest of the manifest stanza for this entry.
    print.print("Name: " + entry.getKey() + "\r\n");
    for (Map.Entry<Object, Object> att : entry.getValue().entrySet()) {
        print.print(att.getKey() + ": " + att.getValue() + "\r\n");
    }
    print.print("\r\n");
    print.flush();

    Attributes sfAttr = new Attributes();
    sfAttr.putValue("SHA1-Digest", base64.encode(md.digest()));
    sf.getEntries().put(entry.getKey(), sfAttr);
}

sf.write(out);
這裏還是用到了剛剛傳入的mainfest變量。遍歷他的條目內容。然後進行SHA算法計算在Base64一下:

事實上就是對MANIFEST.MF文件裏的每個條目內容做一次SHA,在保存一下就可以,做個樣例驗證一下:

用AndroidManifest.xml為例。我們把MANIFEST.MF文件裏的條目拷貝保存到txt文檔中:

技術分享
這裏須要註意的是。我們保存之後,須要加入兩個換行,我們能夠在代碼中看到邏輯:

技術分享

然後我們計算txt文檔的SHA值:

技術分享技術分享

看到了吧,這裏計算的值是一樣的啦啦

技術分享

到這裏我們就知道CERT.SF文件做了什麽:

1》計算這個MANIFEST.MF文件的總體SHA1值。再經過BASE64編碼後,記錄在CERT.SF主屬性塊(在文件頭上)的“SHA1-Digest-Manifest”屬性值值下

2》逐條計算MANIFEST.MF文件裏每個塊的SHA1,並經過BASE64編碼後。記錄在CERT.SF中的同名塊中,屬性的名字是“SHA1-Digest


3、最後我們在來看一下CERT.RSA文件

技術分享

這裏我們看到的都是二進制文件。由於RSA文件加密了。所以我們須要用openssl命令才幹查看其內容

openssl pkcs7 -inform DER -in CERT.RSA -noout -print_certs –text

技術分享

關於這些信息。能夠看以下這張圖:

技術分享

我們來看一下代碼:

/** Write a .RSA file with a digital signature. */
private static void writeSignatureBlock(
        Signature signature, X509Certificate publicKey, OutputStream out)
        throws IOException, GeneralSecurityException {
    SignerInfo signerInfo = new SignerInfo(
            new X500Name(publicKey.getIssuerX500Principal().getName()),
            publicKey.getSerialNumber(),
            AlgorithmId.get("SHA1"),
            AlgorithmId.get("RSA"),
            signature.sign());

    PKCS7 pkcs7 = new PKCS7(
            new AlgorithmId[] { AlgorithmId.get("SHA1") },
            new ContentInfo(ContentInfo.DATA_OID, null),
            new X509Certificate[] { publicKey },
            new SignerInfo[] { signerInfo });

    pkcs7.encodeSignedData(out);
}
我們看到,這裏會把之前生成的 CERT.SF文件, 用私鑰計算出簽名, 然後將簽名以及包括公鑰信息的數字證書一同寫入 CERT.RSA 中保存。CERT.RSA是一個滿足PKCS7格式的文件。


四、為何要這麽來簽名

上面我們就介紹了簽名apk之後的三個文件的具體內容。那麽以下來總結一下。Android中為何要用這樣的方式進行加密簽名。這樣的方加密是不是最安全的呢?以下我們來分析一下,假設apk文件被篡改後會發生什麽。

首先。假設你改變了apk包中的不論什麽文件。那麽在apk安裝校驗時,改變後的文件摘要信息與MANIFEST.MF的檢驗信息不同。於是驗證失敗,程序就不能安裝成功。
其次,假設你對更改的過的文件相應的算出新的摘要值,然後更改MANIFEST.MF文件裏面相應的屬性值,那麽必然與CERT.SF文件裏算出的摘要值不一樣。照樣驗證失敗。
最後,假設你還不死心,繼續計算MANIFEST.MF的摘要值。相應的更改CERT.SF裏面的值,那麽數字簽名值必然與CERT.RSA文件裏記錄的不一樣,還是失敗。
那麽能不能繼續偽造數字簽名呢?不可能,由於沒有數字證書相應的私鑰。
所以,假設要又一次打包後的應用程序能再Android設備上安裝,必須對其進行重簽名。

從上面的分析能夠得出。僅僅要改動了Apk中的不論什麽內容,就必須又一次簽名。不然會提示安裝失敗,當然這裏不會分析,後面一篇文章會註重分析為何會提示安裝失敗。


五、知識點梳理

1、數據指紋,簽名文件,證書文件的含義

1》數據指紋就是對一個數據源做SHA/MD5算法,這個值是唯一的

2》簽名文件技術就是:數據指紋+RSA算法

3》證書文件裏包括了公鑰信息和其它信息

4》在Android簽名之後,當中SF就是簽名文件。RSA就是證書文件我們能夠使用openssl來查看RSA文件裏的證書信息和公鑰信息

2、我們了解了Android中的簽名有兩種方式:jarsigner和signapk 這兩種方式的差別是:

1》jarsigner簽名時,須要的是keystore文件,而signapk簽名的時候是pk8,x509.pem文件

2》jarsigner簽名之後的SF和RSA文件名稱默認是keystore的別名,而signapk簽名之後文件名稱是固定的:CERT

3》Eclipse中我們在跑Debug程序的時候,默認用的是jarsigner方式簽名的,用的也是系統默認的debug.keystore簽名文件

4》keystore文件和pk8,x509.pem文件之間能夠互相轉化


六、思考

我們在分析了簽名技術之後,無意中發現一個問題,就是CERT.SF。MANIFEST.MF,這兩個文件裏的內容的name字段都是apk中的資源名,那麽就有一個問題了,假設資源名非常長。並且apk中的資源非常多。那麽這兩個文件就會非常大。那麽這裏我們是不是能夠優化呢?後面在分析怎樣減小apk大小的文章中會繼續解說,這裏先提出這個問題。

技術分享


資源下載:http://download.csdn.net/detail/jiangwei0910410003/9377046


總結

上面我們就通過源代碼來介紹了Android中的簽名過程。整個過程還是非常清楚的,文章寫得有點長。假設大家看的有問題的話,記得給我留言。後面我還會再寫一篇姊妹篇文章:Android中的簽名校驗過程具體解釋,期待中~~


PS: 關註微信,最新Android技術實時推送

技術分享



Android簽名機制之---簽名過程具體解釋