Android | 他山之石,能夠攻玉!一篇文章看懂 v1/v2/v3 簽名機制

點贊關注,再也不迷路,你的支持對我意義重大!html

🔥 Hi,我是醜醜。本文 GitHub · Android-NoteBook 已收錄,這裏有 Android 進階成長路線筆記 & 博客,歡迎跟着彭醜醜一塊兒成長。(聯繫方式在 GitHub)java

前言

  • 在打生產包時,必定須要對 apk 簽名,那麼你知道爲何要給應用簽名嗎?
  • 在這篇文章裏,我將分析 Android apk 的簽名機制,並介紹 v一、v2 和 v3 三種安裝包簽名方案的原理與演進。若是能幫上忙,請務必點贊加關注,這真的對我很是重要。

系列文章

相關文章


目錄


1. 概述

1.1 爲何要給應用簽名?

應用 APK 實際上是一種特殊的 Zip 壓縮包,沒法避免惡意破解者解壓 / 反編譯修改內容,針對這個問題有何解決方案呢?他山之石,能夠攻玉 ——數字簽名算法。應用簽名正是數字簽名算法的應用場景之一,與其餘應用場景相似,目的無非是:android

  • 認證

Android 平臺上運行的每一個應用都必須有開發者的簽名。在安裝應用時,軟件包管理器會驗證 APK 是否已通過適當簽名,安裝程序會拒絕沒有得到簽名就嘗試安裝的應用。git

  • 驗證完整性

軟件包管理器在安裝應用前會驗證應用摘要,若是破解者修改了 apk 裏的內容,那麼摘要就再也不匹配,驗證失敗(驗證流程見下文方案)。github

提示: 使用數字簽名的優勢是驗證過程無須複雜的接口和權限,只須要在本機驗證。web

1.2 數字簽名 & 驗證模型

在以前我寫過的一篇文章裏,我曾經分析過數字簽名 & 驗證模型:《密碼學 | 高屋建瓴!摘要、簽名與數字證書都是什麼?》。在這裏我簡單複述下:算法

  • 一、私鑰簽名: 先使用消息摘要算法對原始消息作摘要處理,而後再使用私鑰對摘要進行簽名;
  • 二、公鑰驗證: 使用公鑰驗證簽名,檢查是否與消息的摘要值。

1.3 數字證書籤名 & 驗證模型

Editting...安全

須要注意的是,Android 目前不對應用證書進行 CA 認證,應用能夠由第三方(OEM、運營商、其餘應用市場)簽名,也能夠自行簽名。微信

1.4 應用簽名方案演進

截止至 Android 11,Android 支持如下三種應用簽名方案:markdown

  • v1 簽名方案:基於 Jar 簽名;
  • v2 簽名方案:提升驗證速度和覆蓋度(在 Android 7.0 Nougat 中引入);
  • v3 簽名方案:實現密鑰輪轉(在 Android 9.0 Pie 中引入)。

爲了提升兼容性,必須按照 v一、v二、v3 的前後順序採用簽名方案,低版本平臺會忽略高版本的簽名方案在 APK 中添加的額外數據。

引用自 source.android.com/security/ap… —— Android Developers


2. 簽名方案 v1

v1 簽名方案是基於 Jar 的簽名。

2.1 簽名產物

首先,咱們先來分析其簽名產物。v1 簽名後會增長 META-INF 文件夾,其中會有以下三個文件。考慮到使用不一樣的證書和簽名方式,獲得的文件名可能不一樣,所以你只要留意文件的後綴便可:

META-INF
├── MANIFEST.MF
├── CERT.SF
├── CERT.RSA
複製代碼
文件 描述
MANIFEST.MF 記錄「apk 中每個文件對應的摘要」(除了 META-INF 文件夾)
*.SF 記錄「MANIFEST.MF 文件的摘要」和「MANIFEST.MF 中每一個數據塊的摘要」
*.RSA 包含了「*.SF 文件的簽名」和「包含公鑰的開發者證書」

提示: 若是 apk 中文件數不少,並且文件名很長,那麼 MANIFEST.MF*.SF 兩個文件會變得很大。有沒有辦法優化呢?見 第 5.1 節 優化摘要記錄文件大小

