Bo2SS

Bo2SS

iOS | 圖解iOS簽名背後的原理

image

上週我給組裡做了一次 “學習匯報”,其實也是組裡每週都有的技術分享,每個人都有機會,這次輪到我了。那作為團隊菜鳥,我該講點什麼呢?

我思前想後,突然想到自己之前老是遇到的一個棘手的問題:在真機上運行 iOS 工程時,工程還沒跑起來,工程配置的簽名(Targets > Signing & Capabilities)那裡就先報錯了,不管是自己的工程,第三方開源庫,還是公司的項目。

雖然自己每次面向谷歌或者面向同事都可以找到答案,但為什麼能解決以及為什麼真機運行 iOS 項目需要簽名,自己只能說是對它們一知半解、被它們弄得雲里霧里。

不過現在我總算明白了它們背後的奧秘!如果你也有同樣的困惑,往下看,保證這次讓你徹底弄懂它 ——iOS 簽名背後的原理

本文目標#

閱讀完本文會讓你擁有輕鬆解決以下問題的能力:

  1. 如何在真機上跑自己的 iOS 項目,或者 iOS 開源庫?
  2. 如何在真機上跑公司的 iOS 項目?

另外,本文的終極目標是:遇到任何 iOS 簽名相關問題時,你都能夠快速解決。

預備知識:數字簽名 & 數字證書#

在聊 iOS 簽名之前,我們先需要了解兩個預備知識,那就是在互聯網世界裡的簽名✒️和證書📄。

可參考:數字簽名和數字證書是什麼?—— 阮一峰

數字簽名#

數字簽名一般夾帶在要傳輸的數據中,用來防止數據被篡改。

它的底層核心是哈希混淆算法非對稱加密技術(公 / 私鑰)。

生成#

簽名 Signature 的生成由通信中的發送方 Sender 進行,首先對要傳輸的數據 Data 進行哈希 Hash 混淆得到數據摘要 Digest,然後用私鑰 Private Key 對摘要進行加密,這樣就生成了數據的簽名。

image

驗證#

接收方 Receiver 接收來自發送方的數據和簽名,對它們分別做如下處理:

  • 數據:使用與發送方相同的哈希算法對數據進行混淆,得到數據摘要 A;
  • 簽名:利用發送方加密所用私鑰對應的公鑰 Public Key,對簽名進行解密,得到數據摘要 B。

比對摘要 A 和摘要 B,如果相等,則說明數據沒有被篡改,否則數據存在問題。

image

簽名的生成和驗證過程合在一起如下圖所示,自己再分析一遍可加深理解。

image

帶著問題往下走

Q1: 猜想一下 iOS 簽名和驗簽的過程,App 開發者和 App 使用者中,誰是發送方,誰是接收方,傳輸的數據又是什麼呢?

A1: App 開發者是發送方,App 使用者是接收方,傳輸的數據就是 App 的安裝包。

Q2: 接收方如何拿到解密所用的公鑰呢?

請接著往下看。

數字證書#

在了解數字證書之前,我們可能馬上想到的是,發送方在發送數據和簽名的同時,再附帶上公鑰不就萬事俱備了~如下圖所示:

image

但是這樣會引入一個新的問題

Q1: 接收方如何確認公鑰沒有被其他人惡意替換呢?也就是公鑰的身份不明。

這時候數字證書就該登場了。

組成內容#

我們可以先看一下數字證書的組成內容,它由公鑰、公鑰的身份信息 Identity Info 以及它們的簽名 B 組成。

image

注意:加密公鑰數據所用的私鑰又是另外一個公私鑰組合了,它是由權威的認證機構(Certificate Authority,CA)頒發的。

公鑰包裝在數字證書中傳遞#

這下,原來發送方發送的內容由數據 + 簽名 +公鑰,變成了數據 + 簽名 +證書

image

證書裡包含了接收方解密簽名 A 所需的公鑰,除此之外,證書裡還有公鑰的身份信息和簽名 B:

  1. 身份信息的存在則可以消除公鑰身份不明的隱患;
  2. 簽名 B 則對公鑰及其身份信息未被篡改做了保證

但也因為有了簽名 B,我們在取公鑰時,還需要先對證書裡的簽名 B 進行驗證:

image

注意:解密證書裡簽名 B 所用的公鑰也是由 CA 頒發的,它存在於 CA 證書裡。

現在,我猜你已經能夠記住證書的組成內容了:公鑰 + 公鑰的身份信息 + 它們的簽名。

帶著問題往下走

Q1: 接收方從哪裡獲得這個 CA 證書呢?接收方又要如何驗證這個 CA 證書的簽名呢?似乎進入了無限循環♻️。

請接著往下看。

證書信任鏈#

CA 證書一般是在安裝系統 / 軟件時內置的,這樣我們總該信任它了吧~

接下來,我們可以再了解一下證書的信任鏈。

根據證書在信任鏈中所處的位置,可以將證書分為三種

舉例:我的開發者證書 A(Apple Development)由中間證書 B(Apple Worldwide Developer Relations Certification Authority,安裝 Xcode 時內置)的 CA 簽發,中間證書 B 由根證書 C(Apple Root CA,系統內置)的 CA 簽發,而根證書 C 是由自己的 CA 簽發的,因為 C 已經在信任鏈的頂端啦,它自己說了算。

image

回到上個問題:如何保證這個 CA 證書可信呢?只要接收方有簽發方的證書,那麼用證書裡的公鑰驗證一下就可以了。比如一台 iPhone 安裝我開發的 App 時,收到了我的開發者證書 A,那麼手機就會去找簽發方的證書 B(Apple Worldwide Developer Relations Certification Authority,iOS 系統內置)來驗證 A 是否可信。

現在,你也可以看看自己的 Mac > 鑰匙串 Keychain 軟件 > 證書 Certificates,加深理解。

繼續帶著問題往下走

Q1: 加密公鑰及其身份信息所用的 CA 私鑰在我們的電腦本地嗎?如果不是,該如何生成我們的證書呢?

iOS 證書申請原理 & 申請方法#

當然不是,這些私鑰可是 CA 簽發證書的秘密寶貝。比如,我們要想申請 iOS 開發者證書,則需要找 Apple 官方幫忙,Apple 官方會使用上面提到的中間證書 CA 的私鑰來簽發證書。

申請原理#

想要申請自己的 iOS 開發者證書,分為以下幾步:

  1. 用自己的電腦生成公私鑰對,並填寫公私鑰對的身份信息;
  2. 將公鑰及其身份信息發送給 Apple CA;
  3. CA 使用哈希算法和 CA 私鑰對數據(公鑰及其身份信息)進行簽名,數據和簽名則組成了我們想要的證書;
  4. 我們再在 CA 上把證書下載到電腦上,安裝證書後電腦會自動關聯對應的私鑰。

image

申請方式:2 種#

具體的申請方式有 2 種:1)上傳 CSR (CSR, Certificate Signing Request) 文件方式,2)Xcode 自動申請方式,這裡推薦第二種。

1)上傳 CSR 文件方式#

該方法適合想了解 iOS 證書申請原理的你。注意:該方式需要加入蘋果開發者計劃,$99 / 年。

a) 打開 Keychain > Certificate Assistant > Request a Certificate From a Certificate Authority...:

image

b) 輸入身份信息(郵箱、證書名字),可以選擇保存到本地,本地就得到了個 CSR (.certSigningRequest) 文件,裡面包含了公鑰及其身份信息。

image

你可能會問,那私鑰放在哪裡呢?如果你細心的話,你會發現 Keychain > login > Keys 裡面已經多了一對你命名的公私鑰對。

c) 登錄Apple Developer網站,進入 Certificates, Identifiers & Profiles 板塊,上傳剛剛生成的 CSR 文件,即可生成證書(.cer 文件),此時將證書下載到本地,雙擊即可導入 Keychain,在 Keychain > login > May Certificates 中可以看到該證書。

image

如果你細心的話,你會發現 Keychain 裡該證書已經和一把私鑰綁定起來了,如果想把證書共享給其它開發者使用,則需要右鍵導出我們熟悉的.p12 文件,它包含了證書和對應的私鑰~

2)Xcode 自動申請方式(推薦)#

