A. android | 他山之石,可以攻玉!一篇文章看懂 v1/v2/v3 簽名機制
這篇文章的內容會涉及以下前置 / 相關知識,貼心的我都幫你准備好了,請享用~
數字簽名(Digital Signature)也叫作數字指紋(Digital Fingerprint),它是消息摘要演算法和非對稱加密演算法的結合體,能夠驗證數據的完整性,並且認證數據的來源 。
數據簽名演算法的模型分為兩個主要階段:
需要注意的是,Android 目前不對應用證書進行 CA 認證,應用可以由第三方(OEM、運營商、其他應用市場)簽名,也可以自行簽名。
應用 APK 其實是一種特殊的 Zip 壓縮包,無法避免惡意破解者解壓 / 反編譯修改內容,針對這個問題有何解決方案呢?他山之石,可以攻玉 ——數字簽名演算法。應用簽名正是數字簽名演算法的應用場景之一,與其他應用場景類似,目的無非是:
Android 平台上運行的每個應用都必須有開發者的簽名。在慧喚安裝應用時,軟體包管理器會驗證 APK 是否已經過適當簽名,安裝程序會拒絕沒有獲得簽名就嘗試安裝的應用。
軟體包管理器在安裝應用前會驗證應用摘要,如果破解者修改了 apk 里的內容,那麼摘要就不再匹配,驗證失敗(驗證流程見下文方案)。
截止至 Android 11,Android 支持以下三種應用簽名方案:
為了提高兼容性,必須按照 v1、v2、v3 的先後順序採用簽名方案,低版本平台會忽略高版本的簽名方案在 APK 中添加的額外數據。
v1 簽名方案是基於 Jar 的簽名。
首先,我們先來分析其簽名產物。 v1 簽名後會增加 META-INF 文件夾 ,其中會有如下三個文件。考慮到使用不同的證書和簽名方式,得到的文件名可能不同,因此你只要留意文件的後綴即可:
v1 簽名流程如下:
MANIFEST.MF(Message Digest File,摘要文件)
*.SF(Signature File,簽名文件)
驗證流程可以分為驗證簽名和驗證完整性兩個步驟:
驗證簽名步驟:
如果上述簽名驗證結果正確,才會驗證完整性:
以上任何步驟驗證失敗,則整個 APK 驗證失敗。
為了解決這些問題,Android 7.0 中引入了 APK 簽名方案 v2。
v2 簽名方案是一種 全文件簽名方案 ,該方案能夠發現對 APK 的受保護部分進行的所有更改,相對於 v1 簽名方案驗證速度更快,完整性覆蓋范圍更廣。
在分析 v2 簽名方案之前,我們先簡單了解一下 Zip 文件格式:
首先,我們先來分析其簽名產物。v2 簽名後會在 「條目內容區」和「中央目錄區」之間插入「APK 簽名分塊(APK Signing Block)」 。
從左到右邊,我們定義為區塊 1~4。
相對與 v1 簽名方案,v2 簽名方案不再以文件為單位計算摘要了,而敬坦是以 1 MB 為單位將文件拆分為多個連續的塊(chunk),每個分區的最後一個塊可能會小於 1 MB。
v2 簽亮碧桐名流程如下:
驗證流程可以分為驗證簽名和驗證完整性兩個步驟:
簽名方案 v3 支持密鑰輪換,應用能夠在 APK 更新過程中更改其簽名密鑰。
【累了,後面先不寫了...】
這一節,我們介紹基於 Android 應用簽名機制的衍生應用場景。
在 v1 方案中, MANIFEST.MF 和 *.SF 這兩個文件會記錄大量的文件名和文件摘要。如果 apk 中文件數很多,而且文件名很長,那麼這兩個文件會變得很大。使用 AndResGuard 工具,可以將文件名轉換為短路徑文件名,從而減少這兩個文件的大小。
在實際生產中,往往需要生成多個渠道的 APK 包,傳統的方法是使用 APKTool 逆向工具、Flavor + BuildType 等方案,這一類多渠道打包方案的缺點是耗時嚴重。隨著 Android 應用簽名方案的演進,演變出了不同的多渠道打包方案:
在 v1 方案中,我們提到了完整性校驗不覆蓋到 META-INF 文件夾的問題。有些多渠道打包方案就是利用了這個問題,在 META-INF 文件夾下添加空文件, 用空文件的名稱來作為渠道的唯一標識 ,就可以節省打包的時間,提高打渠道包的速度。
除了添加空文件的方法,還可以向 APK 添加 Zip Comment 來生成多渠道包(APK 本身就是特殊的 Zip 包)。
在 v2 簽名方案中,幾乎整個 APK 都納入保護范圍,如果向 APK 添加空文件或 Zip Comment 的話,在安裝時會報以下錯誤:
新背景下的多渠道打包方案,則是利用了 APK 簽名分塊(區塊 2)不受保護 & 欄位可擴展的特點 ,向區塊中添加多渠道信息(ID-Value),例如 美團多渠道打包方案 Walle 。
B. 如何判斷 Android 應用的 Apk 簽名是否一致
一、判斷Apk是否簽名
用命令:jarsigner -verify -verbose -certs <apk文件>
1、如果有Android Debug字樣就是debug
2、如果已經簽名: [證書的有效期為13-8-31 下午2:31至41-1-16 下午2:31]
二、判斷Apk簽名是否一致
jdk 需要安裝;想查demo.apk所使用的簽名的fingerprint,可以這樣做:
1、查找apk里的rsa文件
Windows 平台:
> jar tf demo .apk |findstr RSA
Linux 平台:
$ jar tf demo .apk |grep RSA
META-INF/CERT.RSA
2、 從apk中解壓rsa文件
jar xf demo .apk META-INF/CERT.RSA
3、獲取簽名的fingerprints
keytool -printcert -file META-INF/CERT.RSA
證書指紋:
MD5: 5A:5A:96:63:8E:EF:FC:66:9E:BC:1C:2A:A9:1E:E5:95
SHA1: 44:BD:33:2D:C5:21:AE:78:D5:04:92:1A:39:FD:AC:01:E2:32:3C:AB
SHA256: 2F:C0:A3:8C:0D:42:84:70:48:78:44:A4:2E:64:5B:50:B3:B3:1E:33:94:62:A3:9F:2F:10:DD:EF:D7:CF:02:0B
簽名演算法名稱: SHA1withRSA
版本: 3
兩個apk是否同簽名,比較簽名的MD5碼或SHA1碼 ,一樣就是相同的,反之,不是。
C. Android獲取簽名文件的sha1值
1.打開終端,輸入:
keytool -v -list -keystore +簽名文件路徑
2.輸入簽名文件密碼即可獲取到簽名文件所有信息:
創建日期: 2018-11-8 條目類型: PrivateKeyEntry 證書鏈長度: 1 證書[1]: 所有者: CN=fang 發布者: CN=fang 序列號: 4551a2f8 有效期開始日期: Thu Nov 08 11:17:27 CST 2018, 截止日期: Mon Nov 02 11:17:27 CST 2043 證書指紋: MD5: 2B:85:...... SHA1: 97:7D:...... SHA256: 1A:C5:F7:5C:...... 簽名演算法名稱: SHA256withRSA 版本: 3
D. 一文弄懂關於證書,簽名,ssl,android包簽名機制。
所有的概念都基於一個非常重要的基礎:
rsa 非對稱加密演算法 :
先感受下幾個概念
PKI。
PKI是公鑰基礎設施(Public Key Infrastructure) 包括PKI策略、軟硬體系統、證書機構CA、注冊機構RA、證書發布系統和PKI應用等。
我們關注就倆東西: PKCS 證書機構CA 。前者是定義加密演算法,簽名,證書相關的各種事情採用的協議。後者可以為我們頒發權威的證書。
PKCS :
PKCS(The Public-Key Cryptography Standards )是由美國RSA數據安全公司及其合作夥伴制定的一組公鑰密碼學標准,其中包括證書申請、證書更新、證書作廢表發布、擴展證書內容以及數字簽名、數字信封的格式等方面的一系列相關協議。RSA演算法可以做加密、解密、簽名、驗證,還有RSA的密鑰對存儲。這些都需要標准來規范,如何輸入,如何輸出,如何存儲等。
PKCS。全稱是公鑰密碼學標准, 目前共發布過 15 個標准,這些標准都是協議。總結一下 就是對加密演算法,簽名,證書協議的描述。下面列舉一些常用的協議,這些協議在本文都會對應上。
這些協議具體的實現就體現在openssl等工具中, 以及jdk工具keytool jdk java第三方庫bouncycastle。
比如用openssl 如何生成公/私鑰(PKCS#1)、簽名(PKCS#1 )、簽名請求文件(KCS#10)、 帶口令的私鑰(PKCS#8)。 含私鑰的證書(PKCS#12)、證書庫(PKCS#12)
其中涉及到演算法的基礎協議PKCS#1等,由於涉及到密碼學原理所以我們並不需要深究它,只要知道怎麼做就可以了。
現實中我們要解決這樣一種情況:
客戶端和伺服器之間的數據要進行加密。需要兩個達成同一個對稱秘鑰加密才行,那麼這個秘鑰如何生成,並在兩邊都能拿到,並保證傳輸過程中不被泄露。 這就用到非對稱加密了。 後續的傳輸,就能用這個 對稱秘鑰來加密和解密了。
還有這樣一個問題:
就是客戶端如何判斷服務端是否是合法的服務端。這就需要服務端有個id來證明它,而這個id 就是證書,而且必須是權威機構頒發的才能算是合法的。
因為客戶端即瀏覽器,認定證書合法的規則必須通過第三方來確認 即ca頒發的證書。否則就我可能進了一個假網站。
而這兩個問題 都是ssl協議要解決的內容。
所以ssl協議做了兩件事情,一是驗證身份,二是協商對稱秘鑰,並安全的傳輸。 而實現這個過程的關鍵數據模型就是證書, 通過證書中的ca對證書的簽名,實現了身份驗證,通過證書中的公鑰,實現對對稱秘鑰加密,從而實現數據保密。 其實還順手做了一件事情就是通過解密簽名比對hash,保證了數據完整性。
明白ssl協議 首先明白幾個重要的概念:
證書: 顧名思義就是提供了一種在Internet上驗證通信實體身份的方式,數字證書不是數字身份證,由權威公正的第三方機構,即CA(例如中國各地方的CA公司)中心簽發的證書, 就是可以認定是合法身份的。客戶端不需要證書。 證書是用來驗證服務端的。
一般的證書都是x509格式證書,這是一種標準的證書,可以和其他證書類型互相轉換。完整來說證書包含,證書的內容,包括 版本號, 證書序列號, hash演算法, 發行者名稱,有效期, 公鑰演算法,公鑰,簽名(證書原文以及原文hash一起簽名)而這個內容以及格式 都是標准化的,即x509格式 是一種標準的格式。
簽名: 就用私鑰對一段數據進行加密,得到的密文。 這一段數據在證書的應用上就是 對證書原文+原文hash進行簽名。
誰簽的名,就是用誰的私鑰進行加密。就像身份證一樣, 合法的身份證我們都依據是政府簽的,才不是假證, 那就是瀏覽器會有政府的公鑰,通過校驗(解密)簽名,如果能夠解密,就可以確定這個就是政府的簽名。就對了。
hash演算法 :對原始數據進行某種形式的信息提取,被提取出的信息就被稱作原始數據的消息摘要。比如,MD5和SHA-1及其大量的變體。 hash演算法具有不可逆性,無法從摘要中恢復出任何的原始消息。長度總是固定的。MD5演算法摘要的消息有128個比特位,SHA-1演算法摘要的消息最終有160比特位的輸出。
ca機構: 權威證書頒發機構,瀏覽器存有ca的公鑰,瀏覽器以此公鑰來驗證服務端證書的合法性。
證書的獲取: 生成證書申請文件.csr(涉及到PKCS#10定義的規范)後向ca機構申請。 或者自己直接通過生成私鑰就可以一步到位生成自簽名證書。 自簽名證書就是用自己的私鑰來簽名證書。
那麼為了體現到 證書身份認證、數據完整、保密性三大特性 ,證書的簡化模型可以認為包含以下兩個要素:伺服器公鑰,ca的簽名(被ca私鑰加密過的證書原文+原文hash),
身份認證:
瀏覽器存有ca公鑰,用ca公鑰解密網站發給你的證書中的簽名。如果能解密,說明該證書由ca頒發,證書合法。 否則瀏覽器就會報警告,問你是否信任這個證書,也就是這個網站。這時候的證書可以是任何人簽發的,可以自己簽發的。 但是中間人攻擊。 完全偽造新的證書, 這就沒有辦法了。 所以還是信任證書的時候要謹慎。
數據完整:
如果你信任該證書的話,這時候就會用證書中的公鑰去解密簽名,如果是ca簽發的證書,那麼之前就已經通過ca的公鑰去解密簽名了。 然後得到證書hash,然後在瀏覽器重新對證書做hash,兩者比對一致的話,說明證書數據沒有被篡改。
保密性:
使用證書的公鑰對對稱秘鑰加密保證傳輸安全,對稱秘鑰生成後,後續的傳輸會通過對稱秘鑰來在服務端和客戶端的加解密。
那麼ssl協議的具體過程就是:
4.網站接收瀏覽器發來的數據之後 使用自己的私鑰校驗簽名,並對原文進行hash 與解密出的hash 做比對檢查完整性。然後發送編碼改變通知,伺服器握手結束通知(所有內容做hash )。 發送給客戶端校驗。
5 客戶端校驗,校驗成功後,之後就用 對稱秘鑰進行通信了。
總共的過程是 c-s-c- s-c 四次握手。
四次握手簡單來說分別是:
1.請求獲取證書
2.服務端返回證書,客戶端驗證了證書的合法性和完整性,同時生成了對稱秘鑰。
3.客戶端把加密的 對稱秘鑰發給伺服器。伺服器檢查真實性和完整性。
4.服務端返回握手結束通知,客戶端再檢查一次真實性和完整性。
前兩次握手是明文, 後兩次握手是密文。 所以都要檢查身份真實性和數據完整性。
ca的作用:
ca起到一個權威中間人的角色,如果脫離了ca, 那麼證書還是證書,還能加密,保證數據完整性。 但是無法應用在客戶端去認定伺服器身份合法這個場景下。
下面就詳細說下 脫離了ca簽發的證書的應用:
自簽名證書:
證書如果沒有權威機構的簽名,就是沒有權威機構給你簽發身份證。 那麼這時候身份認證的場景變了。
這時候的認證場景就變成了,不再是某個官方權威說了算,而是假設第一次碰到這個證書,會認為,這個證書與之捆綁的實體之間是合法的並做記錄。如果當這個實體下次捆綁了另一個證書,那麼就是非法的。
這種情況常用於android中安裝和校驗app的時候,會先假設第一次安裝的是合法的應用,認定這個app證書中的公鑰是合法的公鑰。然後通過自簽名的證書,校驗簽名,就能實現後續安裝是否合法以及完整性。
android中的如何對app進行身份認定和不被篡改:
android系統在安裝app時候會進行校驗applicationId,applicationId 不同會認定為不同應用。相同應用,第二次安裝會校驗證書是否和之前app的證書相同,如果相同則兩個包很可能來自同一個身份。 如果證書不同,也就是該包被另一個身份用自己的私鑰重新簽名過,就會拒絕安裝。 然後通過公鑰來解密簽名,如果能解密,說明身份是ok的。否則拒絕安裝。比對解密簽名後的hash 與apk包內的cert.sf文件(該文件是apk內所有文件生成的hash文件)是否一致,如果相同則認定為沒有被篡改。
android在提交應用商店的問題:
應用商店也會校驗 後續的上傳和第一次上傳時的證書,以及類似上述的後續的一系列校驗。防止合法的開發者平台被盜後,上傳非法應用。
android在接入第三方sdk的問題:
接入第三方sdk 會提交applicationId 和 sha1 值。 這個sha1值就是對 證書原文的簽名後的sha1,也就是證書指紋。這個證書是證書庫里最初的那個證書(x509格式),而不是對apk簽名後生成的證書(PKCS#7)。一般的證書簽名的主體是證書原文本身,而對apk簽名還額外會對apk所有文件生成的hash值文件(cert.sf)進行一次簽名。
第三方平台會記錄 applicationId 與sha1 的對應關系。 當有假冒app試圖接入時候,由於會對app內的PKCS#7證書轉換為原始的x509格式證書,重新生成sha1值,與用戶提交sha1 比對, 如果相同則說明證書很可能是ok的。 因為sha1就是證書的指紋。 之後就會通過證書中的公鑰來校驗簽名,從而最終確認身份合法性以及信息完整性。
第三方平台之所以需要用戶去提交證書指紋sha1值,多了這一步,就意味著你的證書是可以更換的,一旦更換了證書,就必須提交新的指紋給我,然後我來做匹配。而應用商店沒有這個功能, 一旦你的證書的私鑰丟了, 那就必須重新建一個新的app。
總結來看證書的身份認定機制:
在ssl協議下,這種場景是 瀏覽器用於認定合法的伺服器身份。 在自簽名證書下,需要用戶選擇是否信任該證書。
在android app採用自簽名證書的場景下, 證書起到了 假設第一次的證書合法,公鑰合法,後續如果證書不一致或不能夠完成簽名校驗,就是非法。
證書庫:
證書庫應該滿足PKCS#12協議。 但是jdk提供了製作證書的工具keytool 可以生成keystore類型的證書庫,後綴為jks。 keystore pk12可以通過keytool命令互相轉換。
證書庫是個證書的容器, 可以用來創建數字證書。 在keystore證書庫中,所有的數字證書是以一條一條(採用別名alias區別)的形式存入證書庫的。證書庫中的證書格式為pk12,即包含私鑰。 如果導出證書的話, 可以導出為x509不包含私鑰的格式 或者pk12包含私鑰的證書。 也可以也可以用-import參數加一個證書或證書鏈到信任證書。
android中一般都採用讀取證書庫的方式,通過證書庫來創建一個證書,通過alias來區分。 所以在簽名的時候,一個alias是一個證書,不同的alias是不同的證書,不要搞錯了。
幾個關系:
證書和非對稱加密演算法的關系:
證書代表一個身份的主體,包含了非對稱秘鑰體系中的公鑰,以及用私鑰對證書簽名。這種組織結構,把非對稱加密演算法從加密的功能,拓寬到了用於身份認證,信息完整性上。這體現在了證書的作用。 本質還是利用了非對稱加密演算法的特性。
ssl協議和證書的關系。
因為證書解決了客戶端對伺服器的身份認證(自簽名證書除外),同時也解決了加密,和信息完整性,所以ssl協議基於證書來實現。
E. 如何查看apk和簽名文件的簽名信息
1.通過apktool工具解壓當前apk(比如:demo.apk),解壓後,會生成CERT.RSA文件
**** apktool d demo.apk
備註:通過這種方式解壓出來的Androidmenifest.xml文件可以通過文件編輯器打開查看;
2.通過keytool命令獲取簽名信息
**** keytool -printcert -file C:\Android\soft\apktool\demo\original\META-INF\CERT.RSA所有者: CN=ryt, OU=ryt, O=ryt, L=nanjing, ST=china, C=cn
發布者: CN=ryt, OU=ryt, O=ryt, L=nanjing, ST=china, C=cn
序列號: 12893889
有效期開始日期:
證書指紋:
MD5: 1A:0F:52:94:8D:DB:7A:E9:7A:7E:A5:3B:A3:A7:F9:39
SHA1: 89:57:4E:E2:1F:CB:68:CB:66:80:D6:29:57:92:AD:AC:51:4F:76:F2
SHA256: BF:FB:9C:67:E1:7B:49:26:4A:D9:84:9D:48:DA:C1:4F:00:77:74:15:27:90:95:D0:B3:D8:0A:EA:2F:81:F1:B9
簽名演算法名稱: SHA256withRSA
版本: 3
擴展:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: C3 D6 E3 9F C3 F3 1E BE 6E F9 BC 35 6F 8B 21 52 ........n..5o.!R
0010: 12 38 AD BC .8..
]
]
3.查看my.keystore簽名文件的簽名信息
**** keytool -list -keystore C:\Android\workspace\my.keystore
輸入密鑰庫口令:
密鑰庫類型: JKS
密鑰庫提供方: SUN
您的密鑰庫包含 1 個條目
my, 2016-6-15, PrivateKeyEntry,
證書指紋 (SHA1): 89:57:4E:E2:1F:CB:68:CB:66:80:D6:29:57:92:AD:AC:51:4F:76:F2