2.2 簽名流程

v1 簽名流程以下:

  • 一、計算每一個文件的 SHA-1 摘要,進行 BASE64 編碼後寫入 MANIFEST.MF 文件;

MANIFEST.MF(Message Digest File,摘要文件)

Manifest-Version: 1.0
Built-By: Generated-by-ADT
Created-By: Android Gradle 3.1.0

Name: AndroidManifest.xml
SHA1-Digest: 9hTSmRfzHEeQc7V2wxBbTT3DmCY= 【文件的摘要】

...
複製代碼
  • 二、計算整個 MANIFEST.MF 文件的 SHA-1 摘要,進行 BASE64 編碼後寫入 *.SF 文件;
  • 三、計算 MANIFEST.MF 文件中每一塊摘要的 SHA-1 摘要,進行 BASE64 編碼後寫入 *.SF 文件;

\*.SF(Signature File,簽名文件)

Signature-Version: 1.0
Created-By: 1.0 (Android)
SHA1-Digest-Manifest: MJQyZ0dc4dv7G9nlJPAMQLwEwbU= 【MANIFEST.MF 文件的摘要】
X-Android-APK-Signed: 2

Name: AndroidManifest.xml
SHA1-Digest: IJioMmfD693T4qnUJcPKhq9woHQ= 【摘要的摘要】

...
複製代碼
  • 四、計算整個 *.SF 文件的數字簽名(先摘要再私鑰加密);
  • 五、將數字簽名和 X.509 開發者數字證書(公鑰)寫入 *.RSA 文件。

提示:*.RSA 文件加密了,須要使用 openssl 工具打開。

引用自 zhuanlan.zhihu.com/p/108034286 —— 木質的旋律 著

2.3 驗證流程

驗證流程能夠分爲驗證簽名和驗證完整性兩個步驟:

驗證簽名步驟:

  • 一、取出 *.RSA 中包含的開發者證書;
  • 二、【注意:這裏不向 CA 認證開發者證書合法性】;
  • 三、用證書中的公鑰解密 *.RSA 中包含的簽名,獲得摘要;
  • 四、計算 *.SF 的摘要;
  • 五、對比 (3) 和 (4) 的摘要是否一致;

若是上述簽名驗證結果正確,纔會驗證完整性:

  • 一、計算 MANIFEST.MF 的摘要;
  • 二、對比 *.SF 記錄中的文件摘要和 (1) 的摘要是否一致;
  • 三、若是一致,再用 MANIFEST.MF 中的每一塊數據去校驗每個文件是否被修改。

以上任何步驟驗證失敗,則整個 APK 驗證失敗。

2.4 存在的問題

  • 完整性覆蓋範圍不足:Zip 文件中部份內容不在驗證範圍,例如 META-INF 文件夾;
  • 驗證速度較差:驗證程序必須解壓全部壓縮的條目,這須要花費更多時間和內存。

爲了解決這些問題,Android 7.0 中引入了 APK 簽名方案 v2。


3. 簽名方案 v2

v2 簽名方案是一種 全文件簽名方案,該方案可以發現對 APK 的受保護部分進行的全部更改,相對於 v1 簽名方案驗證速度更快,完整性覆蓋範圍更廣。

提示: 爲了兼容低版本,使用 v2 簽名方案的同時,還須要使用 v1 簽名方案。

3.1 Zip 文件簡介

在分析 v2 簽名方案以前,咱們先簡單瞭解一下 Zip 文件格式:

  • Zip 文件主體結構分爲三個部分:「條目內容區」&「中央目錄區」&「中央目錄結尾區(EoCD)」

  • EoCD 中記錄了中央目錄的起始位置,在「條目內容區」和「中央目錄區」之間插入了其餘數據不會影響 Zip 解壓。

3.2 簽名產物

首先,咱們先來分析其簽名產物。v2 簽名後會在 「條目內容區」和「中央目錄區」之間插入「APK 簽名分塊(APK Signing Block)」

引用自 source.android.com/security/ap… —— Android Developers

