轉: iOS App 簽名原理

iOS簽名機制挺復雜,各種證書,Provisioning Profile, entitlements, CertificateSigningRequest, p12, AppID, 概念一堆,很容易出錯。本文嘗試從原理出發,一步步推出為什么會有這么多概念,希望能有助于理解iOS App簽名的原理和流程。

目的

先來看看蘋果的簽名機制是為了做什么。在iOS出來之前,在主流操作系統(mac、windows、linux)上開發和運行軟件是不需要簽名的,軟件隨便在哪里下載都可以運行,導致平臺對第三方軟件難以控制、盜版橫行。蘋果希望解決這樣的問題,在iOS平臺對第三方APP有絕對的控制權,一定要保證每一個安裝到iOS的APP都是經過蘋果允許的,怎么保證呢?就是通過簽名機制。

非對稱加密

通常我們說的簽名就是數字簽名,他是基于非對稱加密算法實現的。對稱加密是通過同一份密鑰加密和解密數據,而非對稱加密則有兩份密鑰,分別是公鑰和私鑰,用公鑰加密的數據要用私鑰才能解密,用私鑰加密的數據要用公鑰才能解密。
簡單說一下常用的非對稱加密算法RSA的數學原理,理解簡單的數學原理,就可以理解非對稱加密是怎么做到的,為什么是安全的:

  1. 選兩個質數p和q,相乘得出一個大整數n,例如p=61,q=53, n=pq=3233
  2. 選1-n中任意一個質數e,例如e=17
  3. 經過一系列數學運算,算出一個數字d,滿足:
    a. 通過n和e這兩個數據進行數學運算后,可以通過n和d 去反解運算,反過來也可以。
    b. 如果只知道n和e,要推導出d,需要知道p和q,也就是需要把n因式分解。
    上述的(n,e)這兩個數據在一起就是公鑰,(n,d)這兩個數據在一起就是私鑰,滿足用私鑰加密、公鑰解密,或者公鑰加密、私鑰解密,也滿足只暴露公鑰(只知道n和e)的情況下,要推導出私鑰(n,d),需要將大整數n因式分解。目前因式分解只能用暴力窮舉,而n的數字越大,越難以用窮舉計算出因數p和q,也就越安全,當n大到二進制的1024位或2048位時,以目前的技術要破解幾乎不可能,所以非常安全。

數字簽名

現在知道了有非對稱加密這東西,那數字簽名是怎么回事呢?
數字簽名的作用是我對某一份數據打個標記,表示我認可了這份數據(簽了個名),然后我發送給其他人,其他人可以知道這份數據是經過我認證的,數據沒有被篡改過。
有了上述非對稱加密算法,就可以實現這個需求:

  1. 首先用一種算法算出原始數據的摘要。需滿足:
    a. 若原始數據有任何變化,計算出來的摘要值都會變化;
    b. 摘要要足夠短。這里常用的算法是MD5。

  2. 生成一份非對稱加密的公鑰和私鑰,私鑰我自己拿著,公鑰發布出去。

  3. 對一份數據,算出摘要后,用私鑰加密這個摘要,得到一個加密后的數據,稱為原始數據的簽名。把它和原始數據一起發送給用戶。

  4. 用戶收到數據和簽名后,用公鑰解密簽名得到摘要。同時用戶用同樣的算法計算出原始數據的摘要,對比計算出來的摘要和解密出來的摘要是否相等,若相等這表示這份數據中途沒有被篡改過,因為如果被篡改過,摘要會發生變化。

之所以要有第1步的計算摘要,是因為分對稱加密原理限制可加密數據的內容不能太大(不能大于上數n的位數,也就是一般不能大于1024位/2048位),于是若要對任意大的數據簽名,就需要改成對他的特征值簽名,效果,效果是一樣的。

好了,有了非對稱加密的基礎,知道了數學簽名是什么,怎樣可以保證一份數據是經過某個地方認證的,來看看怎樣通過數字簽名的機制保證每一個安裝到iOS上的APP都是通過蘋果認證允許的。

最簡單的簽名

要實現這個需求很簡單,最直接的方式,蘋果官方生成一對公私鑰,在iOS里內置一個公鑰,私鑰由蘋果后臺保存,我們傳App上AppStore時,蘋果后臺用私鑰對App數據進行簽名,iOS系統下載這個APP后,用公鑰驗證這個簽名,若簽名正確,這個APP肯定是由蘋果后臺認證的,并且沒有被修改過,也就是達到了蘋果的需求:保證每個安裝的APP都是經過蘋果官方允許的。
但實際上因為除了從AppStore下載,我們還可以有三種方式安裝一個App:

  1. 開發App時可以直接把開發中的應用安裝進手機進行調試。
  2. in-house企業內部分發,可以直接安裝企業證書簽名后的APP。
  3. ad-hoc相當于企業分發的限制版,限制安裝設備數量。

蘋果要對這三種方式安裝的APP進行控制,就有了新的需求,無法像上面那樣簡單了。

新的需求

我們先來看第一個,開發時安裝APP,他有兩個需求:

  1. 安裝包不需要傳到服務器,可以直接安裝在手機上。如果你編譯一個APP到手機前要先傳到蘋果服務器簽名,這顯然是不能接受的。
  2. 蘋果必須對這里的安裝有控制權,包括:
    a. 經過蘋果允許才可以這樣安裝。
    b. 不能被濫用導致非開發APP也能被安裝。

為了實現這些需求,iOS簽名的復雜度也就增加了。
蘋果這里給出的方案是使用了雙層簽名,會比較繞,流程大概是這樣的:

  1. 在你的Mac開發機器生成一對公私鑰,這里稱為公鑰L,私鑰L。L:local
  2. 蘋果自己有固定的一對公私鑰,跟上面AppStore例子一樣,私鑰在蘋果后臺,公鑰在每個iOS設備上。這里稱為公鑰A,私鑰A。A:Apple
  3. 把公鑰L傳到蘋果后臺,用蘋果后臺里的私鑰A去簽名公鑰L,得到一份數據包含了公鑰L以及公鑰L的簽名,把這份數據稱為證書。
  4. 在開發時,編譯一個APP后,用本地的私鑰L對這個APP進行簽名,同時把第三步得到的證書打包到APP里,安裝在手機上。
  5. 在安裝時,iOS系統取得證書,通過系統內置的公鑰A,去驗證證書的數字簽名是否正確。
  6. 驗證證書后確保了公鑰L是蘋果開發認證過的,在用公鑰L去驗證APP簽名,這里就間接驗證了這個APP安裝行為是否是經過蘋果官方允許的。(這里是驗證安裝行為,不驗證APP是否被改動,因為開發階段APP內容總是不斷變化的,蘋果不需要管)

加點東西

上述流程只解決了上面第一個需求,也就是需要經過蘋果允許才可以安裝,還未解決第二個避免濫用的問題。怎么解決呢?蘋果再加上兩個限制,一是限制在蘋果后臺注冊過的設備才可以安裝,二是限制簽名只能針對某一個具體的APP。
怎么加呢?在上述第三步,蘋果用私鑰A簽名我們本地公鑰L時,實際上除了簽名公鑰L,還可以加上無限多的數據,這些數據都可以保證是經過批經過官方認證的,不會有被篡改的可能。



可以想到把允許安裝的設備ID列表和APP對應的AppID等數據,都在第3步這里跟公鑰L一起組成證書,再用蘋果私鑰A對這個證書簽名。在最后第5步驗證時就可以拿到設備ID列表,判斷當前設備是否符合要求。根據數字簽名的原理,只要數字簽名通過驗證,第5步這里設備IDs/AppID/公鑰L就都是經過蘋果認證的,沒有被修改,蘋果就可以限制可安裝的設備和APP,避免濫用。
最終流程
到這里這個證書已經變得很復雜了,有很多額外的信息,實際上除了設備IDs/AppID,還有其他信息也需要在這里用蘋果簽名,像這個APP里iCloud/push/后臺運行等權限蘋果都想控制,蘋果把這些權限開關統一稱為Entitlements,它也需要通過簽名去授權。
實際上一個“證書”本來就有規定的格式規范,上面我們把各種額外的信息塞入證書里是不合適的,于是蘋果另外搞了個東西,叫Provisioning Profile,一個Provisioning Profile里就包含了證書以及上述提到的所有額外信息,以及所有信息的簽名。
整個流程稍微變一下,就變成這樣了:


