1. 程式人生 > >APK簽名機制原理詳解

APK簽名機制原理詳解

前言

眾所周知,Android系統在安裝Apk的過程中,會對Apk進行簽名校驗,校驗通過後才能安裝成功。那你知道簽名校驗的機制是什麼?具體校驗的是什麼內容嗎?申請第三方SDK(如微信支付)時填入的SAH1值是什麼?目前眾多的快速批量打包方案又是如何繞過簽名檢驗的?

我將通過一系列的文章來解開這些疑惑:

這篇文章先來介紹Apk簽名相關的基本知識。

  1. 簽名是什麼?如何進行簽名?
  2. keystore和證書格式
  3. jarsigner和apksigner的區別

1. 簽名是什麼?

要知道簽名是什麼,先來看為什麼需要簽名 。大家都知道,在訊息通訊時,必須至少解決兩個問題:一是確保訊息來源的真實性,二是確保訊息不會被第三方篡改。在安裝Apk時,同樣需要確保Apk來源的真實性,以及Apk沒有被第三方篡改。如何解決這兩個問題呢?方法就是開發者對Apk進行簽名:在Apk中寫入一個“指紋”。指紋寫入以後,Apk中有任何修改,都會導致這個指紋無效,Android系統在安裝Apk進行簽名校驗時就會不通過,從而保證了安全性。

要了解如何實現簽名,需要了解兩個基本概念:數字摘要和數字證書。

1.1 數字摘要

數字摘要是將任意長度的訊息變成固定長度的短訊息,它類似於一個自變數是訊息的函式,也就是Hash函式。數字摘要就是採用單向Hash函式將需要加密的明文“摘要”成一串固定長度的密文,這一串密文又稱為數字指紋,它有固定的長度,而且不同的明文摘要成密文,其結果總是不同的,而同樣的明文其摘要必定一致。

簡單來說,就是對一個任意長度的資料,通過一個Hash演算法計算後,都可以得到一個固定長度的二進位制資料,這個資料就稱為“摘要”。摘要具有下面的幾個特徵:

  1. 唯一性

    在不考慮碰撞的情況下,不同的資料的計算出的摘要是不同的。

  2. 固定長度

    不同的Hash演算法計算的長度是不一樣的,但對同一個演算法來說是一樣的。比較常用的Hash演算法有MD5和SHA1,MD5的長度是128拉,SHA1的長度是160位。

  3. 不可逆性

    即從正向計算的摘要不可能逆向推匯出原始資料。

1.2 簽名和校驗的大體過程

前面已經說到,可以通過簽名來確保資料來源的可靠性和資料的不可篡改性。簽名就是在摘要的基礎上再進行一次加密,對摘要加密後的資料就可以當作數字簽名,在安裝Apk需要對簽名進行驗證,驗證通過才能繼續安裝。

這裡有兩個過程:簽名過程 和 校驗過程。

這裡寫圖片描述

先來說簽名過程:

  1. 計算摘要

    通過Hash演算法提取出原始資料的摘要;

  2. 計算簽名

    再通過基於金鑰(私鑰)的非對稱加密演算法對提取出的摘要進行加密,加密後的資料就是簽名信息;

  3. 寫入簽名

    將簽名信息寫入原始資料的簽名區塊內。

再來看校驗過程:

  1. 計算摘要

    接收方接收到資料後,首先用同樣的Hash演算法從接收到的資料中提取出摘要;

  2. 解密簽名

    使用傳送方的公鑰對數字簽名進行解密,解密出原始摘要;

  3. 比較摘要

    如果解密後的資料和提取的摘要一致,則校驗通過;如果資料被第三方篡改過,解密後的資料和摘要不一致,校驗不通過。

1.3 數字證書

這裡有一個前提:接收方必須要知道傳送方的公鑰和所使用的演算法。如果數字簽名和公鑰一起被篡改,接收方無法得知,還是會校驗通過。如何保證公鑰的可靠性呢?答案是數字證書,數字證書是身份認證機構(Certificate Authority)頒發的,包含了以下資訊:

  • 證書頒發機構
  • 證書頒發機構簽名
  • 證書繫結的伺服器域名
  • 證書版本、有效期
  • 簽名使用的加密演算法(非對稱演算法,如RSA)
  • 公鑰 等

接收方收到訊息後,先向CA驗證證書的合法性(根據證書的簽名、繫結的域名等資訊。CA機構是權威的,可以保證這個過程的可靠性。)再進行簽名校驗。