這種方式則不需要繁瑣的上傳 CSR 文件、下載.cer 證書過程,也不會強求你加入 Apple 開發者計劃。

a) 在 Xcode 裡登錄 Apple 賬號:Xcode > Preference > Account > Apple IDs > 「+」。

image

b) 登錄片刻後,你就會發現 KeyChain 裡自動多出一份相應的證書了。

image

注意:如果你加入了開發者計劃,Apple 開發者網站上也會自動添加該證書。

iOS 簽名 & 打包原理#

終於來到 iOS 簽名部分了,前面的內容你理解的怎麼樣呢?如果你還有點迷糊,那麼記住簽名的作用是防止傳輸的數據被篡改這一點,你就基本掌握本文的真諦了!

其實,在真正 iOS 簽名的時候,還不是只附加證書(含公鑰)這一個東西,我們還需要對證書做一層包裝,那就是我們熟悉的 Provisioning Profile 文件,又叫 PP 文件、描述文件、供應配置文件。

PP 文件#

先來了解一下這個重要的 PP 文件,我們可以把它理解為證書的升級版。

如何生成#

  1. 開發者平台上申請;
  2. Xcode 自動生成:Xcode > Targets > Signing & Capabilities > 勾選 Automatically manage signing。

文件結構#

image
  • App ID:
    • 在 Apple 開發者平台上註冊;或者根據 Xcode > Targets > Signing & Capabilities 填寫的 Bundle ID 自動生成。
    • 我們在 Xcode > Targets > Signing & Capabilities 中填寫的 Bunlde Identifier 必須與 App ID 是一致或匹配的。
  • Entitlements:
    • 允許使用的權限列表,實際在 App 中使用的權限必須是這個列表的子集,即我們在 Xcode > Targets > Signing & Capabilities 中添加的 Capabilities,不能超出其範圍。
    • 工程目錄下也會有一份.entitlements 文件(授權文件),它是根據 Xcode > Targets > Signing & Capabilities 裡添加的 Capabilities自動生成的。如果 App 中使用到了某項沙盒限制的功能,但是該.entitlements 文件沒有聲明對應的權限,當 App 運行到相關代碼時,App 會直接 Crash。
  • Certificates:由 iOS 開發者證書組成,可以不只一個,它們就是上面申請到的 iOS 證書。
  • Devices:由 iOS 設備的 UDID (Unique Device Identifier) 組成的列表,限定了可以開發調試該 App 的 iOS 設備。
  • Signature:在生成 PP 文件時,由 Apple CA 對其進行簽名,防止被人篡改。

示例#

PP 文件默認保存在:~/Library/MobileDevice/Provisioning\ Profiles

下面是 Xcode 自動生成的一個 PP 文件預覽:

image

簽名 & 打包#

iOS 簽名和打包過程其實是由 Xcode 來把控的,看下圖:

image

  1. 首先,Xcode 會檢查 App 裡的 bundle ID 是否匹配 PP 文件中的 App ID,以及 App 裡的 Entitlements (.entitlements) 文件聲明的權限是否在 PP 文件中 Entitlements 允許權限的範圍內,二者任一條件為否,則檢查不通過;
  2. 其次,Xcode 會在電腦的 Keychain 裡找,是否有匹配 PP 文件中 Certificates 的證書,如果匹配到了,才繼續下一步;
  3. 然後,Xcode 會檢查匹配到的證書是否綁定了對應的私鑰,如果沒有,就無法進行下面的關鍵步驟 —— 簽名;
  4. 現在,開始利用哈希算法和私鑰對 App 進行簽名了;
  5. 最終,對 App、PP 文件和簽名進行打包,即生成.ipa 包。

⚠️:上面忽略了簽名 C 和簽名 A 的驗證過程,在第 1 步使用 PP 文件之前和在第 2 步匹配到證書之後,都應先使用 CA 公鑰對其附帶的簽名 C 和 A 進行驗證,防止它們被篡改。如有錯誤,歡迎指正。

延伸問題

Q1: Xcode 會對 App 的哪些內容進行簽名呢?

A1: 全部內容。但簽名過程較為複雜,這裡就不展開了,簡而言之,為了權衡簽名的安全性和效率,App 的簽名被分為 4 次哈希和 1 次加密,每次哈希都是環環相扣的,保證了 App 內容沒有被篡改。

