對一個APK文件簽名以後,APK文件根目錄下會增長META-INF目錄,該目錄下增長三個文件:html
MANIFEST.MF NETEASE.RSA NETEASE.SF
其中.RSA文件還多是.DSA文件,RSA與SF文件的文件名能夠更改,可是它們的命名必須同樣。java
MANIFEST.MF中保存了APK裏全部文件的SHA1校驗值的BASE64編碼,格式以下(一個文件對應一條記錄):android
Name: res/anim/abc_fade_in.xml SHA1-Digest: ohPEA4mboaFUu9LZMUwk7FmjbPI= Name: res/anim/abc_fade_out.xml SHA1-Digest: MTJWZc22b5LNeBboqBhxcQh5xHQ=
SF文件裏保存了MANIFEST.MF文件的SHA1校驗值的BASE64編碼,同時還保存了MANIFEST.MF中每一條記錄的SHA1檢驗值BASE64編碼,格式以下:git
SHA1-Digest-Manifest: ZRhh1HuaoEKMn6o21W1as0sMlaU= Name: res/anim/abc_fade_in.xml SHA1-Digest: wE1QEZhFkLBWMw4TRtxPdsiMRtA= Name: res/anim/abc_fade_out.xml SHA1-Digest: MfCV1efdxSKtesRMF81I08Zyvvo=
RSA文件則包含了簽名的公鑰、簽名全部者等信息,還保存了用SHA1withRSA簽名算法對SF文件的簽名結果信息。github
Android系統就是根據這三個文件的內容對APK文件進行簽名檢驗的。算法
通常的簽名過程能夠由apksign.jar或者jarsinger.jar完成。apksign.jar由Android SDK提供,使用方法以下:shell
java -jar signapk.jar testkey.x509.pem testkey.pk8 update.apk update_signed.apk
它接受一個PEM公鑰文件,PK8私鑰文件,對update.apk進行簽名,簽名後的文件保存到update_signed.apk。安全
jarsinger是由JDK提供,使用方法以下:工具
jarsigner -verbose -keystore d:\\debug.keystore -signedjar update_signed.apk update.apk androiddebugkey -digestalg SHA1 -sigalg MD5withRSA -keypass android -storepass android
Android 7.0(Nougat)引入一項新的應用簽名方案APK Signature Scheme v2(簡稱V2),它是一個對全文件進行簽名的方案,能提供更快的應用安裝時間、對未受權APK文件的更改提供更多保護,在默認狀況下,Android Gradle 2.2.0插件會使用APK Signature Scheme v2和傳統簽名方案來簽署應用。gradle
目前該方案並非強制方案,在build.gradle
添加v2SigningEnabled false
便可使用V1方案來簽署應用,以下所示:
android { ... defaultConfig { ... } signingConfigs { release { storeFile file("releasekey.keystore") storePassword "password" keyAlias "ReleaseKeyAlias" keyPassword "KeyPassword" v2SigningEnabled false } } }
使用V2簽名進行簽署的同時也會對APK進行V1簽名,這樣就有着比較好的兼容性,能徹底兼容低於Android 7.0(Nougat)的版本。對比舊簽名方案,它有更快的驗證速度和更安全的保護,所以新的應用簽名方案可能會被採納成一個強制配置。
V2簽名除了V1簽名的功能外,還包含如下工做,下圖是V2簽名後和未簽名apk文件的一個對比:
V2簽名方案會在ZIP文件格式的Central Directory
區塊所在文件位置的前面添加一個APK Signing Block區塊,下面按照ZIP文件的格式來分析V2簽名方案簽名後的APK包。
整個APK(ZIP文件格式)會被分爲如下四個區塊: 1. Contents of ZIP entries(from offset 0 until the start of APK Signing Block) 2. APK Signing Block 3. ZIP Central Directory 4. ZIP End of Central Directory
V2簽名方案的簽名信息會被保存在區塊2(APK Signing Block)中, 而區塊1(Contents of ZIP entries
)、區塊3(ZIP Central Directory
)、區塊4(ZIP End of Central Directory
)是受保護的,在簽名後任何對區塊一、三、4的修改都逃不過新的應用簽名方案的檢查。
Android 9 支持APK 密鑰輪轉,這使應用可以在 APK 更新過程當中更改其簽名密鑰。爲了實現輪轉,APK 必須指示新舊簽名密鑰之間的信任級別。爲了支持密鑰輪轉,Google將APK 簽名方案從 v2 更新爲 v3,以容許使用新舊密鑰。v3 方案的設計與v2 方案很是類似,採用相同的常規格式,並支持相同的簽名算法 ID、密鑰大小和 EC 曲線。v3 在 APK 簽名分塊中添加了有關受支持的 SDK 版本和 proof-of-rotation 結構的信息。
V3簽名中會保存minSDK與maxSDK版本,若是平臺版本不符合minSDK與maxSDK的要求則拒絕安裝。
proof-of rotation 結構容許應用輪轉其簽名證書,就是當咱們的簽名證書快過時或者須要更換時,打包時提早將新證書打包信息打包進apk文件中,後續版本升級時便可使用新證書籤名。
在 Android 9 及更高版本中,能夠根據 APK 簽名方案 v三、v2 方案或 v1 方案驗證 APK。較舊的平臺會忽略 v3 簽名並嘗試驗證 v2 簽名,而後驗證 v1。
android 5.0以前能夠直接往apk的後邊追加數據,因爲這個方法太魯莽,直接往 apk 文件後邊追加數據也不安全。在android 5.0 後開始校驗 apk 數據格式合法性了。因此這種粗魯的辦法不能用了。
從第一張圖中咱們瞭解到ZIP文件分爲了3塊分別是:Contents of ZIP entries(壓縮的文件內容源數據
)、ZIP Central Directory(壓縮的目錄源數據
)、ZIP End of Central Directory(目錄結束標識結構
)。這裏咱們只關心最後一塊目錄結束標識結構,目錄結束標識存在於整個歸檔包的結尾,用於標記壓縮的目錄數據的結束。結構以下:
Offset | Bytes | Description | 譯 |
---|---|---|---|
0 | 4 | End of centraldirectory signature =0x06054b50 | 核心目錄結束標記(0x06054b50) |
4 | 2 | Number of this disk | 當前磁盤編號 |
6 | 2 | Disk where central directory starts | 核心目錄開始位置的磁盤編號 |
8 | 2 | Number of central directory records on this disk | 該磁盤上所記錄的核心目錄數量 |
10 | 2 | Total number of central directory records | 核心目錄結構總數 |
12 | 4 | Size of central directory (bytes) | 核心目錄的大小 |
16 | 4 | Offset of start of central directory, relative to start of archive | 核心目錄開始位置相對於archive開始的位移 |
20 | 2 | Comment length (n) | 註釋長度 |
22 | n | Comment | 註釋內容 |
apk文件默認狀況下沒有comment,因此 comment length的short 兩個字節爲 0,咱們須要把這個值修改成咱們的comment的長度,而後把comment追加到後邊便可。
追加comment會致使區塊4(ZIP End of Central Directory
)產生變化,上文中也說道V2簽名方案的簽名信息會被保存在區塊2(APK Signing Block)中, 而區塊1(Contents of ZIP entries
)、區塊3(ZIP Central Directory
)、區塊4(ZIP End of Central Directory
)是受保護的,在簽名後任何對區塊一、三、4的修改都逃不過新的應用簽名方案的檢查。所以此方案在V2簽名上不可用。
V1簽名後apk文件多了META-INF這個文件夾,裏面包含了三個文件,MANIFEST.MF、CERT.SF、CERT.RSA
MANIFEST.MF文件列出了apk的全部文件,以及這些文件內容所對應的base64-encoded SHA1 哈希值,例如,
Name: AndroidManifest.xml SHA1-Digest: 7lLs5fV2H4ttapcDEdtJRTQOzpk=
上述表示AndroidManifest.xml這個文件的SHA1的哈希值爲7lLs5fV2H4ttapcDEdtJRTQOzpk=
CERT.SF和MANIFEST.MF很類似,可是它描述的不是文件內容的hash值,而是列出了MANIFEST.MF這個文件中每條信息的hash值,舉例會明白些:
Name: AndroidManifest.xml SHA1-Digest-Manifest: 8CVc0D8U2qQKRD+7Fw7+Jmb6Qos=
上面這條hash值‘8CVc0D8U2qQKRD+7Fw7+Jmb6Qos=’對應的是MANIFEST.MF中下面這幾行字符串的hash值
Name: AndroidManifest.xml SHA1-Digest: 7lLs5fV2H4ttapcDEdtJRTQOzpk=
這個文件裏面其實包含了對CERT.SF文件的數字簽名以及簽名時所用數字證書公鑰等數字證書的信息。
在安裝APK時,會先檢查CERT.SF文件,確認簽名正確,然後檢查SF文件,再經過SF去檢查apk文件下各個文件,這樣一步步環環相扣確保了每一個文件都是有效的,確保了文件的完整和安全性,可是對於好事者仍是發現了其中的漏洞,美團有個方案就是經過在META-INF下添加一個空文件來表明渠道,這個空文件不會被檢查,他和任何文件沒有關聯(CERT.SF、CERT.RSA和MANIFEST.MF他們是相關關聯的)。
添加空文件會致使apk文件區塊1(Contents of ZIP entries
) 產生變化,上文中也說道V2簽名方案的簽名信息會被保存在區塊2(APK Signing Block)中, 而區塊1(Contents of ZIP entries
)、區塊3(ZIP Central Directory
)、區塊4(ZIP End of Central Directory
)是受保護的,在簽名後任何對區塊一、三、4的修改都逃不過新的應用簽名方案的檢查。所以此方案在V2簽名上不可用。
配置productFlavors的方式替換AndroidManifest文件,首先在AndroidManifest.xml
文件中定義一個meta-data
<meta-data android:name="CHANNEL" android:value="${CHANNEL_VALUE}" />
而後在gradle文件中設置一下productFlavors
android { productFlavors { huawei { manifestPlaceholders = [CHANNEL_VALUE: "1"] } xiaomi { manifestPlaceholders = [CHANNEL_VALUE: "2"] } oppo { manifestPlaceholders = [CHANNEL_VALUE: "3"] } vivo { manifestPlaceholders = [CHANNEL_VALUE: "4"] } } }
而後執行gradle任務便可打出所有配置的渠道包,因爲每一個渠道都會致使一些gradle任務從新執行,而後再從新生成apk文件再簽名,所以這種方案是最慢的。
上面說過,V2簽名後的4個區塊,一、三、4都會被保護,而區塊2簽名信息區塊缺不受保護,因而就有好事者發現了其中漏洞,那麼咱們先看看區塊2的格式是什麼樣的吧。
Offset | Bytes | Description |
---|---|---|
0 | 8 | 這個Block的長度,以字節數(不含此字段) |
8 | n | 一組ID-value |
-24 | 8 | 這個Block的長度(和第一個字段同樣值) |
-16 | 16 | 魔數 「APK Sig Block 42」 |
咱們重點來看一下這個ID-value,區塊2中包含多個「ID-值」對,所採用的封裝方式有助於更輕鬆地在 APK 中找到該分塊。APK 的 v2 簽名會存儲爲一個「ID-值」對,其中 ID 爲 0x7109871a。在APK安裝時會解譯該分塊,Android會忽略 ID 未知的「ID-值」對。
那麼在區塊2中增長一個」ID-值「對便可實現快速的渠道包生成,這就是美團walle的渠道包方案。
APK文件中放置一個文件寫入渠道號,經過腳本修改渠道號文件內容,而後經過官方簽名工具(apksigner、jarsigner)進行簽名,此種方案是比較簡單、很是安全固然速度也比較慢。
首先看一下各個簽名方案的對比:
APK文件追加 | Comment寫入 | META-INF空文件 | Flavor配置 | 區塊2 | 二次簽名 | |
---|---|---|---|---|---|---|
是否支持V1 | 是 | 是 | 是 | 是 | 是 | 是 |
是否支持V2 | 否 | 否 | 否 | 是 | 是 | 是 |
是否支持V3 | 否 | 否 | 否 | 是 | 是 | 是 |
速度 | 快 | 快 | 快 | 慢 | 快 | 中 |
安全性 | 不安全 | 不安全 | 不安全 | 安全 | 通常 | 安全 |
每一種方案都有其優勢與缺點,隨着V2與V3簽名的普及,後面渠道包的方案可能會愈來愈少,固然這也有利有弊,好處就是咱們的apk被修改渠道植入後門的風險下降了,那麼壞處就是對於渠道太多的APP來講每一次發版都須要喝上幾杯咖啡的時間來打渠道包。
https://source.android.google.cn/security/apksigning/v2
https://source.android.google.cn/security/apksigning/v3
https://tech.meituan.com/2017/01/13/android-apk-v2-signature-scheme.html
https://tech.meituan.com/2014/06/13/mt-apk-packaging.html