(轉(zhuǎn))細(xì)說iOS代碼簽名(一)

轉(zhuǎn)自 http://xelz.info/blog/2019/01/11/ios-code-signature-1/,版權(quán)歸原作者所有

導(dǎo)航

0x01 簽名的作用

數(shù)字簽名其實(shí)跟我們手寫的簽名類似,代表一個(gè)特定的主體(簽名者)對(duì)特定內(nèi)容(被簽名數(shù)據(jù))的署名和認(rèn)可,簽名是對(duì)信息發(fā)送行為真實(shí)性的有效保障。數(shù)字簽名在很多領(lǐng)域都有應(yīng)用,iOS的代碼簽名正是其中最典型的一種,我們可以先嘗試分析一下iOS上代碼簽名的目的和好處。

安全性

代碼簽名的首要任務(wù)是保證設(shè)備及系統(tǒng)的安全性,只有被蘋果設(shè)備認(rèn)可的證書簽名的代碼才能夠被執(zhí)行,否則在安裝或者運(yùn)行時(shí)會(huì)因?yàn)闊o法通過內(nèi)核的簽名校驗(yàn)而失敗。iOS的系統(tǒng)中內(nèi)置了來自蘋果的CA證書,系統(tǒng)自身的代碼都是被蘋果”簽名“過的, 而用戶從AppStore下載的App也都已被蘋果官方進(jìn)行簽名。簽名機(jī)制可以有效地防止來自外部的攻擊。

這里存在兩種場(chǎng)景:

  • 第一種是對(duì)系統(tǒng)本身的攻擊,比如越獄,假如黑客發(fā)現(xiàn)了內(nèi)核任意讀寫的漏洞,借此注入提權(quán)代碼,但是這些代碼會(huì)因?yàn)闆]有合法的簽名而被系統(tǒng)拒絕運(yùn)行,也就自然無法對(duì)系統(tǒng)造成實(shí)質(zhì)性的破壞。
  • 第二種是對(duì)設(shè)備或者用戶的攻擊,眾所周知,提交到AppStore的應(yīng)用代碼都會(huì)經(jīng)過蘋果的審查,包含惡意代碼的App是無法上架的。此時(shí),黑客可能會(huì)嘗試先提交一個(gè)正常的App,通過各種技術(shù)手段躲避Apple的審查,上架后從網(wǎng)絡(luò)上下載惡意代碼并加載執(zhí)行,但這種方式也會(huì)因?yàn)楹灻缓戏ǘ ?/li>

沙盒

除了能夠避免非授權(quán)的惡意代碼運(yùn)行,代碼簽名還可以有效地限制app的行為,這部分功能主要是由Sandbox機(jī)制來保證,但Sandbox的配置是綁定在簽名中的,就是通常所說的Entitlements文件。試想,如果Entitlements文件可以被任意修改,那么Sandbox也就失去了意義,所以Entitlements文件也是強(qiáng)制簽名保護(hù)的對(duì)象。對(duì)于越獄來說,如果無法繞過簽名和Sandbox,再強(qiáng)大的提權(quán)漏洞也無計(jì)可施。

壟斷

代碼簽名還給蘋果帶來了一個(gè)巨大的好處:App分發(fā)的絕對(duì)控制權(quán)。在iOS平臺(tái)上(面向未越獄的用戶)公開發(fā)行App的合法途徑有且只有一種,就是上傳到蘋果官方的AppStore供用戶下載。蘋果會(huì)對(duì)App進(jìn)行嚴(yán)格的審查并簽名,App的功能及支付渠道也因此可以受蘋果的嚴(yán)格管制,這為蘋果帶來的經(jīng)濟(jì)效益不言而喻。

0x02 什么是簽名

簽名的本質(zhì)是用于驗(yàn)證數(shù)據(jù)的合法性,確保被簽名的數(shù)據(jù)來自特定的來源,并且未經(jīng)篡改。它基于非對(duì)稱加密,和哈希算法,研究簽名之前需要對(duì)這兩種算法有一定的了解。

公鑰加密算法

也叫非對(duì)稱加密,它在加密和解密時(shí)使用的是不同的密鑰,具有這樣的特征:

  • 有一對(duì)密鑰 ab ,滿足 a ≠ b
  • 用密鑰a加密的數(shù)據(jù)只能用b進(jìn)行解密,a自身無法解密,反之亦然
  • 只知道其中一個(gè)密鑰,無法推導(dǎo)出另一個(gè)
  • 把其中一個(gè)可以公開的叫做公鑰,另一個(gè)不能公開的叫做私鑰。

