APK簽名機制原理詳解
前言
眾所周知,Android系統在安裝Apk的過程中,會對Apk進行簽名校驗,校驗通過後才能安裝成功。那你知道簽名校驗的機制是什麼?具體校驗的是什麼內容嗎?申請第三方SDK(如微信支付)時填入的SAH1值是什麼?目前眾多的快速批量打包方案又是如何繞過簽名檢驗的?
我將通過一系列的文章來解開這些疑惑:
這篇文章先來介紹Apk簽名相關的基本知識。
- 簽名是什麼?如何進行簽名?
- keystore和證書格式
- jarsigner和apksigner的區別
1. 簽名是什麼?
要知道簽名是什麼,先來看為什麼需要簽名 。大家都知道,在訊息通訊時,必須至少解決兩個問題:一是確保訊息來源的真實性,二是確保訊息不會被第三方篡改。在安裝Apk時,同樣需要確保Apk來源的真實性,以及Apk沒有被第三方篡改。如何解決這兩個問題呢?方法就是開發者對Apk進行簽名:在Apk中寫入一個“指紋”。指紋寫入以後,Apk中有任何修改,都會導致這個指紋無效,Android系統在安裝Apk進行簽名校驗時就會不通過,從而保證了安全性。
要了解如何實現簽名,需要了解兩個基本概念:數字摘要和數字證書。
1.1 數字摘要
數字摘要是將任意長度的訊息變成固定長度的短訊息,它類似於一個自變數是訊息的函式,也就是Hash函式。數字摘要就是採用單向Hash函式將需要加密的明文“摘要”成一串固定長度的密文,這一串密文又稱為數字指紋,它有固定的長度,而且不同的明文摘要成密文,其結果總是不同的,而同樣的明文其摘要必定一致。
簡單來說,就是對一個任意長度的資料,通過一個Hash演算法計算後,都可以得到一個固定長度的二進位制資料,這個資料就稱為“摘要”。摘要具有下面的幾個特徵:
唯一性
在不考慮碰撞的情況下,不同的資料的計算出的摘要是不同的。
固定長度
不同的Hash演算法計算的長度是不一樣的,但對同一個演算法來說是一樣的。比較常用的Hash演算法有MD5和SHA1,MD5的長度是128拉,SHA1的長度是160位。
不可逆性
即從正向計算的摘要不可能逆向推匯出原始資料。
1.2 簽名和校驗的大體過程
前面已經說到,可以通過簽名來確保資料來源的可靠性和資料的不可篡改性。簽名就是在摘要的基礎上再進行一次加密,對摘要加密後的資料就可以當作數字簽名,在安裝Apk需要對簽名進行驗證,驗證通過才能繼續安裝。
這裡有兩個過程:簽名過程 和 校驗過程。
先來說簽名過程:
計算摘要
通過Hash演算法提取出原始資料的摘要;
計算簽名
再通過基於金鑰(私鑰)的非對稱加密演算法對提取出的摘要進行加密,加密後的資料就是簽名信息;
寫入簽名
將簽名信息寫入原始資料的簽名區塊內。
再來看校驗過程:
計算摘要
接收方接收到資料後,首先用同樣的Hash演算法從接收到的資料中提取出摘要;
解密簽名
使用傳送方的公鑰對數字簽名進行解密,解密出原始摘要;
比較摘要
如果解密後的資料和提取的摘要一致,則校驗通過;如果資料被第三方篡改過,解密後的資料和摘要不一致,校驗不通過。
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簽名機制的詳細介紹,參考下面兩篇文章: