來自:https://juejin.im/entry/58ff10bf1b69e60058abb6b8
要理解 iOS 中的簽名和證書機制,首先我們要理解什么是簽名(Signature),什么是證書(Certificate)。
簽名
在數據傳輸的過程中,我們最關心的就是兩件事:可靠性和數據完整性。我們可不想打開身份不明的開發者的程序,或是打開已經損壞/被修改過的程序。
為校驗數據包的完整性,數據發送端通常會利用 [MD5 算法]來將需要發送的信息進行“摘要”,而后發送端利用私鑰將摘要加密,得到簽名,
連同數據包一起發送給接受方,接受方收到數據包和簽名后,接受方利用公鑰解密簽名,拿到發送方的 MD5 數據,再對數據包進行 MD5 操作并與發送方 MD5 進行對比,
以驗證數據完整性。上述的過程,摘要的加、解密步驟,驗證了數據發送方的身份,而兩次 MD5 操作驗證了數據完整性。
一般情況下僅對 md5 內容進行加密,是為了節省加密、解密的時間。
證書
上述簽名過程看起來已經足夠安全了,接受方通過**發送方公鑰**對簽名進行解密,就能夠驗證數據包發送者的身份。
但這都是建立在我們手中的公鑰是正確的前提下的,一旦我們的公鑰被替換,我們便無法解開對應發送方的簽名,但更危險的是,偽造公鑰的第三方,可以發送數據給我們,而我們拿著“假”的公鑰,能正確解開其簽名,從而誤以為該數據源于我們所信任的發送方。
那么如何確保公鑰的安全性呢?一個很自然的想法就是為公鑰進行加密。通常的做法是,我們找到一個可信任的數字證書認證機構(Certificate Authrotiy,簡稱 CA),將我們自己的**公鑰**交給 CA,隨后該機構運用上述簽名類似流程,用 CA 私鑰加密我們的公鑰,然后暴露一個 CA 公鑰供使用者驗解密。
這個被 CA 私鑰所加密的我們自己的密鑰(當然還有一些像有效期、用戶信息等其它內容)一般被稱為證書。但這里也引入了另一個問題:解密證書所需要的公鑰,同樣存在被冒充的風險,如何規避這其中的風險呢?這里我并沒有查到詳細的資料,我想,這里公鑰的正確性,便是由這些具有公信力的 CA 所保證的,而平時我們自己做業務,在網絡安全方面,是難以達到這樣的安全標準的。
小結
有了上面的儲備知識,我們知道,想要在網絡上安全的傳輸數據,我們一般需要兩對密鑰:自己的業務服務器公私鑰以及 CA 的公私鑰。接下來讓我們看看蘋果是如何執行這樣的思想的。
iOS 的簽名證書機制
接下來我們看看 iOS 上的簽名機制,下文只討論 AppStore 以及本地 build 這兩種情況(本文編寫時,Xcode 版本為 8.3 (8E162),若是更早的版本,macOS 上證書的生成與導入可能需要手動完成)。
為什么需要
相信大家入門程序員的時候,大多都是在 VC 或 VS 上打 C/C++ 入門的,那時候一切都很簡單,code-build-link-run,并沒有什么認證過程,而由于蘋果對其生態圈管理嚴格,任何在 iOS 上跑的程序都需要經過蘋果的“同意”。所以,不論是 AppStore,還是 Xcode 本地 build 的項目,蘋果都需要驗證這個應用的身份。
從 AppStore 下載的應用驗證流程非常簡單,Apple 用私鑰簽名 App 后,iOS 設備下載簽名和 App,利用 Apple 公鑰驗證簽名并安裝。由于 iOS 設備內置 Apple 公鑰,而數據包直接由蘋果服務器下載,因此此處的安全是有保障的,同時,簽名驗證能通過,證明該安裝包是從蘋果服務器下載的,也就滿足了蘋果對 App 安裝的控制。
Xcode Build
從 AppStore 分發應用的過程是非常簡單的,但在日常開發中,上述的驗證過程是不能被接受的,我們不可能每次 build 都將應用打包上傳,經過蘋果的加密后再下載運行。
于是蘋果想出了這么一套認證體系。從 AppStore 下載應用時,iOS 設備會用 Apple 公鑰驗證應用安裝包,以確保該應用來源于 Apple 服務器。而在本地調試模式下,設備會把對 App 的驗證,改為對開發者的驗證:
此處的開發者賬號,就是在 Apple Developer Center 中所申請的開發者賬號,一旦信任之后,**每次 build 的時候,iOS 設備就將驗證該開發者身份,而不是驗證你 build 的 App**。
如此一來,便省去了應用包上傳、下載的過程。我們知道,驗證 App 是通過簽名機制,來確定安裝包來源的,而此處的驗證開發者身份,又是怎樣的機制呢?
驗證開發者
首先,iOS 設備需要驗證你的開發者身份,這是基于 macOS 的一種證書策略,如果打開鑰匙串訪問,選中證書,我們可以看到這樣的一堆東西:
這里你可以看到你的企業證書、個人開發者證書等等,我這里還有 Charles 用來做網絡代理的證書。為了更好的說明這一工作機制,我用我的 gmail 郵箱重新申請了一個開發者賬號。
當你在 Xcode 添加自己的賬號后,并在項目中將 Team 選擇為自己的開發者賬號后,鑰匙串中就多出了這么一個東西:
我們可以看到,證書中存儲著一個“專用密鑰”,這個專用密鑰,和前面 AppStore 分發過程中的 Apple 密鑰作用類似,是用來給 App 安裝包進行簽名的。這份密鑰在生成的時候,Xcode 將密鑰的公鑰部分上傳至 Apple 服務器,由 Apple 進行簽名生成證書,最后返回到本地,而私鑰,則會在 Xcode 本地構建安裝時,用來為 App 安裝包簽名。
最后,Xcode 會將簽名后的安裝包和本地證書打包都放到 iOS 設備中,iOS 設備接著使用 Apple 公鑰來解密證書,拿到本地公鑰,接著用本地公鑰去解密 App 。
繞了這么一大圈,無非是為了驗證兩件事:
iOS 設備用 Apple 公鑰解密證書,所拿到的本地證書,必定是經過 Apple 服務器簽名的,也就是說,這里保證了開發者是經過蘋果認證的。
上一步拿到的公鑰,用于解密 App 簽名,若解密成功且驗證通過,證明該 App 確實是由該開發者構建的,也就保證了 App 的安全性。
或許看看流程圖,能更清楚:
上述的流程唯一問題是,這個密鑰是在本地生成的,如果更換了電腦,本地的密鑰對就會改變。這時候,你需要將舊 mac 上的證書導出,并安裝到新的電腦上。
一一對應
我們能在 mac 的鑰匙串訪問中找到本地的密鑰證書,但其它的東西似乎就不是那么地可見了,對于一般的開發者來說,我們更熟悉的概念應該是 Provisioning Profile,Xcode 8.3 提供了很友好的可視化界面來讓我們查看 Provisioning Profile 里面的東西。
我們可以看到,Provisioning Profile 中包含了開發組名、組內設備、應用 Capabilities 配置等等,這些都是決定應用行為的一些信息。我們還注意到,Provisioning Profile 中包含了一個 Certificates,這個證書就是我們在上面說的本地公鑰證書。
其實在 Xcode 構建安裝 App 時,我們不是直接將本地證書裝入 iOS 設備中,而是將 Provisioninng Profile 裝入設備中。如果我們開啟了 Automatically manage signing,Provisioning Profile 的生成和下載都是由 Xcode 自動完成的,但其實,Provisioning Profile 的創建過程,和本地密鑰證書的過程極其相似。
如果你是付費開發者,或是加入了企業開發 team,你就能在 [證書管理](https://link.juejin.im/?target=https%3A%2F%2Fdeveloper.apple.com%2Faccount%2Fios%2Fcertificate) 看到所有的 Provisioning Profile,每個 Provisioning Profile 與 teamId 和 bundleId 唯一對應。類似的,你也需要將它下載到本地,只不過這一步很多時候 Xcode 替我們完成了。
*Xcode 默認存儲 Provisioning Profile 的文件夾*
由于 Provisioning Profile 在 Apple 后臺生成,很自然的,它也會被 Apple 私鑰加密,而它的作用主要有兩點:
校驗 App 的各項參數(如 Capabilities,后臺獲取、通用鏈接等配置)是否與蘋果后臺定義的一致。
其中包含的 Certificates,也就是本地證書,用于校驗開發者身份。
所以,我們可以將上一部分的圖進行一些細化:
這里對身份驗證的原理和上一部分一致,只是多了一層封裝,用于校驗項目信息。
總結
至此,我們大致了解了 AppStore 以及 Xcode 本地構建時,iOS 所使用的身份驗證機制了。整套驗證流程其實就是對非對稱加密的封裝和應用,只是蘋果為驗證項目信息,又不想與“證書”這一專有名詞混淆,加入了一個 Provisioning Profile 的概念。如果你熟悉簽名、證書的概念,那么其實理解這一套驗證機制也就并不困難了。
參考資料
iOS App 簽名的原理
數字簽名是什么?
Public-key cryptography for non-geeks