本文作者: 九心,原文發布于: 九心說。
前言
最近幫測試做了一點關于簽名的需求,今天就和各位同學簡單聊一聊關于簽名的那些事兒。
如果問到 Android 為什么需要簽名?大家都可能想到官網的解釋:
?Android 系統要求所有 APK 必須先使用證書進行數字簽名,然后才能安裝到設備上進行更新。
?
這是一個比較模糊的解釋,簡單來說,有了簽名,就可以讓 App 和開發者綁定。
畢竟,應用那么多,別的開發者也有可能盜用你的代碼,這個時候,包名和你相同,代碼和你相同,怎么區分你的 App 和這些人的 App 不是同一個呢?
這個時候數字簽名就派上用場了。
1
簽名基礎
想要徹底了解簽名知識,我們得了解以下知識:
-
消息摘要
-
數字簽名
-
加密
-
數字證書
這一系列的知識各位可能在學習網絡的時候或多或少的接觸過。
我們簡單的學習一下這些知識:
1. 消息摘要
消息摘要常常被被稱為數字摘要或者數字指紋,定義如下:
?在原來的數據基礎上,經過一個單向的 Hash 計算,得到一個固定的 Hash 值,這就是消息摘要。
?
常見的摘要算法都有 MD5、SHA-1 和 SHA-256,特點如下:
1. 「 長度固定,與內容長度無關」:比如 MD5 是 128 位、SHA-1 是 160 位、SHA-256 是 256 位。
2. 「 看似隨機,其實不隨機」:同內容兩次摘要得出的結果一致。
3. 「 單向」:只能從原數據得出摘要,不能從消息摘要得出原來的數據。
4. 「 優秀的摘要算法很難 Hash 碰撞」。
基于此,消息摘要常常會被用來檢查內容的完整性。
比如我們下載起點讀書,消息摘要的用法如下:
1. 計算摘要:App 會針對自己的文件信息計算出一個數字摘要比如 123**...**123。
2. 下載App。
3. 驗證摘要:對下載的 App 再次計算摘要,比如得出的也是 123**...**123,和之前的數字摘要一對比,這就代表我從服務器下載的內容是完整的,可以正常使用。
當然,上面只涉及了摘要部分,其他過程,我們后面分析。
2. 加密算法
什么是加密?
百科是這么解釋的:
?將明文信息改變為難以讀取的密文內容,使之不可讀的過程。只有擁有解密方法的對象,經由解密過程,才能將密文還原為正常可讀的內容。
?
所以啊,加密方法得到的密文是可以轉變為明文的,像信息摘要算法比如 MD5 得出來的結果是不可逆的,所以面試官問你們什么是加密算法的時候,你可不能把 MD5 說進去!
加密算法分為兩大類,「 對稱加密」和「 非對稱加密」。
2.1 對稱加密
對稱加密在加密和解密的時候使用的同一把鑰匙:
圖片來自《一文徹底搞懂加密、數字簽名和數字證書!》
2.2 非對稱加密
非對稱加密是使用公鑰/私鑰中的公鑰來加密明文,然后使用對應的私鑰來解密密文的過程:
圖片來自《一文徹底搞懂加密、數字簽名和數字證書!》
簡單對比一下對稱加密和非對稱機密:
非對稱加密 | 對稱加密 | |
---|---|---|
速度 | 慢 | 快 |
效率 | 低 | 高 |
安全性 | 高 | 低 |
常見算法 | RSADH | AESDESIDEA |
2.3 使用場景
學過網絡的同學應該都了解,在 Https 的傳輸過程中,客戶端和服務端使用非對稱加密生成對稱加密的密鑰,然后用對稱加密傳輸網絡中的數據。
比如我上大學那會兒,每個月的月尾我和我媽的對話是這樣的:
對話
網絡環境是開放的,萬一這時,有一個黑客監聽了我和我媽的對話,過程就變成了這樣:
監聽過程
在我發卡號的時候,黑客將我的卡號改成了他的卡號,于是我的生活費變成了他的生活費。
為了避免這種情況,于是我和我媽約定好了,每次發送前,使用對稱加密對消息進行加密,接受消息的時候使用密鑰解密,過程就變成了這樣:
對稱加密
中間人再也不能獲取到消息了,看似一點問題都沒有,但是我和老媽之間如何確定密鑰呢?
密鑰總要在互聯網之間進行傳輸的,有傳輸就有被中間人截獲的風險,一旦被截獲,錢可就沒了!
為了解決對稱加密鑰匙傳輸的問題,我和老媽用上了非對稱加密,像這樣:
非對稱加密
即使這樣,還是有問題存在:
1. 怎么才能確認我獲得的公鑰來自老媽?
2. 如何確定消息確實來自老媽?
解決這兩個問題也很簡單,一是數字簽名,二就是數字證書。
3. 數字簽名
數字簽名的作用是為了消息的完整性。
在非對稱加密的體系下,消息的發送過程是這樣的,還是上面的例子:
數字簽名
數字簽名的過程是這樣的:
1. 我發送消息前,利用 Hash 算法針對數據得出一個摘要。
2. 我使用老媽的公鑰對摘要內容進行加密,連同對稱加密的數據一起發送過去。
3. 老媽接收到消息后,先利用對稱密鑰對內容解密,再進行 Hash 計算得出摘要。
4. 老媽使用私鑰將摘要內容解密,和再次計算得出的摘要作對比,一致就代表消息無誤。
上面的這種場景其實有點不妥,數字簽名一般用在證書上,協商好對稱密鑰以后一般不會進行消息完整性校驗了,不過大伙只要了解數字簽名要來校驗消息完整性就好。
截止現在,還有最后一個問題,我無法確認獲取的公鑰確實來自老媽。
4. 數字證書
證書的作用很簡單,證明公鑰的身份。
就像在現實中,大家都是怎么證明自己的身份的?
沒錯,是身份證。你有沒有發現,每張身份證,會有三種信息:
1. 自身的信息。
2. 置辦身份證的派出所。
3. 有效期。
對應的數字證書也有很多內容:
1. CA:證書的頒發機構。
2. 證書的有效期。
3. 公鑰。
4. 證書的授予對象。
CA 將這些內容利用 CA 的私鑰進行簽名,用戶使用 CA 的公鑰驗簽,從而證明公鑰的身份。
常見的證書分為兩種:
1. 簽名證書:由 CA 機構頒發,絕大部分網站都采用的這種方式。
2. 自簽名證書:由服務器自己頒發給自己。
重回之前的例子,老媽只需要將自己的簽名證書發給我,我就可以獲取她的公鑰,之后就可以正常的通信。
2
Android簽名機制
在 Android 中,也需要使用數字證書做數字簽名,數字證書中公鑰對應的私鑰由開發者持有。
關于私鑰和證書的生成方式,可以查看:
?《Android官方文檔》
https://developer.android.com/studio/publish/app-signing?hl=zh-cn#debug-mode
?
在 Android Studio 中,最終會生成一個 .jks 的文件,早期 Eclipse 是 .keystore,它們都是用作證書和私鑰的二進制文件。
App 如果使用了一種私鑰簽名,另外一個私鑰簽名的文件將無法安裝或覆蓋老的版本,這樣做是為了防止已經安裝的 App 被惡意的第三方覆蓋。
1. Android簽名機制的異同點
Android 中數字簽名的生成和普通的數字簽名并沒有很大的區別。
但是進行數字簽名的證書可以采用自簽名證書,即不需要權威證書頒發機構(CA)來做背書,因為它的作用是用來標識應用程序的開發者,下載的用戶并不需要這個證書來下載該 App。
2. Debug和Relase的簽名
當我們在IDE中運行或調試項目時,AS 會自動使用 Android SDK 工具生成的調試證書為我們的應用簽名,路徑為 $HOME/.android/debug.keystore,但是應用商店可不接受使用調試證書發布的應用簽名。
打包Release時,我們一般會在 app 模塊中的build.gradle 進行配置:
android {
...
signingConfigs {
release {
storeFile file( "release.keystore")
storePassword "******"
keyAlias "******"
keyPassword "******"
}
}
}
這些都是我們生成.jks 或者.keystore需要生成的參數。
3
簽名方案
目前 Android 支持以下四種應用簽名方案:
1. v1方案:基于 JAR 簽名。
2. v2方案:Android 7.0 引入,改動大。
3. v3方案:Android 9.0 引入,基于 v2 的升級。
4. v4方案:Android 11.0 引入,用來支持 ADB 增量 APK 安裝。
1. v1方案
v1 是一個老生常談的簽名了,簽名過程也很簡單。
我們如果選中一個任意簽名后的 apk 進行解壓,會找到一個META-INF文件,這個文件里一般會有以 MF、SF 和 RSA 結尾的文件,如圖:
這些文件在 v1 簽名流程中是這樣的:
v1流程
驗證過程在 Apk 安裝的過程中:
v1驗證
整個過程清晰明了,但 v1 有兩個問題:
第一個問題是簽名校驗慢,要針對 Apk 中所有的文件進行校驗,這會拖累老設備的安裝時間。
第二個問題是僅針對 ZIP 條目校驗,META-INF文件不會計入校驗過程。這樣會導致即使我 Apk 已經簽過名,工程師也可以移動條目順序并重新壓縮,也可以修改 META-INF文件下的內容,帶來一些安全隱患,早期的多渠道打包就是在這里做的文章。
2. v2方案
v2 是 Android 簽名方案的一大步,它解決了 v1 遺留的簽名校驗慢和完整性的問題。
我們先來看一下 v2 的組成部分:
簽名前和簽名后的 APK
v1 的組成部分其實就和 Before signing 那一塊兒一樣,v2 多了紅色區域,我們稱之為APK簽名分塊。
簽名后的各個 APK 部分
從保護的內容來看,v1 僅保護內容1,v2 保護的區域有 1、3、4 和 2 的 signed data 區域,signed data 是 1、3 和 4 得出來的摘要等信息。
簽名過程
就一個 App 而言,它可能有一個或者多個簽名者,對于每個簽名者而言,都會進行簽名過程。
v2 沒有對每個文件都進行計算,而是針對的所有字節。它將 1、3 和 4 區域都拆分成了大小為 1MB 的連續塊,計算方式如下:
1. 每個小塊都按:字節 0xa5 + 塊字節長度 + 塊內容 進行計算。
2. 每個1、3 和 4 塊都按:字節 0xa5 + 塊數 + 小塊摘要 進行計算。
最后,將這些一個或者多個簽名者的摘要、證書等信息都打包到 Apk 中。
v2流程
驗簽過程
v2 方案的 APK 驗證過程是這樣的:
1. 找到APK簽名分塊區域。
2. 每找到一個簽名者,都會驗證:簽名算法、信息摘要、證書和公鑰。
3. 所有的簽名者都驗證通過了,APK 驗證才會通過。
3. v3方案
v3 方案建立在 v2 的基礎上,目標是解決在更新過程中更改簽名密鑰的問題。
所以 APK 簽名分塊中 添加了兩部分內容:
1. Proof-of-rotation: 一個存在替換的所有舊簽名證書的鏈表,根節點是最舊的證書。
2. SDK 版本支持。
v3 和 v2 的簽名過程和驗證過程幾乎一致,就不寫出來了。
4. v4方案
如果同學們經常玩一些主機游戲,可以發現,在 PS5 或者 Swtich 上,一些游戲即使沒有安裝完成,我們也可以打開游戲玩一些基本功能,比如我以前常玩的 NBA 2k 系列。
Android 11 中谷歌也新增了 「ADB增量APK安裝」 功能,比如一個 APK 有 2GB,我下載完 50 MB 以后,就可以使用一些基本功能,剩余的文件通過后臺流式傳輸,不過 Android 11 中的這個功能是面向 ADB 的。
雖然這個功能很贊,但是對簽名方案帶來了一些挑戰,之前的方案都是基于所有文件進行校驗的,于是推出 Android 第四代簽名方案 v4。
v4 基于 APK 所有的字節計算出 Merkle Hash 樹,并將 Merkle 樹的根 Hash、鹽值作為簽名數據進行包完整性校驗,v4 簽名必須單獨存在 .idsig 文件中,不會存在于 APK 文件中,所以 apk 文件中仍然需要 v2 或者 v3 簽名。
5. 向下兼容的簽名方案
Android 中的簽名方案是自上而下兼容的,如圖:
APK 驗證流程 v4
對于 Android 11 來說,驗證過程是這樣的:
1. 是否支持 v4,v4 驗證完了再驗證 v3 或者 v2
2. v4 不通過,驗證 v3
3. v3 不通過,驗證 v2
4. v2 不通過,驗證 v1
5. v1 不通過,安裝失敗
對于 Android 9 來說,就得從 v3 方案開始驗證的。
總結
讀完這篇文章,相信你對 Android 簽名方案有了基礎的了解。
如果文章有不對的地方,評論區見~
參考文章:
《Android v1、v2、v3簽名詳解》
《增量安裝與安卓V4簽名簡介》
《官方文檔》
https://source.android.google.cn/security/apksigning?hl=zh-cn
最后推薦一下我做的網站,玩Android: wanandroid.com,包含詳盡的知識體系、好用的工具,還有本公眾號文章合集,歡迎體驗和收藏!
:
遵循最新安卓架構,做一個完美的玩Android App
超好的的包體積優化教程,真不僅僅是優化!
黑科技!讓Native Crash 與ANR無處發泄!