從左到右邊,咱們定義爲區塊 1~4。

3.2 簽名流程

相對與 v1 簽名方案,v2 簽名方案再也不以文件爲單位計算摘要了,而是以 1 MB 爲單位將文件拆分爲多個連續的塊(chunk),每一個分區的最後一個塊可能會小於 1 MB。

v2 簽名流程以下:

  • 一、對區塊 一、三、4,按照 1MB 大小分割爲多個塊(chunk);
  • 二、計算每一個塊的摘要;
  • 三、計算 (2) 中全部摘要的簽名。
  • 四、添加 X.509 開發者數字證書(公鑰)

引用自 source.android.com/security/ap… —— Android Developers

3.3 驗證流程

驗證流程能夠分爲驗證簽名和驗證完整性兩個步驟:

  • 驗證簽名步驟:用公鑰驗證區塊 2 的簽名;
  • 驗證完整性步驟:用「APK數據摘要集」驗證每一塊數據的摘要。

4. 簽名方案 v3

簽名方案 v3 支持密鑰輪換,應用可以在 APK 更新過程當中更改其簽名密鑰。

【累了,後面先不寫了...】


5. 衍生應用場景

這一節,咱們介紹基於 Android 應用簽名機制的衍生應用場景。

5.1 優化摘要記錄文件大小

在 v1 方案中,MANIFEST.MF*.SF 這兩個文件會記錄大量的文件名和文件摘要。若是 apk 中文件數不少,並且文件名很長,那麼這兩個文件會變得很大。使用 AndResGuard 工具,能夠將文件名轉換爲短路徑文件名,從而減小這兩個文件的大小。

引用自 time.geekbang.org/column/arti… —— 張紹文 著

5.2 多渠道打包方案

在實際生產中,每每須要生成多個渠道的 APK 包,傳統的方法是使用 APKTool 逆向工具、Flavor + BuildType 等方案,這一類多渠道打包方案的缺點是耗時嚴重。隨着 Android 應用簽名方案的演進,演變出了不一樣的多渠道打包方案:

v1 方案時代下的多渠道打包

  • 添加空文件

在 v1 方案中,咱們提到了完整性校驗不覆蓋到 META-INF 文件夾的問題。有些多渠道打包方案就是利用了這個問題,在 META-INF 文件夾下添加空文件,用空文件的名稱來做爲渠道的惟一標識,就能夠節省打包的時間,提升打渠道包的速度。

  • Zip Comment

除了添加空文件的方法,還能夠向 APK 添加 Zip Comment 來生成多渠道包(APK 自己就是特殊的 Zip 包)。

v2 方案時代下的多渠道打包

在 v2 簽名方案中,幾乎整個 APK 都歸入保護範圍,若是向 APK 添加空文件或 Zip Comment 的話,在安裝時會報如下錯誤:

Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES: 
Failed to collect certificates from base.apk: META-INF/CERT.SF indicates base.apk is signed using APK Signature Scheme v2, 
but no such signature was found. Signature stripped?]
複製代碼

新背景下的多渠道打包方案,則是利用了 APK 簽名分塊(區塊 2)不受保護 & 字段可擴展的特色,向區塊中添加多渠道信息(ID-Value),例如 美團多渠道打包方案 Walle


6. 總結

  • 簽名應用是處於兩個目的:認證 & 驗證完整性,即:認證 APK 的開發者以及驗證 APK 內容是否被篡改。截止到 Android 11,一共有 v一、v二、v3 三種簽名方案。

  • v1 是基於 Jar 的簽名方案,它存在完整性覆蓋範圍不足 & 驗證速度較差兩個問題。

  • Android 7.0 推出的 v2 簽名方案優化了這兩個問題,經過「條目內容區」和「中央目錄區」之間插入「APK 簽名分塊(APK Signing Block)」,優化了 v1 方案的兩大問題。

  • Android 9.0 推出的 v3 方案是 v2 方案的優化版本,知足了密鑰輪換的需求。


參考資料

創做不易,你的「三連」是醜醜最大的動力,咱們下次見!

相關文章
相關標籤/搜索