最常見的公鑰加密算法是RSA公鑰加密算法,也是簽名中普遍使用的算法。其數(shù)學(xué)原理如下:

  • 選定兩個(gè)超大的素?cái)?shù)p, q,并計(jì)算他們的乘積n = p * q
  • 計(jì)算歐拉函數(shù) φ(n) = φ(p) * φ(q) = (p-1) * (q-1)
  • 隨機(jī)選定一個(gè)數(shù)e,滿足1 < e < φ(n) ,且與φ(n)互質(zhì)
  • 根據(jù)擴(kuò)展歐幾里得算法計(jì)算e對(duì)于φ(n)的乘法逆元d ,e * d = 1 mod φ(n)
  • {n, e}{n, d} 分別組成這個(gè)算法的一對(duì)密鑰
  • 對(duì)于給定明文p, 若使用{n, e} 作為加密密鑰,其密文計(jì)算方法為 c = p ^ e mod n
    • 這是一個(gè)單向函數(shù),已知{c, n, e} 無法計(jì)算出p
  • 相應(yīng)地需要使用{n, d}進(jìn)行解密, p' = c * d mod n
    • 這是上一步加密函數(shù)的逆函數(shù)
  • 兩組密鑰中n是相同的,那么如果已知了ed其中的一個(gè),想要計(jì)算另一個(gè),必須知道φ(n),也就是必須先將n分解質(zhì)因數(shù),得到pq,但由于n的值非常大,這樣的計(jì)算量基本上是不可能的,也就保障了算法的安全性

理論上 {n, e}{n, d} 可以互換,任何一個(gè)都可以是公鑰或者私鑰,加密和解密的函數(shù)也可以互換。但實(shí)踐中,一般固定設(shè)置e=65537(0x10001),相當(dāng)于公開的一個(gè)約定,這樣一來{n, e}就只能作為公鑰使用。

哈希算法

也叫散列或者摘要算法,對(duì)一段任意長(zhǎng)度的數(shù)據(jù),通過一定的映射和計(jì)算,得到一個(gè)固定長(zhǎng)度的值,這個(gè)值就被稱為這段數(shù)據(jù)的哈希值(hash)。給定一個(gè)哈希算法,它一定具有以下特征:

  • 哈希值不同的兩段數(shù)據(jù)絕對(duì)不同
  • 相同的數(shù)據(jù)計(jì)算出的哈希值絕對(duì)相同
  • 由于哈希值是固定長(zhǎng)度, 也就意味著哈希值的數(shù)量是有限的。而任意數(shù)據(jù)都可以計(jì)算出一個(gè)哈希值,計(jì)算哈希的過程,相當(dāng)于無限集到有限集的映射。因此哈希值相同,對(duì)應(yīng)的原始數(shù)據(jù)不一定相同,如果不同,則稱這兩段數(shù)據(jù)存在哈希碰撞,實(shí)際應(yīng)用中認(rèn)為這是小概率事件(數(shù)學(xué)意義上的"不可能事件"),優(yōu)秀的哈希算法都是碰撞率極低的。
  • 哈希算法是單向算法,無法通過哈希值,計(jì)算出原始數(shù)據(jù),這一點(diǎn)非常重要!

常見的哈希算法有: md5, sha1, sha256等,其中sha1長(zhǎng)度為160bits,而sha256長(zhǎng)度為256bits,二者相比,sha256的取值范圍更大,因此碰撞和破解的概率更低,也就相對(duì)更安全。

簽名算法

有了上面這兩種算法作為基礎(chǔ),就可以組建一個(gè)簽名和驗(yàn)證簽名的體系了,如下圖所示

假如A要給B發(fā)送一段數(shù)據(jù)d,先對(duì)其簽名:

  • 計(jì)算d的哈希值h,并使用自己的私鑰a 對(duì) h 進(jìn)行加密,得到的密文c就是簽名

得到簽名后,將數(shù)據(jù)d和簽名c通過某種方式發(fā)送給B,此時(shí)B收到了數(shù)據(jù)d'以及簽名c',需要驗(yàn)證這段數(shù)據(jù)是否被篡改,以及是否是A發(fā)送的

  • 計(jì)算d'的哈希值h',使用A的公鑰b將簽名c'解密,得到h''。通過對(duì)比h'h''是否一致,就可以知道數(shù)據(jù)或簽名是否被篡改。并且,如果哈希值是匹配的,能夠說明這段數(shù)據(jù)一定是由A簽名并發(fā)出的

