提綱
初步了解:是什么,為什么需要,有什么好處,怎么加密和驗證,怎么使用,要點補充
簽名方案:v1,v2,v3簽名及校驗流程,版本缺陷
初步了解Android簽名機制
? ? Android應用程序的簽名(是什么)
???????? - 在Android 系統中,所有安裝到系統的應用程序都必有一個數字證書,要求每一個安裝進系統的應用程序都是經過數字證書簽名的,此數字證書用于標識應用程序的作者和在應用程序之間建立信任關系
? ? ? 簽名的必要性(為什么需要)
????? -證明 APK的所有者。通過對 Apk 進行簽名,開發者可以證明對 Apk 的所有權和控制權,可用于安裝和更新其應用。而在 Android 設備上的安裝 Apk ,如果是一個沒有被簽名的 Apk,則會被拒絕安裝。
????? -允許 Android市場和設備校驗APK的正確性。在安裝 Apk 的時候,軟件包管理器也會驗證 Apk 是否已經被正確簽名,并且通過簽名證書和數據摘要驗證是否合法沒有被篡改。只有確認安全無篡改的情況下,才允許安裝在設備上。
? ? ? 統一簽名的好處(有什么好處)
????? - 有利于程序升級。當新版程序和舊版程序的數字證書相同時,Android 系統才會認為這兩個程序是同一個程序的不同版本。如果新版程序和舊版程序的數字證書不相同,則Android 系統認為他們是不同的程序,并產生沖突,會要求新程序更改包名。
????? -有利于程序的模塊化設計和開發。Android 系統允許擁有同一個數字簽名的程序運行在一個進程中,Android程序會將他們視為同一個程序。所以開發者可以將自己的程序分模塊開發,而用戶只需要在需要的時候下載適當的模塊
????? -可以通過權限(permission)的方式在多個程序間共享數據和代碼。Android提供了基于數字證書的權限賦予機制,應用程序可以和其他的程序共享概功能或者數據給那那些與自己擁有相同數字證書的程序。如果某個權限(permission)的protectionLevel是signature,則這個權限就只能授予那些跟該權限所在的包擁有同一個數字證書的程序。
? ? ? 簽名的驗證方式(怎么加密和驗證的)
? ? ? ? ?發送者把文件用算法生成摘要,再用私鑰對摘要進行加密,把證書、文件、加密串、公鑰發給接收端。接收端獲取到之后,根據證書認真發送者身份,用公鑰對加密串進行解密,再對文件也用算法生成摘要,對比解密之后的信息和摘要的信息是否相同。
簽名的動作是執行在打包流程的后期
靜態資源整理->編譯源碼,處理得到dex文件->合成APK->簽名,對齊
? ? ? 對Android應用程序簽名(怎么使用的)
使用方面只需要兩步
第一步是生成密鑰,在as中就有方便的頁面生成密鑰
第二步則是使用密鑰簽名,常見的在gradle中設置好signingconfig,不同編譯下的使用
這里舉例的使用方式是比較常用有效的,也有命令行,gui界面的方式進行設置。在ppt注釋中有鏈接,有興趣可以看看這些連接
Android APK命令行實現V1、V2簽名及驗證http://www.lxweimin.com/p/e00f9bb12340
Android 開發者->Android Studio->用戶指南->為您的應用簽名https://developer.android.com/studio/publish/app-signing
? ? ? 關于數字證書的要點補充
??????? -所有的應用程序都必須有數字證書(包括未發布的應用)。Android 系統不會安裝一個沒有數字證書的應用程序。
????????? 從IDE 中運行或調試您的項目時,Android Studio 將自動使用通過 Android SDK 工具生成的調試證書為您的應用簽名。當您首次在 Android Studio 中運行或調試項目時,IDE 會自動在 `$HOME/.android/debug.keystore` 中創建調試密鑰庫和證書,并設置密鑰庫和密鑰密碼。
??????? -在簽名時,需要考慮數字證書的有效期。數字證書都是有有效期的,Android 只是在應用程序安裝的時候才會檢查證書的有效期。如果程序已經安裝在系統中,即使證書過期也不會影響程序的正常功能。一旦數字證書失效,持有改數字證書的程序將不能正常升級。
Android中的簽名方案
Android 的簽名方案,發展到現在,不是一蹴而就的。Android
現在已經有三種應用簽名方案,v1升級到v3
v1 到 v2 是顛覆性的,為了解決JAR簽名方案的安全性問題,而到了v3方案,其實結構上并沒有太大的調整,可以理解為v2簽名方案的升級版,有一些資料也把它稱之為v2+方案。
簽名方案的升級,就是向下兼容的,所以只要使用得當,這個過程對開發者是透明的。
v1 到 v2 方案的升級,對開發者影響最大的,就是渠道打包簽署的問題。這個后續會說到。
簽名工具有兩個jarsigner 和 apksigner。它們的簽名算法沒什么區別,主要是簽名使用的文件不同。
V1(<7.0)基于jar簽名,jarsigner
V2(>=7.0)基于apk簽名,apksigner
V3(>=9.0)基于apk簽名,apksigner,支持密鑰輪轉
Android提供了兩種對Apk的簽名方式,區別:
1、jarsigner是為jar簽名設計的,apksigner是為apk設計的;
2、jarsigner不能生成v2簽名,apksigner可以;
3、jarsigner沒有判斷4.2(api17)以下,不能用SHA-256摘要算法;
4、支持的算法jarsigner使用keystore文件進行簽名;
apksigner除了支持使用keystore文件進行簽名外,還支持直接指定pem證書文件和私鑰進行簽名。
5、與對齊的關系;
了解V1簽名
通過解壓工具打開apk文件,會發現有一個META-INF目錄,該目錄中有3個文件,這3個文件是簽名以后生成的,顯然與簽名相關,我們依次看這幾個文件中的內容:MANIFEST.MF、CERT.SF和CERT.RSA。
MANIFEST.MF:APK中所有原始文件的數據摘要的Base64編碼,而數據摘要算法就是SHA1或者SHA256
CERT.SF:
1》計算這個MANIFEST.MF文件的數據摘要(如SHA1算法),再經過BASE64編碼后,記錄在CERT.SF主屬性塊(在文件頭上)的“SHA1-Digest-Manifest”屬性值值下
2》逐條計算MANIFEST.MF文件中每一個塊的數據摘要,并經過BASE64編碼后,記錄在CERT.SF中的同名塊中,屬性的名字是“SHA1-Digest
CERT.RSA:把之前生成的 CERT.SF文件, 用私鑰計算出簽名, 然后將簽名以及包含公鑰信息的數字證書一同寫入生成CERT.RSA文件,用openssl命令才能查看其內容:openssl pkcs7 -inform DER -in CERT.RSA -noout -print_certs –text
V1簽名的詳細流程(具體可參考SignApk.java)? ? ? ??
生成簽名過程
1.對APK內部所有文件(條目),提取摘要后BASE64生成指紋清單MANIFEST.MF;
2.對指紋清單提取摘要后BASE64生成CERT.SF;
3.對CERT.SF用私鑰加密并且附加公鑰生成CERT.RSA.
整個簽名機制的最終產物就是MANIFEST.MF、CERT.SF、CERT.RSA三個文件
V1安裝驗證簽名流程
1.校驗CERT.SF文件
計算CERT.SF的摘要,與通過簽名者公鑰解密得到的摘要進行對比,如果一致進行下一步。
2.檢驗MANIFEST.MF文件
計算MANIFEST.MF文件的摘要,與CERT.SF主屬性記錄的摘要進行對比。
3.檢驗apk中每個文件的完整性
逐步計算apk中每個文件的摘要。與MANIFEST.MF中的記錄進行對比,如果一致,則校驗通過。
如果是升級安裝,還需校驗證書簽名是否與已安裝的一致。
在安裝APK時,Android系統會校驗簽名,檢查APK是否被篡改。代碼流程是:PackageManagerService.java ->
PackageParser.java,PackageParser類負責V1簽名的具體校驗。
V1簽名是怎么保證APK文件不被篡改的?
首先,如果破壞者修改了APK中的任何文件,那么被篡改文件的數據摘要的Base64編碼就和MANIFEST.MF文件的記錄值不一致,導致校驗失敗。
?其次,如果破壞者同時修改了對應文件在MANIFEST.MF文件中的Base64值,那么MANIFEST.MF中對應數據塊的Base64值就和CERT.SF文件中的記錄值不一致,導致校驗失敗。
最后,如果破壞者更進一步,同時修改了對應文件在CERT.SF文件中的Base64值,那么CERT.SF的數字簽名就和CERT.RSA記錄的簽名不一致,也會校驗失敗。
那有沒有可能繼續偽造CERT.SF的數字簽名那?理論上不可能,因為破壞者沒有開發者的私鑰。那破壞者是不是可以用自己的私鑰和數字證書重新簽名那,這倒是完全可以!
綜上所述,任何對APK文件的修改,在安裝時都會失敗,除非對APK重新簽名。但是相同包名,不同簽名的APK也是不能同時安裝的。
V1簽名的缺陷(也是迭代V2的原因)
?不夠安全,簽名驗證范圍僅驗證 元數據 以外的 文件 條目,這里說的元數據主要指meta-inf目錄下的文件
v1 簽名方案,并不會保護
Apk 內的所有內容,有一些例外部分,被修改也并不會導致簽名失效
這樣,在驗證APK 簽名的時候,就需要處理大量不可信(尚未經過驗證)的數據結構,然后還需要過濾并舍棄掉這部分不受簽名保護的數據,再進行簽名校驗。也就是說你可以在已簽名的文件中,增加一些不被簽名保護的內容,這將導致受攻擊的可能增大。
?不夠迅速:驗證簽名的時候,須解壓所有已壓縮的文件條目進行數據摘要的校驗
v1方案是對 APK 內部的被保護的原始文件(未壓縮),單獨進行計算數據摘要,所以在驗證期間,也需要對每個文件進行解壓再進行簽名校驗,來驗證是否被篡改。所以在驗證APK 簽名的時候,必須解壓 APK 的所有已壓縮的文件條目進行數據摘要的校驗,而這些,都將需要花費更多的時間和內存。
V2簽名(Android 7.0引入)
引入目的:解決v1的問題
?MANIFEST.MF中的數據摘要是基于原始未壓縮文件計算的。因此在校驗時,需要先解壓出原始文件,才能進行校驗。而解壓操作無疑是耗時的。
?V1簽名僅僅校驗APK第一部分中的文件,缺少對APK的完整性校驗。因此,在簽名后,我們還可以修改APK文件,例如:通過zipalign進行字節對齊后,仍然可以正常安裝。
怎么解決的
?V2簽名是對APK本身進行數據摘要計算,不存在解壓APK的操作,減少了校驗時間。
?V2簽名是針對整個APK進行校驗(不包含簽名塊本身),因此對APK的任何修改(包括添加注釋、zipalign字節對齊)都無法通過V2簽名的校驗。
某實驗室數據(Nexus 6P、Android 7.1.1)可供參考
了解APK文件結構(V1)
APK文件本質上是一個ZIP壓縮包,而ZIP格式是固定的,主要由三部分構成,如下圖所示
?第一部分是內容塊,所有的壓縮文件都在這部分。每個壓縮文件都有一個local file header,主要記錄了文件名、壓縮算法、壓縮前后的文件大小、修改時間、CRC32值等。
?第二部分稱為中央目錄,包含了多個central directory file header(和第一部分的local file header一一對應),每個中央目錄文件頭主要記錄了壓縮算法、注釋信息、對應local file header的偏移量等,方便快速定位數據。
?最后一部分是EOCD,主要記錄了中央目錄大小、偏移量和ZIP注釋信息等
根據之前的V1簽名和校驗機制可知,V1簽名只會檢驗第一部分的所有壓縮文件,而不理會后兩部分內容。
了解APK文件結構(V2區別于V1)
v2 簽名會在原先APK塊中增加了一個新的塊(簽名塊),新的塊存儲了簽名、摘要、簽名算法、證書鏈和額外屬性等信息,這個塊有特定的格式。
APK簽名塊位于中央目錄之前,文件數據之后。V2簽名同時修改了EOCD中的中央目錄的偏移量,使簽名后的APK還符合ZIP結構。
v2 簽名塊負責保護第1、3、4 部分的完整性,以及第2部分包含的APK 簽名方案v2分塊中的signed data分塊的完整性。
V2簽名原理
V2簽名塊的生成(可參考ApkSignerV2)
V2簽名塊的生成可參考ApkSignerV2,整體結構和流程如圖
首先,根據多個簽名算法,計算出整個APK的數據摘要,組成左上角的APK數據摘要集;
接著,把最左側一列的數據摘要、數字證書和額外屬性組裝起來,形成類似于V1簽名的“MF”文件(第二列第一行);
其次,再用相同的私鑰,不同的簽名算法,計算出“MF”文件的數字簽名,形成類似于V1簽名的“SF”文件(第二列第二行);
然后,把第二列的類似MF文件、類似SF文件和開發者公鑰一起組裝成通過單個keystore簽名后的v2簽名塊(第三列第一行)。
最后,把多個keystore簽名后的簽名塊組裝起來,就是完整的V2簽名塊了(Android中允許使用多個keystore對apk進行簽名)。
簡而言之,單個keystore簽名塊主要由三部分組成,分別是上圖中第二列的三個數據塊:類似MF文件、類似SF文件和開發者公鑰,其結構如圖2所示
V2簽名的校驗流程(PackageManagerService.java
-> PackageParser.java -> ApkSignatureSchemeV2Verifier.java)
Android Gradle Plugin2.2之上默認會同時開啟V1和V2簽名,同時包含V1和V2簽名的CERT.SF文件會有一個特殊的主屬性,該屬性會強制APK走V2校驗流程(7.0之上),以充分利用V2簽名的優勢(速度快和更完善的校驗機制)
同時包含V1和V2簽名的APK的校驗流程,優先校驗V2,沒有或者不認識V2,則校驗V1。具體校驗類似v1,但是不是校驗一個個文件,而是apk的分段,如下圖所示
V2簽名是怎么保證APK不被篡改的?
首先,如果破壞者修改了APK文件的任何部分(簽名塊本身除外),那么APK的數據摘要就和“MF”數據塊中記錄的數據摘要不一致,導致校驗失敗。
其次,如果破壞者同時修改了“MF”數據塊中的數據摘要,那么“MF”數據塊的數字簽名就和“SF”數據塊中記錄的數字簽名不一致,導致校驗失敗。
然后,如果破壞者使用自己的私鑰去加密生成“SF”數據塊,那么使用開發者的公鑰去解密“SF”數據塊中的數字簽名就會失敗;
最后,更進一步,若破壞者甚至替換了開發者公鑰,那么使用數字證書中的公鑰校驗簽名塊中的公鑰就會失敗,這也正是數字證書的作用。
綜上所述,任何對APK的修改,在安裝時都會失敗,除非對APK重新簽名。但是相同包名,不同簽名的APK也是不能同時安裝的。
V2簽名注意點補充
Q1:V2簽名后EOCD的中央目錄偏移量更改了,安裝時怎么通過校驗的?
A1:Android系統在校驗APK的數據摘要時,首先會把EOCD的中央目錄偏移量替換成簽名塊的偏移量,然后再計算數據摘要。而簽名塊的偏移量就是v2簽名之前的中央目錄偏移量
具體代碼邏輯,可參考ApkSignatureSchemeV2Verifier.java。截圖自https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/util/apk/ApkSigningBlockUtils.java
Q2:APK先V1簽名,還是V2先簽名?
A2:先V1,再V2,因為JAR簽名需要修改zip數據區和中央目錄的內容,先使用V2簽名再JAR簽名會破壞V2簽名的完整性。
Q3:刪掉V2簽名,能否繞過V2簽名?
A3:不行,簽名CERT.SF文件中的標志也是防回滾的保護,防止刪除高版本簽名,僅驗證低版本簽名
Q4:APK簽名時,只有V2簽名,沒有V1簽名行不行?
A4:這種情況是可以編譯通過的,并且在Android 7.0之上也可以正確安裝和運行。但是7.0之下,因為不認識V2,又沒有V1簽名,所以會報沒有簽名的錯誤。
V2簽名的缺陷:沒支持簽名的更新(迭代v3原因)
?簽名有默認25年的有效期限
?簽名泄漏想替換
V3簽名(Android 9.0 引入)
V3簽名結構基于V2, 增加支持密鑰輪轉
V3 簽名新增的新塊(attr)存儲了所有的簽名信息,由更小的 Level 塊,以鏈表的形式存儲。
其中每個節點都包含用于為之前版本的應用簽名的簽名證書,最舊的簽名證書對應根節點,系統會讓每個節點中的證書為列表中下一個證書簽名,從而為每個新密鑰提供證據來證明它應該像舊密鑰一樣可信。
V3 方案還沒有正式開放,在最新版的Build Tools 版本28.0.3中的Apksigner,尚不支持V3的APK簽名方案。想嘗鮮可以通過源代碼自行編譯。
https://developer.android.google.cn/studio/command-line/apksigner#usage-verify
到這里,就了解完了簽名的機制,下一篇講下android的打包機制。感謝支持
?參考鏈接:
?應用簽名 https://source.android.google.cn/security/apksigning
?Android簽名機制v1、v2、v3 http://www.lxweimin.com/p/9ca1d6f3f083
?APK簽名機制原理詳解http://www.lxweimin.com/p/286d2b372334