1. 程式人生 > >ios APP 簽名

ios APP 簽名

針對 什麽是 nts nat 每一個 title png 當前 tom

前言

相信很多同學對於iOS的真機調試,App的打包發布等過程中的各種證書、Provisioning ProfileCertificateSigningRequestp12的概念是模糊的,導致在實際操作過程中也很容易出錯。好在Xcode8.0出現了Automatically manage signing,讓我們在這步操作中減少了難度。雖然說我們在Xcode8.0之後可以選擇讓Xcode自動管理了,但是我們還是應該知道App簽名的原理。本文嘗試從原理出發,一步步推出為什麽會有這麽多概念,希望能有助於理解iOS App簽名的原理和流程。

簽名目的

先來看看蘋果采用簽名機制的目的。在iOS出來之前,在主流操作系統(Mac/Windows/Linux)上開發和運行軟件是不需要簽名的,軟件隨便從哪裏下載都能運行,導致平臺對第三方軟件難以控制,盜版流行。蘋果就希望在iOS平臺對第三方App有絕對的控制權,一定要保證每一個安裝到iOS上的App都必須經過蘋果官方認證的。那麽問題來了,怎麽保證呢?就是通過簽名這種機制。

非對稱加密

通常我們所說的簽名就是數字簽名,它是基於非對稱加密算法實現的。對稱加密是通過同一份密鑰加密和解密數據,而非對稱加密則有兩份密鑰,分別是公鑰和私鑰,用公鑰加密的數據,要用私鑰才能解密;用私鑰加密的數據,要用公鑰才能解密。

簡單說一下常用的非對稱加密算法RSA的數學原理,理解簡單的數學原理,就可以理解非對稱加密是怎麽做到的,為什麽是安全的:

1. 選兩個質數`p`和`q`,相乘得出一個大整數`n`,例如:p=61,q=53,n=p*q=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)這兩個數據就是私鑰,滿足用公鑰加密,私鑰解密,或者反過來私鑰加密,公鑰解密;也滿足在只暴露公鑰(只知道ne)的情況下,要推導出私鑰(n,d)需要把大整數n因數分解,目前因數分解只能靠暴力窮舉,而n數字越大,越難以用窮舉計算出因數pq,也就越安全,當n大到二進制1024位或2048位時,以目前技術要破解幾乎不可能,所以非常安全。

若對數字d是怎樣計算出來的感興趣,可以詳讀這兩篇文章:RSA 算法原理(一)(二)

數字簽名

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

  1. 首先用一種算法,算出原始數據的摘要,需要滿足:

    a. 若原始數據有任何變化,計算出來的摘要值也要有變化。

    b. 摘要要夠短,這裏常用的算法是MD5

  2. 生成一份非對稱加密的公鑰和私鑰,私鑰自己拿著,公鑰發布出去。
  3. 對一份數據,算出摘要之後,用私鑰加密這個摘要,得到一份加密後的數據,稱為原始數據的簽名。把它跟原始數據一起發送給用戶。
  4. 用戶收到數據和簽名後,用公鑰解密得到摘要,同時用戶用同樣的算法計算原始數據的摘要,比對這裏計算出來的摘要和公鑰解密簽名得到的摘要是否相等,若相等則表示這份數據中途沒有被篡改過,因為如果有篡改,摘要會變化。

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

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

最簡單的簽名

要實現這個需求很簡單,最直接的方式,蘋果官方生成一對公私鑰,在iOS裏內置一個公鑰,私鑰由蘋果後臺保存,我們傳App上AppStore時,蘋果後臺用私鑰對App數據進行簽名,iOS系統下載這個App後,用公鑰驗證這個簽名,若簽名正確,這個App肯定由蘋果後臺認證的,並且沒有被修改過,也就達到了蘋果的需求:保證安裝的每一個App都是經過蘋果認證允許的。
技術分享圖片

如果我們iOS安裝App只有從AppStore下載一種方式的話,這樣就可以搞定了,沒有任何復雜的東西,只有一個數字簽名,非常簡單的解決問題。
但是實際上,因為安裝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以及其簽名,把這份數據稱為證書。
  4. 在開發時,編譯完一個App後,用本地的私鑰L對這個App進行簽名,同時把第三步得到的證書一起打包進App裏,安裝到手機。
  5. 在安裝時,iOS系統取得證書,通過系統內置的公鑰A,去驗證證書的數字簽名是否正確。
  6. 驗證證書確保公鑰L是蘋果認證過的,再用公鑰L去驗證App的簽名,這裏就間接驗證了這個App的安裝行為是否經過蘋果官方允許。(這裏只驗證安裝行為,不驗證App是否被改動,因為開發階段App內容總是不斷變化的,蘋果不需要管)。

加點東西

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

可以想到把允許安裝的設備ID列表和App對應的AppID等數據,都在第三步這裏跟公鑰L一起組成證書,再用蘋果私鑰A對這個證書簽名。在最後第5步驗證時就可以拿到設備ID列表,判斷當前設備是否符合要求。根據數字簽名的原理,只要數字簽名通過驗證,第5步這裏的設備IDs/AppID/公鑰L就都是經過蘋果認證的,無法被修改,蘋果就可以限制可安裝的設備和APP,避免濫用。

最終流程

到這裏這個證書已經變得很復雜了,有很多額外信息,實際上除了設備ID/AppID,還有其他信息也需要在這裏用蘋果簽名,像App裏iCloudpush、後臺運行 等權限蘋果都想控制,蘋果把這些權限開關統稱為Entitlements,它也需要通過簽名去授權。
實際上一個證書本來就有規定的格式規範,上面我們把各種額外的信息塞入證書裏是不合適的,於是蘋果另外搞了一個東西,叫Provisioning Profile,一個Provisioning Profile裏就包含了證書以及上述提到的所有額外信息,以及所有信息的簽名。
所以,整個流程稍微變一下,就成這樣了:
技術分享圖片

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

  1. 在你的 Mac 開發機器生成一對公私鑰,這裏稱為公鑰L,私鑰L。L:Local
  2. 蘋果自己有固定的一對公私鑰,跟上面 AppStore 例子一樣,私鑰在蘋果後臺,公鑰在每個iOS設備上。這裏稱為公鑰A,私鑰A。A:Apple
  3. 把公鑰L傳到蘋果後臺,用蘋果後臺裏的私鑰A去簽名公鑰L。得到一份數據包含了公鑰L以及其簽名,把這份數據稱為證書。
  4. 在蘋果後臺申請AppID,配置好設備ID列表和APP可使用的權限,再加上第③步的證書,組成的數據用私鑰A簽名,把數據和簽名一起組成一個Provisioning Profile文件,下載到本地Mac開發機。
  5. 在開發時,編譯完一個APP後,用本地的私鑰L對這個APP進行簽名,同時把第④步得到的Provisioning Profile文件打包進APP裏,文件名為 embedded.mobileprovision,把APP安裝到手機上。
  6. 在安裝時,iOS系統取得證書,通過系統內置的公鑰A,去驗證 embedded.mobileprovision的數字簽名是否正確,裏面的證書簽名也會再驗一遍。
  7. 確保了embedded.mobileprovision裏的數據都是蘋果授權以後,就可以取出裏面的數據,做各種驗證,包括用公鑰L驗證APP簽名,驗證設備ID是否在ID列表上,AppID是否對應得上,權限開關是否跟APP裏的Entitlements對應等。

開發者證書從簽名到認證最終蘋果采用的流程大致是這樣,還有一些細節像證書有效期/證書類型等就不細說了。

概念和實際操作

上面的步驟對應到我們平常具體的操作和概念是這樣的:

  1. 第1步對應的是keychain裏的“從證書頒發機構請求證書”,這裏就本地生成了一對公私鑰,保存的CertificateSigningRequest就是公鑰,私鑰保存在本地電腦裏。
  2. 第2步蘋果自己處理,我們不用管。
  3. 第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. 第6、7步的打包和驗證都是 Xcode 和 iOS 系統自動做的事。

這裏再總結一下這些概念:
證書:內容是公鑰或私鑰,由其他機構對其簽名組成的數據包。
Entitlements:包含了App權限開關列表。
CertificateSigningRequest:本地公鑰。
.p12:本地私鑰,可以導入到其他電腦。
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 簽名機制的原理和主流程大致說完了,希望能對理解蘋果簽名和排查日常簽名問題有所幫助。

致謝

感謝各位能改耐心看完,也希望能夠對大家帶來幫助。同時也感謝原作者的文章。本篇文章主要是為了做筆記。

最後,打一波廣告,需要簽名的朋友可以加QQ:2302398895 一朋友專業做ios APP簽名。

ios APP 簽名