"用戶會(huì)感激代碼簽名帶來的好處" – Apple Developer Library: Code Signing Guide
在 iOS 或 OS X 平臺(tái)上進(jìn)行應(yīng)用開發(fā)時(shí),你所需要使用的 API 大多設(shè)計(jì)得簡(jiǎn)潔明了。你可以輕易地實(shí)現(xiàn)酷炫的動(dòng)畫效果,便捷地進(jìn)行應(yīng)用發(fā)布前測(cè)試,或是用 Core Data 將數(shù)據(jù)安全的存儲(chǔ)在本地。但是總有一天,你會(huì)碰上代碼簽名 (code signing) 和配置文件 (provisioning),大多數(shù)情況下,這會(huì)是你在心里問候某些人祖宗的開始。
如果你已經(jīng)在 iOS 上開發(fā)過應(yīng)用,那么你多半已經(jīng)與代碼簽名或設(shè)備配置文件打過交道了。即使是 OS X 開發(fā)者,如果你想發(fā)布自己的應(yīng)用到 Mac App Store 上去或者想?yún)⑴c蘋果的開發(fā)者項(xiàng)目,那么也不得不開始為自己的代碼進(jìn)行簽名。
大多數(shù)時(shí)候代碼簽名看上去像是一個(gè)難以理解的神秘黑盒。在這篇文章里我會(huì)盡可能揭示盒子內(nèi)部的運(yùn)作機(jī)理。
通常來說,我們無法直接看到代碼簽名的運(yùn)作過程,它們隱藏在 iOS 系統(tǒng)內(nèi)部和 SDK 之中。但我們可以通過觀察設(shè)置代碼簽名所需工具的運(yùn)作方式,來找出一些線索。除此之外,我們還可以參考 OS X 上的代碼簽名運(yùn)作方式,畢竟 iOS 和 OS X 系出同源,我們可以從他們的對(duì)比之中得到很多有用的信息。
OS X 上代碼簽名技術(shù)和相應(yīng)的 API 是在 Mac OS X Leopard 10.5 上首次出現(xiàn)的,這剛好是第一臺(tái) iPhone 發(fā)布的時(shí)候。這并非巧合,因?yàn)樵?iOS 上,代碼簽名起到的作用更加重要。iPhone 是在眾多游戲主機(jī)之后第一個(gè)大規(guī)模出售并且從頭就開始使用代碼簽名的計(jì)算平臺(tái)。只有在越獄之后,iOS 才能運(yùn)行沒有簽名的代碼。越獄使應(yīng)用可以繞過代碼簽名和沙盒安全機(jī)制的全部限制,這會(huì)是一個(gè)非常危險(xiǎn)的行為。
證書和密匙
作為一個(gè) iOS 開發(fā)者,在你開發(fā)使用的機(jī)器上應(yīng)該已經(jīng)有一個(gè)證書,一個(gè)公鑰,以及一個(gè)私鑰。這些是代碼簽名機(jī)制的核心。像 SSL 一樣,代碼簽名也依賴于采用 X.509 標(biāo)準(zhǔn)的公開密鑰加密。
在 OS X 上,X.509 的基本組成部分(譯者注:例如證書等)都是由一個(gè)叫鑰匙串訪問的工具來進(jìn)行管理。打開你開發(fā)機(jī)器上的鑰匙串訪問應(yīng)用,選擇類別選項(xiàng)下的“我的證書(My Certificates)”,你可以看到所有你持有的私鑰相對(duì)應(yīng)的證書。要用一個(gè)證書設(shè)置代碼簽名,你必須擁有私鑰,所以所有你擁有私鑰的證書都會(huì)被列在這里。如果你擁有一個(gè)證書的私鑰,你可以展開證書并將它的私鑰顯示出來:

如果你要導(dǎo)出證書,例如為了備份(強(qiáng)烈建議進(jìn)行),一定要記得展開證書那一條顯示出私鑰并將兩行都選中。
還有一種可以用來快速地顯示出你的系統(tǒng)中能用來對(duì)代碼進(jìn)行簽名的認(rèn)證的方法,那就是利用用途廣泛的命令行工具 security:
概括的講,一個(gè)證書是一個(gè)公鑰加上許多附加信息,這些附加信息都是被某個(gè)認(rèn)證機(jī)構(gòu)(Certificate Authority 簡(jiǎn)稱 CA)進(jìn)行簽名認(rèn)證過的,認(rèn)證這個(gè)證書中的信息是準(zhǔn)確無誤的。對(duì)于 iOS 開發(fā)來說這個(gè)認(rèn)證機(jī)構(gòu)就是蘋果的認(rèn)證部門 Apple Worldwide Developer Relations CA。認(rèn)證的簽名有固定的有效期,這就意味著當(dāng)前系統(tǒng)時(shí)間需要被正確設(shè)置,因?yàn)樽C書是基于當(dāng)前時(shí)間進(jìn)行核對(duì)。這也是為什么將系統(tǒng)時(shí)間設(shè)定到過去會(huì)對(duì) iOS 造成多方面破壞的原因之一。

對(duì)于 iOS 開發(fā)來說,一般會(huì)有兩個(gè)證書:一個(gè)帶有前綴 iPhone Developer,另一個(gè)帶有前綴 iPhone Distribution。前者用于使應(yīng)用可以在你的測(cè)試設(shè)備上運(yùn)行,后者是在提交應(yīng)用到 APP store 時(shí)用到。一個(gè)證書的用途取決于它所包含的內(nèi)部信息,在鑰匙串訪問中雙擊打開一個(gè)證書文件,你可以看到許多詳細(xì)條目,拖動(dòng)到最下面有一條標(biāo)記著 Apple Developer Certificate (Submission), 或者 Apple Developer Certificate (Development),具體你會(huì)看到哪一種,取決于你所打開的證書是哪一種類型,iOS 系統(tǒng)會(huì)利用這個(gè)信息來判斷你的應(yīng)用是運(yùn)行在開發(fā)模式下還是發(fā)布模式,并據(jù)此判斷以切換應(yīng)用運(yùn)行規(guī)則。
為了讓擁有公鑰的證書起作用,我們需要有私鑰。私鑰是你在為組成應(yīng)用的二進(jìn)制文件進(jìn)行簽名時(shí)派上用場(chǎng)的。沒有私鑰,你就無法用證書和公鑰對(duì)任何東西設(shè)置簽名。
簽名過程本身是由命令行工具 codesign 來完成的。如果你在 Xcode 中編譯一個(gè)應(yīng)用,這個(gè)應(yīng)用構(gòu)建完成之后會(huì)自動(dòng)調(diào)用 codesign 命令進(jìn)行簽名,codesign 也正是給你提供了許多格式友好并且有用錯(cuò)誤信息的那一個(gè)工具。你可以在 Xcode 的 project settings 中設(shè)置代碼簽名信息。

需要注意的是 Xcode 只允許你在有限的選項(xiàng)中進(jìn)行選擇,這些選項(xiàng)都是你既擁有公鑰也擁有私鑰的證書。所以如果在選項(xiàng)中沒有出現(xiàn)你想要的那一個(gè),那么你需要檢查的第一件事情就是你是否擁有這個(gè)證書的私鑰。在這里你需要區(qū)分開用于開發(fā)測(cè)試還是用于發(fā)布,如果你想要在機(jī)器上測(cè)試你的應(yīng)用,你需要用用于開發(fā)測(cè)試的那一對(duì)密匙來進(jìn)行簽名,如果你是要發(fā)布應(yīng)用,無論是給測(cè)試人員還是發(fā)布到 APP Store,你需要用用于發(fā)布的那一對(duì)密匙來進(jìn)行簽名。
一直以來,以上這些就是代碼簽名需要設(shè)置的全部,設(shè)置了這些就幾乎完成了。
但是在 Xcode 6 的 project settings 中出現(xiàn)了設(shè)置配置文件的選項(xiàng)。如果你選擇了某一個(gè)配置文件,你必須選擇這個(gè)配置文件的證書中所包含的公鑰所對(duì)應(yīng)的那個(gè)密匙對(duì),或者你可以選擇讓 Xcode 自動(dòng)完成正確的設(shè)置。關(guān)于這方面我們稍后再詳細(xì)說明,首先還是回到代碼簽名。
一個(gè)已簽名應(yīng)用的組成
一個(gè)已簽名的可執(zhí)行文件的簽名包含在 Mach-O 二進(jìn)制文件格式中;對(duì)于例如腳本這樣的非 Mach-O 可執(zhí)行文件,就存放在該文件系統(tǒng)的的擴(kuò)展屬性中。這種做法使得在 OS X 和 iOS 上的任何可執(zhí)行二進(jìn)制文件都可以被設(shè)置簽名:不論是動(dòng)態(tài)庫,命令行工具,還是 .app 后綴的程序包。這也意味著設(shè)置簽名的過程實(shí)際上會(huì)改動(dòng)可執(zhí)行文件的文件內(nèi)容,將簽名數(shù)據(jù)寫入二進(jìn)制文件中。
如果你擁有一個(gè)證書和它的私鑰,那么用 codesign 來設(shè)置簽名非常簡(jiǎn)單,我們現(xiàn)在嘗試用下面列出的這個(gè)證書來為 Example.app 設(shè)置簽名:
$ codesign -s 'iPhone Developer: Thomas Kollbach (7TPNXN7G6K)' Example.app
如果你想為某一個(gè) app 程序包重新設(shè)置簽名,那么這個(gè)工具就很有用了。為了重新設(shè)置簽名,你必須帶上 -f參數(shù),有了這個(gè)參數(shù),codesign 會(huì)用你選擇的簽名替換掉已經(jīng)存在的那一個(gè):
$ codesign -f -s 'iPhone Developer: Thomas Kollbach (7TPNXN7G6K)' Example.app
codesign 還可以為你提供有關(guān)一個(gè)可執(zhí)行文件簽名狀態(tài)的信息,這些信息在出現(xiàn)不明錯(cuò)誤時(shí)會(huì)提供巨大的幫助。舉例來說,$ codesign -vv -d Example.app 會(huì)列出一些有關(guān) Example.app 的簽名信息:
你需要查看的第一件事是以 Authority 開頭的那三行。這三行告訴你到底是哪一個(gè)證書為這個(gè) app 設(shè)置了簽名。在這里當(dāng)然是我的證書,iPhone Developer: Thomas Kollbach (7TPNXN7G6K)。我的這個(gè)證書則是被證書 Apple Worldwide Developer Relations Certification Authority 設(shè)置了簽名的,依此類推這個(gè)證書則是被證書 Apple Root CA 設(shè)置了簽名。
在 Format 中也包含了一些關(guān)于代碼的信息:Example.app 并不單單是一個(gè)可執(zhí)行文件,它是一個(gè)程序包,其中包含了一個(gè) arm64 二進(jìn)制文件。從 Executable 中的路徑信息你可以看出,這是一個(gè)以測(cè)試為目的的打包,所以是一個(gè) Mach-O thin 的二進(jìn)制文件。
在一堆診斷信息中還包含了兩個(gè)非常有趣的條目。 Identifier 是我在 Xcode 中設(shè)置的 bundle identifier。 TeamIdentifier 用于標(biāo)識(shí)我的工作組(系統(tǒng)會(huì)用這個(gè)來判斷應(yīng)用是否是由同一個(gè)開發(fā)者發(fā)布)。此外用于發(fā)布應(yīng)用的證書中也包含這種標(biāo)識(shí),這種標(biāo)識(shí)在區(qū)分同一名稱下的不同證書時(shí)非常有用。
現(xiàn)在這個(gè)二進(jìn)制文件已經(jīng)用證書設(shè)置好簽名。就像中世紀(jì)人用蠟來封印信封一樣,簽名就這樣封印了這個(gè)應(yīng)用。下面我們來檢查一下封印是否完好:
就像大多數(shù) UNIX 工具一樣,沒有任何輸出代表簽名是完好的。那么我下面破壞這個(gè)封印,只要修改一下這個(gè)二進(jìn)制文件:
修改已經(jīng)簽名的應(yīng)用會(huì)破壞封印,從命令行輸出我們可以看到代碼簽名正如我們所預(yù)期一樣起到了作用。
程序包和其他資源文件
對(duì)于命令行工具和腳本來說,只是一個(gè)可執(zhí)行文件被設(shè)置簽名,但是 iOS 和 OS X 的應(yīng)用和框架則是包含了它們所需要的資源在其中的。這些資源包括圖片和不同的語言文件,資源中也包括很重要的應(yīng)用組成部分例如 XIB/NIB 文件,存檔文件(archives),甚至是證書文件。所以為一個(gè)程序包設(shè)置簽名時(shí),這個(gè)包中的所有資源文件也都會(huì)被設(shè)置簽名。
為了達(dá)到為所有文件設(shè)置簽名的目的,簽名的過程中會(huì)在程序包中新建一個(gè)叫做 _CodeSignatue/CodeResources 的文件,這個(gè)文件中存儲(chǔ)了被簽名的程序包中所有文件的簽名。你可以自己去查看這個(gè)簽名列表文件,它僅僅是一個(gè) plist 格式文件。
這個(gè)列表文件中不光包含了文件和它們的簽名的列表,還包含了一系列規(guī)則,這些規(guī)則決定了哪些資源文件應(yīng)當(dāng)被設(shè)置簽名。伴隨 OS X 10.10 DP 5 和 10.9.5 版本的發(fā)布,蘋果改變了代碼簽名的格式,也改變了有關(guān)資源的規(guī)則。如果你使用10.9.5或者更高版本的 codesign 工具,在 CodeResources 文件中會(huì)有4個(gè)不同區(qū)域,其中的 rules 和 files 是為老版本準(zhǔn)備的,而 files2 和 rules2是為新的第二版的代碼簽名準(zhǔn)備的。最主要的區(qū)別是在新版本中你無法再將某些資源文件排除在代碼簽名之外,在過去你是可以的,只要在被設(shè)置簽名的程序包中添加一個(gè)名為 ResourceRules.plist 的文件,這個(gè)文件會(huì)規(guī)定哪些資源文件在檢查代碼簽名是否完好時(shí)應(yīng)該被忽略。但是在新版本的代碼簽名中,這種做法不再有效。所有的代碼文件和資源文件都必須設(shè)置簽名,不再可以有例外。在新版本的代碼簽名規(guī)定中,一個(gè)程序包中的可執(zhí)行程序包,例如擴(kuò)展 (extension),是一個(gè)獨(dú)立的需要設(shè)置簽名的個(gè)體,在檢查簽名是否完整時(shí)應(yīng)當(dāng)被單獨(dú)對(duì)待。
授權(quán)機(jī)制 (Entitlements) 和配置文件 (Provisioning)
到目前為止,我們都假設(shè)所有的證書起到的作用都是一樣的,并且假設(shè)如果我們有了一個(gè)有效的證書代碼簽名也就相應(yīng)的有效。然而這當(dāng)然不是唯一的規(guī)則。操作系統(tǒng)有許多標(biāo)準(zhǔn)來檢測(cè)你的代碼是否允許運(yùn)行。
這些標(biāo)準(zhǔn)并不是一成不變的。舉例來說,在 OS X 上一個(gè)應(yīng)用是否允許被開啟是由 Gatekeeper 的選項(xiàng)決定的,你可以在系統(tǒng)設(shè)置的安全選項(xiàng)中改變選項(xiàng)。在 Gatekeeper 選項(xiàng)中選擇 “受信任的開發(fā)者或者來自 Mac App Store” 會(huì)要求被打開的應(yīng)用必須被證書簽名,可以是 Mac App Store 開發(fā)者的應(yīng)用發(fā)布證書也可以是開發(fā)者 ID 證書。這些選項(xiàng)是由一個(gè)系統(tǒng)工具 spctl 來管理的,它管理著系統(tǒng)的所有安全評(píng)估策略。
在 iOS 上規(guī)則是不一樣的,無論是用戶還是開發(fā)者都不能改變應(yīng)用開啟策略,你必須有一個(gè)開發(fā)者帳號(hào)或者應(yīng)用發(fā)布證書才能讓應(yīng)用運(yùn)行在 iOS 系統(tǒng)上。
即使你可以讓應(yīng)用運(yùn)行起來,在 iOS 上你的應(yīng)用能做什么依然是受限制的。這些限制是沙盒管理的。沙盒和代碼簽名機(jī)制是不同的,這很重要。代碼簽名保證了這個(gè)應(yīng)用里所包含的內(nèi)容正如它所說的那樣不多不少,而沙盒則是限制了應(yīng)用訪問系統(tǒng)的資源。這兩種技術(shù)是相互合作來發(fā)揮作用的,它們都能阻止你的應(yīng)用運(yùn)行,也都能在 Xcode 中引起奇怪的問題。但是在日常開發(fā)過程中,沙盒可能會(huì)更經(jīng)常引起問題。沙盒機(jī)制在什么時(shí)候會(huì)引起問題呢,大多數(shù)情況下都是由于一個(gè)叫做授權(quán)的機(jī)制決定的。
授權(quán)機(jī)制
授權(quán)機(jī)制決定了哪些系統(tǒng)資源在什么情況下允許被一個(gè)應(yīng)用使用。簡(jiǎn)單的說它就是一個(gè)沙盒的配置列表,上面列出了哪些行為被允許,哪些會(huì)被拒絕。
很可能你已經(jīng)猜到授權(quán)機(jī)制也是按照 plist 文件格式來列出的。Xcode 會(huì)將這個(gè)文件作為 --entitlements 參數(shù)的內(nèi)容傳給 codesign ,這個(gè)文件內(nèi)部格式如下:
在 Xcode 的 Capabilities 選項(xiàng)卡下選擇一些選項(xiàng)之后,Xcode 就會(huì)生成這樣一段 XML。 Xcode 會(huì)自動(dòng)生成一個(gè) .entitlements 文件,然后在需要的時(shí)候往里面添加條目。當(dāng)構(gòu)建整個(gè)應(yīng)用時(shí),這個(gè)文件也會(huì)提交給 codesign 作為應(yīng)用所需要擁有哪些授權(quán)的參考。這些授權(quán)信息必須都在開發(fā)者中心的 App ID 中啟用,并且包含在配置文件中,稍后我們會(huì)詳細(xì)討論這一點(diǎn)。在構(gòu)建應(yīng)用時(shí)需要使用的授權(quán)文件可以在 Xcode build setting 中的 code signing entitlements 中設(shè)置。
在這個(gè)應(yīng)用中我啟用了 iCloud 鍵值對(duì)存儲(chǔ) (key-value storage) (com.apple.developer.ubiquity-kvstore-identifier) ,以及 iCloud 文檔存儲(chǔ) (com.apple.developer.ubiquity-container-identifiers)。另外我還將應(yīng)用添加進(jìn)了一個(gè) App Group (比如說為了與擴(kuò)展 (extensions) 共享數(shù)據(jù),com.apple.security.application-groups), 最后開啟了推送功能 (aps-environment)。這是一個(gè)開發(fā)版本,我會(huì)有將它連接到調(diào)試器的需求,這就需要將 get-task-allow 設(shè)為 true。另外,app id,也就是 bundle id 加上開發(fā)者 id,也被單獨(dú)列出來了。
當(dāng)然你并不能隨心所欲的取得授權(quán),你的應(yīng)用能否得到某一項(xiàng)授權(quán)是有特定的規(guī)定的。舉例來說,當(dāng) get-task-allow 被設(shè)定為 ture 時(shí),應(yīng)用只能在用于開發(fā)的證書簽名下運(yùn)行。你被允許使用的推送環(huán)境 (aps-environment) 也存在類似的限制。
根據(jù)操作系統(tǒng)版本的不同我們可選的授權(quán)項(xiàng)目是不一樣的,所以很難有一份列表可以詳盡地列出所有條目。至少在文檔 Adding Capabilities 中提到的所有功能都是需要經(jīng)過授權(quán)的。
授權(quán)信息會(huì)被包含在應(yīng)用的簽名信息中。如果你在這方面遇到了問題,可以嘗試查看簽名信息中具體包含了什么授權(quán)信息:$ codesign -d --entitlements - Example.app 會(huì)列出一個(gè)和前面的很像的 XML 格式的屬性列表。你可以將這個(gè)文件的內(nèi)容添加進(jìn)一個(gè)腳本,每次構(gòu)建應(yīng)用時(shí)用腳本檢查是否包含了推送服務(wù)的授權(quán)信息,以此確保推送服務(wù)工作正常。在這里推送服務(wù)只是一個(gè)例子,你使用的服務(wù)越多,這樣的時(shí)候都添加推送通知的授權(quán),以保證可以注冊(cè)推送通知。在新版本的 Xcode 6 之后,授權(quán)信息列表會(huì)以 Example.app.xcent這樣的名字的文件形式包含在應(yīng)用包中。在我看來,這么做是為了在出現(xiàn)配置錯(cuò)誤時(shí)提供更加有用的錯(cuò)誤信息。
配置文件
在整個(gè)代碼簽名和沙盒機(jī)制中有一個(gè)組成部分將簽名,授權(quán)和沙盒聯(lián)系了起來,那就是配置文件 (provisioning profiles)。
每一個(gè) iOS 開發(fā)者可能都花費(fèi)過相當(dāng)?shù)臅r(shí)間研究如何設(shè)置配置文件,這個(gè)環(huán)節(jié)也正是會(huì)經(jīng)常出問題的地方。
一個(gè)配置文件中存放了系統(tǒng)用于判斷你的應(yīng)用是否允許運(yùn)行的信息,這就意味著如果你的配置文件有問題,修復(fù)起來會(huì)相當(dāng)煩人。
一個(gè)配置文件是一組信息的集合,這組信息決定了某一個(gè)應(yīng)用是否能夠在某一個(gè)特定的設(shè)備上運(yùn)行。配置文件可以用于讓應(yīng)用在你的開發(fā)設(shè)備上可以被運(yùn)行和調(diào)試,也可以用于內(nèi)部測(cè)試 (ad-hoc) 或者企業(yè)級(jí)應(yīng)用的發(fā)布。Xcode 會(huì)將你在 project setting 中選擇的配置文件打包進(jìn)應(yīng)用。前面提到了,選擇配置文件是 Xcode 6 才提供的功能,在 Xcode 5 或更早版本中,配置文件是 Xcode 根據(jù)你選擇的簽名證書來選擇的。事實(shí)上同一個(gè)證書可以擁有多個(gè)不同的配置文件,因此讓 Xcode 自行選擇可能存在一些不確定性,最好的方式是你自主去選擇,在 Xcode 6 中終于提供了這個(gè)功能。