image

具體可參考細說 iOS 代碼簽名 (三):簽名的過程及代碼簽名的數據結構

iOS 驗簽原理#

說完了 iOS 簽名,那真機在安裝 App 時如何驗證它的簽名呢?

首先,我們需要知道的是,App 安裝包分為測試包正式包,兩者的驗簽過程有所不同。

順便補充一下測試包和正式包的知識:

1)測試包:分為內測包、準備上傳 App Store 的發布包、Ad Hoc 發布包、In-house 企業內部發布包 4 種類型。

2)正式包:上傳到 App Store 上的安裝包。

⚠️:Xcode 打的包都屬於測試包。

參考iOS 不同類型測試包介紹—— 搜狗測試公眾號。

測試包#

安裝測試包時,真機對其進行了完整的驗簽過程,如下圖所示:

image

  1. 首先,真機設備會使用系統內置的 CA 公鑰驗證PP 文件及其簽名 C 的合法性;
  2. 其次,真機再使用系統內置的 CA 公鑰驗證 PP 文件中證書及其簽名 A 的合法性;(App 裡保存了生成簽名 B 所用的證書信息,這樣真機才知道去取出哪個證書裡的公鑰)
  3. 然後,真機再取出證書中的公鑰驗證App及其簽名 B 的合法性;
  4. 最後,真機會檢查自己的 UDID 是否在 PP 文件的 Devices 列表中,如果存在,才開始安裝 App。

⚠️:圖裡簡化了驗簽的過程,相信你已經很熟悉這個過程了,哈希混淆、公鑰解密、判等...

其實,簽名的驗證並非一次性完成,在安裝、啟動和運行時有著不同的校驗規則,上面簡化了該過程,具體可參考細說 iOS 代碼簽名 (四):簽名校驗、越獄、重簽名

正式包#

而安裝正式包時,真機對其的驗簽過程簡化了很多,因為對開發者證書的驗簽過程交給了 App Store

1)將發布包上傳到 App Store。

image

  1. 當你將發布包(測試包的一種)上傳到 App Store 時,Apple 官方同樣會對發布包進行驗簽,其過程類似上述測試包的驗簽過程;
  2. 驗簽通過後,App Store 會重新對 App 進行簽名,這裡用的就不是開發者證書對應的私鑰了,而是 CA 私鑰;
  3. 最後,App Store 只會對 App 和新的簽名進行打包,生成.ipa 包。(⚠️:不包含 PP 文件了)

2)真機設備安裝正式包。

image

當設備從 App Store 上下載 App 後,只需要用系統內置的 CA 公鑰對 App 進行驗簽,驗證通過即可安裝。

練習一下#

網上也有一些畫得很好的圖,你們可以按照序號回顧一遍過程,用來鞏固本文的內容。

image

來自iOS 證書那些事兒—— 掘金。

image

來自iOS App 簽名的原理——Blog。

回到:本文目標#

最後,我們再回到文章開頭目標裡提出的問題,解決那些問題之前永遠銘記兩個東西:包含私鑰的證書(.p12 = .cer + private key)、PP 文件(.mobileprovision)。

再看看我們的問題,只要找到上面兩個東西就可以了:

  1. 如何在真機上跑自己的 iOS 項目,或者 iOS 開源庫?
    1. 包含私鑰的證書:參考 iOS 證書申請方式那一節;
    2. PP 文件:在開發者平台上申請;或者由 Xcode 自動生成,Targets > Signing & Capabilities > 勾選 Automatically manage signing。
  2. 如何在真機上跑公司的 iOS 項目?
    1. 包含私鑰的證書:向團隊索要,注意文件後綴是.p12;
    2. PP 文件:向團隊索要,記得讓負責人在開發者平台上添加自己真機的 UDID 到 PP 文件中。

PS:

  • 運行測試包安裝的 App 時,一般還需要在真機上信任證書:Settings > General > VPN & Device Management。
  • Xcode 自動生成 PP 文件:

image

參考資料#

整體把握:iOS 證書幕後原理——Blog

深度理解:細說 iOS 代碼簽名——Blog

其它你可能感興趣的:

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。