常見的簽名算法:

  • sha1WithRSAEncryption:先對(duì)數(shù)據(jù)計(jì)算sha1摘要,再對(duì)摘要進(jìn)行RSA加密
  • sha256WithRSAEncryption:先對(duì)數(shù)據(jù)計(jì)算sha256摘要,再對(duì)摘要進(jìn)行RSA加密
  • md5WithRSAEncryption:先對(duì)數(shù)據(jù)計(jì)算MD5摘要,再對(duì)摘要進(jìn)行RSA加密

證書

上面這個(gè)例子中,任何需要接受A的消息的人都需要事先保存A的公鑰。這樣的方案存在一個(gè)很大的問題:公鑰如何分發(fā)?如果B要接受來自很多不同來源的數(shù)據(jù),不可能事先將所有來源的公鑰都提前保存下來,并且這樣無法適應(yīng)來源變動(dòng)(增加、刪除、變更)等帶來的變化。因此,一般會(huì)把公鑰當(dāng)做簽名的一部分,隨著數(shù)據(jù)一起分發(fā),接收方不需要事先保存任何數(shù)據(jù)來源的公鑰。

sign_verify1.png

但是這樣會(huì)引入一個(gè)新的問題:如何知道數(shù)據(jù)中所攜帶的公鑰就是否是發(fā)送者自己的公鑰?

這涉及到密鑰的管理和分發(fā),細(xì)節(jié)展開的話是一個(gè)非常大的課題。簡(jiǎn)單來說,可以把公鑰和所有者的信息保存在一個(gè)文件里,并讓一個(gè)可信的第三者使用其私鑰對(duì)這個(gè)文件進(jìn)行簽名,得到一個(gè)簽了名的公鑰文件,這個(gè)文件就叫做證書。證書會(huì)作為簽名的一部分,隨著數(shù)據(jù)一起分發(fā)。

這里出現(xiàn)了一個(gè)有意思的事情,數(shù)據(jù)簽名中的證書本身也是一段數(shù)據(jù)(公鑰+所有者信息)以及其簽名組成的,但證書中的簽名是簡(jiǎn)單簽名,一般只有哈希值和簽發(fā)者名稱,不會(huì)再將簽發(fā)者的證書包含在簽名中,否則就陷入無限遞歸的死循環(huán)了。

此時(shí)我們還需要使用第三者的公鑰驗(yàn)證這個(gè)證書的合法性。雖然需要多驗(yàn)證一步,但是這樣一來,本地不再需要保存每個(gè)數(shù)據(jù)來源的公鑰,只需要保存這個(gè)第三者的證書(公鑰)即可,每個(gè)數(shù)據(jù)來源的證書都由這個(gè)可信的第三者進(jìn)行簽發(fā),這個(gè)可信的第三者就被稱為證書頒發(fā)機(jī)構(gòu)(Certification Authority),簡(jiǎn)稱CA。

實(shí)際上,CA的證書可能也是由其他更高一級(jí)的CA進(jìn)行簽發(fā)的,這種情況會(huì)產(chǎn)生3級(jí)甚至3級(jí)以上的證書鏈,系統(tǒng)中只需要保存最高級(jí)CA的證書,中間CA的證書和信息提供者的證書依次進(jìn)行遞歸校驗(yàn)即可。

可以通過這個(gè)命令導(dǎo)出Xcode應(yīng)用中可執(zhí)行程序的簽名證書,mac OS上的代碼簽名格式與iOS平臺(tái)是相同的

$ codesign -d --extract-certificates=cert /Applications/Xcode.app/Contents/MacOS/Xcode

當(dāng)前文件夾下會(huì)產(chǎn)生三個(gè)證書文件cert0 cert1 cert2。其中cert0是由cert1簽發(fā)的,可以使用cert1驗(yàn)證其合法性,同理cert2可以驗(yàn)證cert1的合法性。而對(duì)于cert2,只需要對(duì)比系統(tǒng)的keychain中是否有相同的證書文件即可。通過下面的命令可以分別查看他們的所有者名稱:

$ for i in 0 1 2; do openssl x509 -inform DER -text -noout -in cert$i | grep Subject:; done
        Subject: CN=Apple Mac OS Application Signing, O=Apple Inc., C=US
        Subject: C=US, O=Apple Inc., OU=Apple Worldwide Developer Relations, CN=Apple Worldwide Developer Relations Certification Authority
        Subject: C=US, O=Apple Inc., OU=Apple Certification Authority, CN=Apple Root CA

本篇完。


下一篇: 細(xì)說iOS代碼簽名(二):開發(fā)者證書、Entitlements、Provisioning Profile

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容