因為步驟有小變動,這里我們不辭啰嗦重新列一遍整個流程:

  1. 在你的Mac上生成一對公私鑰,這里稱為公鑰L,私鑰L。L:Local
  2. 蘋果自己有固定的一對公私鑰,跟上面AppStore例子一樣,私鑰在蘋果后臺,公鑰在每個iOS設備上。這里稱為公鑰A,私鑰A。A:Apple
  3. 把公鑰L傳到蘋果后臺,用蘋果后臺里的私鑰A去簽名公鑰L。得到一份數據包含了公鑰L以及公鑰L的簽名,這份數據稱為證書。
  4. 在蘋果后臺申請AppID,配置好設備列表和APP可使用的權限,再加上第3步的證書,組成的數據用私鑰A去簽名,把數據和簽名一起組成一個Provisioning Profile文件,下載到本地Mac開發機器上。
  5. 在開發時,編譯完一個APP后,用本地的私鑰對這個APP進行簽名,同時把第4步得到的Provisioning Profile文件打包進APP里,文件名為embedded.mobileprovison,把APP安裝到手機上。
  6. 在安裝時,iOS系統取得證書,通過系統內置的公鑰A,去驗證embedded.mobileprovison是否正確,里面的證書簽名也會再驗證一遍。
  7. 確保了embedded.mobileprovison里的數據都是通過蘋果授權以后,就可以取出里面的數據,做各種驗證,包括公鑰L驗證APP簽名,驗證設備ID是否在IDs列表中,AppID是否對應得上,權限開關是否跟App里的Entitlements對應等。
    開發者證書從簽名到認證最終蘋果采用的流程大致是這樣,還有一些細節像證書有效期/證書類型等就不細說了。
    概念和操作
    上面的步驟對應到我們平常具體的操作和概念大致是這樣的:
  8. 第1步對應的是keychain里的“從證書頒發機構請求證書”,這里就本地生成了一對公私鑰,保存的CertificateSigningRequest就是公鑰,私鑰保存在本地電腦中。
  9. 第2步蘋果處理,不用管。
  10. 第3步對應把CertificateSigningRequest傳到蘋果后臺生成證書,并下載到本地。這是本地有兩個證書,一個是第1步生成的,一個是這里下載回來的,keychain會把兩個證書關聯起來,因為他們公私鑰是對應的,在Xcode選擇下載回來的證書時,實際上會找到keychain里對應的私鑰去簽名。這里私鑰只有生成它的這臺Mac有,如果別的Mac也要編譯簽名這個APP怎么辦?答案是把私鑰導出給其他Mac用,在keychain里導出私鑰,就會存為.p12文件,其他Mac打開后就導入了這個私鑰。
    4、第4步都是在蘋果網站上操作,配置AppID/權限/設備等,最后下載Provisioning Profile 文件。
    5、第5步Xcode會通過第3步下載回來的證書(存著公鑰),在本地找到對應的私鑰(第1步生成的)去簽名APP,并把Provisioning Profile文件命名為embedded.mobileprovision一起打包進去。這里對APP的簽名數據保存分兩部分,Mach-O 可執行文件會把簽名直接寫入這個文件里,其他資源文件則會保存在 _CodeSignature 目錄下。
    第6-7步的打包和驗證都是xcode和iOS系統自動做的事。
    這里再總結一下這些概念:
  11. 證書:內容是公鑰和私鑰,由其他機構對其簽名組成的數據包。
  12. Entitlements:包含了APP權限開關列表。
  13. CertificateSigningRequest:本地公鑰。
  14. p12:本地私鑰,可以導入到其他電腦。
  15. Provisioning Profile:包含了證書/Entitlements等數據,并有蘋果后臺私鑰簽名的數據包。
    其他發布方式
    前面以開發包為例說了簽名和驗證的流程,另外兩種方式In-House企業簽名和AD-Hoc流程也是差不多的,只是企業簽名不限制安裝的設備數,另外需要用戶在iOS系統設置上手動點擊信任這個企業才能通過驗證。
    而AppStore的簽名驗證方式有些不一樣,前面我們說到最簡單的簽名方式,蘋果在后臺直接用私鑰簽名APP就可以了,實際上蘋果確實是這樣做的,如果去下載一個AppStore 的安裝包,會發現它里面是沒有embedded.mobileprovision 文件的,也就是它安裝和啟動的流程是不依賴這個文件,驗證流程也就更上述幾種類型不一樣了。
    據猜測,因為上傳到AppStore的包蘋果會重新對內容加密,原來的本地私鑰簽名就沒有用了,需要重新簽名,從AppStore下載的包蘋果也并不打算控制它的有效期,不需要內置一個embedded.mobileprovision 去做校驗,直接在蘋果用后臺的私鑰重新簽名,iOS 安裝時用本地公鑰驗證 App 簽名就可以了。那為什么發布AppStore的包還是要跟開發版一樣搞各種證書和Provisioning Profile?猜測因為蘋果想做統一管理,Provisioning Profile 里包含一些權限控制,AppID 的檢驗等,蘋果不想在上傳 AppStore 包時重新用另一種協議做一遍這些驗證,就不如統一把這部分放在 Provisioning Profile 里,上傳 AppStore 時只要用同樣的流程驗證這個 Provisioning Profile 是否合法就可以了。
    所以 App 上傳到 AppStore 后,就跟你的證書 / Provisioning Profile 都沒有關系了,無論他們是否過期或被廢除,都不會影響 AppStore 上的安裝包。
    到這里 iOS 簽名機制的原理和主流程大致說完了,希望能對理解蘋果簽名和排查日常簽名問題有所幫助。
    P.S.一些疑問
    最后這里再提一下我關于簽名流程的一些的疑問。
    企業證書
    企業證書簽名限制比較少,在國內被廣泛使用于測試和盜版,fir.im/蒲公英等測試平臺都是通過企業證書分發,國內一些市場像pp助手,愛思助手,一部分安裝手段也是通過企業證書重簽名。通過企業證書簽名安裝的APP,啟動時都會驗證證書的有效期,并且不定期請求蘋果服務器看證書是否被吊銷,若已過期或吊銷,就會無法啟動APP。對于這種助手的盜版安裝手段,蘋果想打擊只能一個個吊銷企業證書,并沒有太好的辦法。
    這里我對疑問是,蘋果做了這么多簽名和驗證機制去限制在iOS安裝APP,為什么又要出這樣一個限制很少使用的方式讓盜版鉆空子呢?若真的是企業用途不適合上AppStore,也可以完全在AppStore開辟一個小的秘密板塊,還是通過AppStore去安裝,就不會有這個問題了。
    AppStore加密
    另一個問題是我們把App傳到AppStore后,蘋果會對App進行加密,導致App體積增大不少,這個加密實際上是沒什么用的,只是讓破解的人要多做一個步驟,運行APP去內存dump出可執行文件而已,無論怎樣加密,都可以用這種方式拿出加密前的可執行文件。所以為什么要做這樣的加密呢,沒有任何好處。
    本地私鑰
    我們前面說的簽名流程很繞很復雜,經常出現各種問題,像有Provisioning Profile文件但證書又不對,本地有公鑰證書沒對應的私鑰等情況,不理解原理的情況下會被繞暈,我的疑問是,這里為什么不能簡化呢?還是以開發證書為例,為什么一定要用到本地Mac生成的私鑰去簽名?蘋果要的只是本地簽名,私鑰不一定是要本地生成的,蘋果也可以自己生成一對公私鑰給我們,放在 Provisioning Profile 里,我們用里面的私鑰去加密就行了,這樣就不會有 CertificateSigningRequest 和 p12 的概念,跟本地 keychain 沒有關系,不需要關心證書,只要有 Provisioning Profile 就能簽名,流程會減少,易用性會提高很多,同時蘋果想要的控制一點都不會少,也沒有什么安全問題,為什么不這樣設計呢?能想到的一個原因是 Provisioning Profile 在非 AppStore 安裝時會打包進安裝包,第三方拿到這個 Provisioning Profile 文件就能直接用起來給他自己的 App 簽名了。但這種問題也挺好解決,只需要打包時去掉文件里的私鑰就行了,所以仍不明白為什么這樣設計。

原文作者:blog/cnbang

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,011評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,263評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,543評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,323評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,874評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,095評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,605評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,551評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,720評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,961評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,358評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,612評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,330評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,690評論 2 370

推薦閱讀更多精彩內容