我們下面來仔細(xì)研究一下配置文件。如果你要在自己的機(jī)器上找到配置文件,在這個(gè)目錄下 ~/Library/MobileDevice/Provisioning Profiles。Xcode 將從開發(fā)者中心下載的全部配置文件都放在了這里。
不要驚訝,配置文件并不是一個(gè) plist 文件,它是一個(gè)根據(jù)密碼訊息語法 (Cryptographic Message Syntax) 加密的文件(下文中會(huì)簡(jiǎn)稱 CMS,但不要用這個(gè)簡(jiǎn)寫 Google,這不是一個(gè)很好的關(guān)鍵字)。如果你處理過 S/MIME 郵件或者證書你會(huì)對(duì)這種加密比較熟悉,詳細(xì)信息可以查看互聯(lián)網(wǎng)工程任務(wù)組 (IETF) 制定的 RFC 3852。
采用 CMS 格式進(jìn)行加密使得配置文件可以被設(shè)置簽名,所以在蘋果給你這個(gè)文件之后文件就不能被改變了。配置文件的簽名和應(yīng)用的簽名不是一回事,它是由蘋果直接在開發(fā)者中心 (developer portal) 中設(shè)置好了的。
某些版本的 OpenSSL 可以讀取這種格式,但是 OS X 自帶那個(gè)版本并不行。幸運(yùn)的是命令行工具 security也可以解碼這個(gè) CMS 格式,那么我們就用 security 來看看一個(gè) .mobileprovision 文件內(nèi)部是什么樣子:
$ security cms -D -I example.mobileprovision
這個(gè)命令會(huì)輸出簽名信息中的內(nèi)容,如果你親自試一下,接下來你會(huì)得到一個(gè) XML 格式的 plist 文件內(nèi)容輸出。
這個(gè)列表中的內(nèi)容是 iOS 用于判斷你的應(yīng)用是否能運(yùn)行在某個(gè)設(shè)備上真正需要的配置信息,每一個(gè)配置文件都有它自己的 UUID 。Xcode 會(huì)用這個(gè) UUID 來作為標(biāo)識(shí),記錄你在 build settings 中選擇了哪一個(gè)配置文件。
首先來看 DeveloperCertificates 這項(xiàng),這一項(xiàng)是一個(gè)列表,包含了可以為使用這個(gè)配置文件的應(yīng)用簽名的所有證書。如果你用了一個(gè)不在這個(gè)列表中的證書進(jìn)行簽名,無論這個(gè)證書是否有效,這個(gè)應(yīng)用都無法運(yùn)行。所有的證書都是基于 Base64 編碼符合 PEM (Privacy Enhanced Mail, RFC 1848) 格式的。要查看一個(gè)證書的詳細(xì)內(nèi)容,將編碼過的文件內(nèi)容復(fù)制粘貼到一個(gè)文件中去,像下面這樣:
然后讓 OpenSSL 來處理 openssl x509 -text -in file.pem。
回到配置文件中繼續(xù)往下看,你可能會(huì)注意到在 Entitlements 一項(xiàng)中包含了你的應(yīng)用的所有授權(quán)信息,鍵值就和之前在授權(quán)那節(jié)看到的一模一樣。
這些授權(quán)信息是你在開發(fā)者中心下載配置文件時(shí)在 App ID 中設(shè)置的,理想的情況下,這個(gè)文件應(yīng)該和 Xcode 為應(yīng)用設(shè)置簽名時(shí)使用的那一個(gè)同步,但這種同步并不能得到保證。這個(gè)文件的不一致是比較難發(fā)現(xiàn)的問題之一。
舉例來說,如果你在 Xcode 中添加了 iCloud 鍵值對(duì)存儲(chǔ)授權(quán) (com.apple.developer.ubiquity-kvstore-identifier),但是沒有更新,重新設(shè)置并下載新的配置文件,舊的配置文件規(guī)定你的應(yīng)用并沒有這一項(xiàng)授權(quán)。那么如果你的應(yīng)用使用了這個(gè)功能,iOS 就會(huì)拒絕你的應(yīng)用運(yùn)行。這也是當(dāng)你在開發(fā)者中心編輯了應(yīng)用的授權(quán),對(duì)應(yīng)的配置文件會(huì)被標(biāo)記為無效的原因。
如果你打開的是一個(gè)用于開發(fā)測(cè)試的證書,你會(huì)看到一項(xiàng) ProvisionedDevices,在這一項(xiàng)里包含了所有可以用于測(cè)試的設(shè)備列表。因?yàn)榕渲梦募枰惶O果簽名,所以每次你添加了新的設(shè)備進(jìn)去就要重新下載新的配置文件。
小結(jié)
代碼簽名和配置文件這一套大概是一個(gè) iOS 開發(fā)者必須處理的僅次于編碼的最復(fù)雜的問題之一。與在 Mac 或 PC 上直接的編譯運(yùn)行你的代碼不同,處理這些問題會(huì)是非常不同的經(jīng)歷。
雖然了解每一個(gè)部分是怎么運(yùn)作的很有幫助,但是要控制好所有這些設(shè)置和工具其實(shí)是一件很消耗時(shí)間的事情,特別是在一個(gè)開發(fā)團(tuán)隊(duì)中,到處發(fā)送證書和配置文件顯然很不方便。雖然蘋果在最近幾次發(fā)布的 Xcode 中都嘗試改善,但是我不是很確定每一項(xiàng)改動(dòng)都起到了好的作用。處理代碼簽名是每個(gè)開發(fā)者必過的大坑。
雖然處理代碼簽名對(duì)于開發(fā)者來說非常繁瑣,但不可否認(rèn)正是它使得 iOS 對(duì)于用戶來說是一個(gè)非常安全的操作系統(tǒng)。如果你注意安全相關(guān)的新聞,每一次出現(xiàn)號(hào)稱能在 iOS 上運(yùn)行的木馬或者惡意軟件,例如不怎么出名的 FinFisher,仔細(xì)看看詳細(xì)說明,都會(huì)寫明 “需要越獄”。說實(shí)話我還沒見過面向 iOS 的不需要越獄的病毒或者木馬。
所以為代碼簽名和配置文件進(jìn)行的這些麻煩設(shè)置并不是徒勞無功。