關於通過數字證書進行簽名校驗的詳細過程,可以參考我之前寫的一篇關於HTTPS通訊機制的介紹:詳談HTTPS通訊機制,HTTPS是如何進行安全通訊的?

需要注意的是,Apk的證書通常的自簽名的,也就是由開發者自己製作,沒有向CA機構申請。Android在安裝Apk時並沒有校驗證書本身的合法性,只是從證書中提取公鑰和加密演算法,這也正是對第三方Apk重新簽名後,還能夠繼續在沒有安裝這個Apk的系統中繼續安裝的原因。

1.4 簽名和校驗過程

完整的簽名和校驗過程如下:(圖片來源:維基百科

這裡寫圖片描述

2. keystore和證書格式

我們在對Apk簽名時並沒有直接指定私鑰、公鑰和數字證書,而是使用keystore檔案,這些資訊都包含在了keystore檔案中。根據編碼不同,keystore檔案分為很多種,Android使用的是Java標準keystore格式JKS(Java Key Storage),所以通過Android Studio匯出的keystore檔案是以.jks結尾的。

keystore使用的證書標準是X.509,X.509標準也有多種編碼格式,常用的有兩種:pem(Privacy Enhanced Mail)和der(Distinguished Encoding Rules)。jks使用的是der格式,Android也支援直接使用pem格式的證書進行簽名,我們下面會介紹。

兩種證書編碼格式的區別:

  • DER(Distinguished Encoding Rules)

    二進位制格式,所有型別的證書和私鑰都可以儲存為der格式。

  • PEM(Privacy Enhanced Mail)

    base64編碼,內容以—–BEGIN xxx—– 開頭,以—–END xxx—– 結尾,比如:

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAlmXFRXEZomRKhNRp2XRoXH+2hm17RfrfecQlT49fktoDLkF6r99uiNnuUdPi6UQuXOnzEbe1nZkfuqfB10aBLrDqBUSZ+3
-----END RSA PRIVATE KEY-----

-----BEGIN CERTIFICATE-----
MIICvTCCAaWgAwIBAgIEcWTElDANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDEwRyPQDLnVKeEIh81OwD3KIrQOUwsxyptOVVea1D8CzIAnGs
-----END CERTIFICATE-----

X.509證書格式:

這裡寫圖片描述

3. jarsigner和apksigner的區別

Android提供了兩種對Apk的簽名方式,一種是基於JAR的簽名方式,另一種是基於Apk的簽名方式,它們的主要區別在於使用的簽名檔案不一樣:jarsigner使用keystore檔案進行簽名;apksigner除了支援使用keystore檔案進行簽名外,還支援直接指定pem證書檔案和私鑰進行簽名。

android {
    signingConfigs {
        config {
            storeFile file(keystoreProperties['storeFile'])
            storePassword keystoreProperties['storePassword']
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword'] 
        }
    }
    ...
  }

不知道大家有沒有注意一個問題,我們通過keytool或者AS生成一個keystore的時候(簽署您的應用),除了要輸入keystore的密碼外,還要輸入一個alias和key的密碼。在簽名時,除了要指定keystore檔案和密碼外,也要指定alias和key的密碼,這是為什麼呢?

原因是keystore是一個金鑰庫,也就是說它可以儲存多對金鑰和證書,keystore的密碼是用於保護keystore本身的,一對金鑰和證書是通過alias來區分的。從這裡可以看出jarsigner是支援使用多個證書對Apk進行簽名的。apksigner也同樣支援,關於apksigner的使用介紹可以參考官方文件apksigner

3.1 簽名相關命令

● jarsigner簽名
jarsigner -keystore keystore_file -signedjar signed.apk unsigned.apk alias_name -storepass pwd

● apksigner簽名
java -jar signapk.jar cert.x509.pem private.pk8 unsigned.apk signed.apk

● 檢視keystore檔案
keytool -list  -v -keystore keystore_file -storepass pwd

● 檢視apk證書
keytool -printcert -jarfile apk

● 檢視DER格式證書(META-INFO/CERT.RSA)
openssl pkcs7 -inform DER -in CERT.RSA -noout -print_certs -text

● 檢視PEM格式證書
openssl x509 -in cert.x509.pem -text -noout

● apksigner檢查apk是否簽名,以及檢視證書SHA1值
apksigner verify -v --print-certs

ok,簽名的基本概念和校驗過程就介紹到這裡,關於JAR簽名和V2簽名機制的詳細介紹,參考下面兩篇文章: