Android 9 支援APK 金鑰輪換,這使應用程式能夠在 APK 更新過程中更改其簽署金鑰。為了使輪換切實可行,APK 必須指示新舊簽署金鑰之間的信任等級。為了支援金鑰輪換,我們將APK 簽章方案從 v2 更新為 v3,以允許使用新舊金鑰。 V3 將有關支援的 SDK 版本的資訊和旋轉證明結構添加到 APK 簽章區塊中。
APK簽名區塊
為了保持與 v1 APK 格式的向後相容性,v2 和 v3 APK 簽章儲存在緊鄰 ZIP 中央目錄之前的 APK 簽章區塊內。
v3 APK 簽章區塊格式與 v2 相同。 APK 的 v3 簽章儲存為 ID 值對,ID 為 0xf05368c0。
APK簽章方案v3塊
v3 方案的設計與v2 方案非常相似。它具有相同的通用格式,並支援相同的簽章演算法 ID 、金鑰大小和 EC 曲線。
然而,v3方案增加了有關支援的SDK版本和proof-of-rotation結構的資訊。
格式
APK 簽章方案 v3 區塊儲存在 ID 為0xf05368c0
的 APK 簽章區塊內。
APK簽章方案v3塊的格式遵循v2的格式:
- 長度前綴
signer
的長度前綴序列:- 長度前綴的
signed data
:- 長度前綴
digests
的長度前綴序列:-
signature algorithm ID
(4位元組) -
digest
(長度前綴)
-
- X.509
certificates
的長度前綴序列:- 長度前綴的 X.509
certificate
(ASN.1 DER 形式)
- 長度前綴的 X.509
-
minSDK
(uint32) - 如果平台版本低於此數字,則應忽略此簽署者。 -
maxSDK
(uint32) - 如果平台版本高於此數字,則應忽略此簽署者。 - 長度前綴附加
additional attributes
的長度前綴序列:-
ID
(uint32) -
value
(可變長度:附加屬性的長度 - 4 個位元組) -
ID - 0x3ba06f8c
-
value -
旋轉證明結構
-
- 長度前綴
minSDK
(uint32) - 簽章資料部分中 minSDK 值的重複 - 如果目前平台不在範圍內,則用於跳過此簽章的驗證。必須匹配帶符號的資料值。-
maxSDK
(uint32) - 簽章資料部分中 maxSDK 值的重複 - 如果目前平台不在範圍內,則用於跳過此簽章的驗證。必須匹配帶符號的資料值。 - 長度前綴
signatures
的長度前綴序列:-
signature algorithm ID
(uint32) - 對
signed data
進行長度前綴signature
-
- 長度前綴的
public key
(SubjectPublicKeyInfo,ASN.1 DER 形式)
- 長度前綴的
輪換證明和自信任的舊證書結構
輪換證明結構允許應用程式輪換其簽名證書,而不會被與其通訊的其他應用程式阻止。為了實現這一點,應用程式簽章包含兩個新資料:
- 第三方斷言應用程式的簽名證書在其前身受信任的任何地方都可以信任
- 應用程式本身仍然信任的舊簽名證書
簽署資料部分中的proof-of-rotation 屬性由一個單鍊錶組成,每個節點都包含一個用於對應用程式的早期版本進行簽署的簽章憑證。此屬性旨在包含概念性的輪換證明和自信任的舊證書資料結構。此清單按版本排序,最舊的簽章憑證對應於根節點。旋轉證明資料結構是透過讓每個節點中的憑證簽署清單中的下一個節點來建構的,從而為每個新金鑰注入證據,表明它應該與舊金鑰一樣受信任。
self-trusted-old-certs 資料結構是透過向每個節點添加標誌來建構的,指示其在集合中的成員身份和屬性。例如,可能存在一個標誌,指示給定節點處的簽章憑證對於取得 Android 簽章權限是可信的。此標誌允許由舊憑證簽署的其他應用程式仍被授予使用新簽署憑證簽署的應用程式定義的簽署權限。由於整個旋轉證明屬性駐留在 v3 signer
欄位的簽署資料部分中,因此它受到用於對包含的 apk 進行簽署的金鑰的保護。
這種格式排除了多個簽章金鑰以及不同祖先簽章憑證會聚合為一個(多個起始節點到一個公用接收器)。
格式
旋轉證明儲存在 APK 簽章方案 v3 區塊內,ID 為0x3ba06f8c
。其格式為:
- 長度前綴
levels
的長度前綴序列:- 長度前綴
signed data
(透過先前的憑證 - 如果存在)- 長度前綴的 X.509
certificate
(ASN.1 DER 形式) -
signature algorithm ID
(uint32) - 上一層憑證所使用的演算法
- 長度前綴的 X.509
flags
(uint32) - 指示此憑證是否應位於 self-trusted-old-certs 結構中以及用於哪些操作的標誌。-
signature algorithm ID
(uint32) - 必須與下一層中簽章資料部分的 ID 相符。 - 對上述
signed data
進行長度前綴signature
- 長度前綴
多項證書
Android 目前將使用多個憑證簽署的 APK 視為具有獨立於組成憑證的唯一簽署身分。因此,簽名資料部分中的旋轉證明屬性形成一個有向非循環圖,最好將其視為單鍊錶,給定版本的每組簽署者代表一個節點。這增加了旋轉證明結構的額外複雜性(下面的多簽名者版本)。特別是,訂購成為一個問題。更重要的是,不再可能獨立簽署 APK,因為輪換證明結構必須讓舊的簽名證書簽署新的一組證書,而不是逐一簽署它們。例如,由金鑰 A 簽署的 APK 希望由兩個新金鑰 B 和 C 簽名,則 B 簽署者不能只包含 A 或 B 的簽名,因為這是與 B 和 C 不同的簽名身分。這將意味著簽名者必須在建立這樣的結構之前進行協調。
多簽名者旋轉證明屬性
- 長度前綴集合的長度前綴
sets
:-
signed data
(按先前的設定 - 如果存在)-
certificates
的長度前綴序列- 長度前綴的 X.509
certificate
(ASN.1 DER 形式)
- 長度前綴的 X.509
-
signature algorithm IDs
序列 (uint32) - 對應前一組中的每個證書,順序相同。
-
-
flags
(uint32) - 指示這組憑證是否應位於 self-trusted-old-certs 結構中以及用於哪些操作的標誌。 - 長度前綴
signatures
的長度前綴序列:-
signature algorithm ID
(uint32) - 必須與簽章資料部分中的演算法 ID 相符 - 對上述
signed data
進行長度前綴signature
-
-
旋轉證明結構中的多位祖先
v3 方案也不處理同一應用程式的兩個不同金鑰輪換為相同簽章金鑰。這與收購的情況不同,收購公司希望將收購的應用程式轉移到使用其簽署金鑰來共享權限。此次收購被視為受支援的用例,因為新應用程式將透過其套件名稱進行區分,並且可以包含自己的旋轉證明結構。不受支援的情況是,同一應用程式有兩個不同的路徑來獲取相同證書,這打破了密鑰輪換設計中的許多假設。
確認
在 Android 9 及更高版本中,可以根據 APK 簽章方案 v3、v2 方案或 v1 方案驗證 APK。較舊的平台會忽略 v3 簽名並嘗試驗證 v2 簽名,然後驗證 v1。
APK簽章方案v3驗證
- 找到 APK 簽名區塊並驗證:
- APK 簽名區塊的兩個大小欄位包含相同的值。
- ZIP 中央目錄後面緊跟著 ZIP 中央目錄結束記錄。
- ZIP 中央目錄末尾後面沒有更多資料。
- 找到 APK 簽章區塊內的第一個 APK 簽章方案 v3 區塊。如果存在 v3 區塊,請繼續步驟 3。否則,請回退到使用 v2 方案驗證 APK。
- 對於 APK 簽章方案 v3 區塊中的每個
signer
,其最小和最大 SDK 版本都在目前平台的範圍內:- 從
signatures
中選擇支援的最強signature algorithm ID
。強度排序取決於每個實作/平台版本。 - 使用
public key
根據signatures
signed data
驗證相應的signature
。 (現在可以安全地解析signed data
。) - 驗證簽章資料中的最小和最大 SDK 版本是否與
signer
指定的版本相符。 - 驗證
digests
和signatures
中的簽名演算法 ID 的有序列表是否相同。 (這是為了防止簽名剝離/添加。) - 使用與簽章演算法使用的摘要演算法相同的摘要演算法來計算 APK 內容的摘要。
- 驗證計算出的摘要與
digests
中對應的digest
相同。 - 驗證
certificate
中第一個certificates
的SubjectPublicKeyInfo 是否與public key
相同。 - 如果
signer
存在旋轉證明屬性,請驗證結構是否有效且該signer
是清單中的最後一個憑證。
- 從
- 如果在目前平台的範圍內恰好找到一個
signer
且步驟 3 對於該signer
成功,則驗證成功。
驗證
若要測試您的裝置是否已正確支援 v3,請執行cts/hostsidetests/appsecurity/src/android/appsecurity/cts/
中的PkgInstallSignatureVerificationTest.java
CTS 測試。