iOS的簽名機(jī)制很復(fù)雜,各種證書(shū),Provision Profile,entitlements,CertificateSigningRequest,p12,AppID,這篇文章從概念出發(fā),一步一步推出為什么會(huì)有這么對(duì)概念,希望能有助于理解iOS的App簽名的原理。
目的
在iOS出來(lái)之前,在主流操作系統(tǒng)(Mac,Windows,Linux)上開(kāi)發(fā)和運(yùn)行軟件是不需要簽名的,軟件隨便從哪里下載都能運(yùn)行,導(dǎo)致平臺(tái)對(duì)第三方軟件很難控制,盜版盛行。蘋(píng)果希望解決這樣的問(wèn)題,希望iOS平臺(tái)對(duì)第三方App有絕對(duì)控制權(quán),一定要保證每一個(gè)安裝到iOS上的App都是經(jīng)過(guò)蘋(píng)果官方允許的,怎么保證呢?就是通過(guò)簽名機(jī)制。
最簡(jiǎn)單的實(shí)現(xiàn)
要實(shí)現(xiàn)這個(gè)需求很簡(jiǎn)單,最直接的方式,蘋(píng)果官方生成一對(duì)公私鑰,私鑰由蘋(píng)果后臺(tái)保管,公鑰內(nèi)置到iOS設(shè)備里,在我們將App上傳到App Store上時(shí),蘋(píng)果后臺(tái)使用私鑰對(duì)App進(jìn)行簽名,iOS設(shè)備下載這個(gè)應(yīng)用后,用公鑰驗(yàn)證這個(gè)簽名,若簽名正確,則說(shuō)明這個(gè)App是經(jīng)過(guò)蘋(píng)果后臺(tái)認(rèn)證的,并且沒(méi)有被修改過(guò),這樣也就達(dá)到了蘋(píng)果的目的:保證iOS設(shè)備安裝的每一個(gè)APP都是經(jīng)過(guò)蘋(píng)果官方允許的。
如果我們的iOS設(shè)備安裝App只通過(guò)App Store這一種方式的話(huà),那么問(wèn)題到這里就已經(jīng)解決了,但是實(shí)際上除了從App Store上下載應(yīng)用,還可以以一下三種方式安裝一個(gè)APP:
- 1.作為開(kāi)發(fā)者,開(kāi)發(fā)App時(shí)直接進(jìn)行真機(jī)調(diào)試。
- 2.In-House 企業(yè)內(nèi)部分發(fā),可以直接安裝企業(yè)證書(shū)簽名后的App。
- 3.AD-Hoc 相當(dāng)于是企業(yè)分發(fā)的限制版,限制安裝設(shè)備數(shù)量。
蘋(píng)果要對(duì)這三種方式安裝的APP進(jìn)行孔子,就無(wú)法像上面這樣簡(jiǎn)單了。
新的需求
我們先來(lái)看第一個(gè),開(kāi)發(fā)時(shí)安裝APP,它有兩個(gè)需求:
- 1.安裝包不需要傳到蘋(píng)果服務(wù)器,可以直接安裝到手機(jī)上。
- 2.蘋(píng)果必須對(duì)這個(gè)安裝過(guò)程有控制權(quán),包括:
a.經(jīng)過(guò)蘋(píng)果允許才可以這樣安裝
b.不能被濫用導(dǎo)致非開(kāi)發(fā)App也能被安裝。
為了滿(mǎn)足這個(gè)需求,iOS簽名的復(fù)雜度也就開(kāi)始增加了。
蘋(píng)果給出的方案是使用雙層簽名,有一點(diǎn)繞,流程大概是下圖這樣:
1.在我們開(kāi)發(fā)使用的Mac上生成一對(duì)公鑰和私鑰,稱(chēng)為公鑰,私鑰L。L:Local。
2.蘋(píng)果有固定的一對(duì)公鑰和私鑰,私鑰在自己后臺(tái)保存,公鑰內(nèi)置到了iOS設(shè)備里,稱(chēng)為公鑰,私鑰A。A:Apple。
3.把公鑰L上傳到蘋(píng)果后臺(tái),用蘋(píng)果后臺(tái)的私鑰A去簽名公鑰L。得到了一份數(shù)據(jù)包括公鑰L及其簽名,這份數(shù)據(jù)稱(chēng)為證書(shū)。
4.在開(kāi)發(fā)時(shí),編譯完一個(gè)APP后,用第一步生成的私鑰L去簽名這個(gè)App,同時(shí)把第三步得到的證書(shū)一起打包進(jìn)App里,安裝到手機(jī)上。
5.在安裝時(shí),iOS系統(tǒng)取得證書(shū),通過(guò)系統(tǒng)內(nèi)置的公鑰A,去驗(yàn)證證書(shū)的數(shù)字簽名是否正確。
6.驗(yàn)證證書(shū)后確保了公鑰L是蘋(píng)果認(rèn)證的,再用公鑰L去驗(yàn)證App的簽名,這樣就間接驗(yàn)證了這個(gè)App安裝行為是否經(jīng)過(guò)蘋(píng)果允許。
加點(diǎn)東西
上述流程只解決了上面的第一個(gè)需求,也就是需要經(jīng)過(guò)蘋(píng)果的允許才可以安裝,還未解決第二個(gè)避免被濫用的問(wèn)題。怎么解決呢?蘋(píng)果加了兩個(gè)限制,一個(gè)是限制在蘋(píng)果后天注冊(cè)過(guò)的設(shè)備才可以安裝,二是限制簽名只能針對(duì)某個(gè)具體的App。
在上述的第三步中,蘋(píng)果用私鑰A去簽名我們本地公鑰L時(shí),實(shí)際上除了簽名公鑰L,還可以加上很多數(shù)據(jù),這些數(shù)據(jù)都可以保證是經(jīng)過(guò)蘋(píng)果官方認(rèn)證的,不會(huì)有被篡改的可能,那么我們就可以把AppID和設(shè)備ID添加進(jìn)去:
把允許安裝的設(shè)備ID和APP對(duì)應(yīng)的AppID等數(shù)據(jù),都在第三步這里和公鑰L一起,被私鑰A簽名,一起組成證書(shū)。在第五步驗(yàn)證時(shí)就可以拿到設(shè)備ID列表,判斷當(dāng)前設(shè)備是否符合要求。
最終流程
到這里這個(gè)證書(shū)已經(jīng)變得很復(fù)雜了,有很多額外的信息,實(shí)際上除了設(shè)備ID,AppID,還有其他信息也需要用蘋(píng)果簽名,像App里面的iCloud,后天運(yùn)行等蘋(píng)果都想控制,蘋(píng)果把這些權(quán)限開(kāi)關(guān)統(tǒng)稱(chēng)為entitlements,它也需要通過(guò)簽名去授權(quán)。
但是一個(gè)證書(shū)本來(lái)就有規(guī)范的格式,我們把這些雜七雜八的額外信息賽入證書(shū)是不合適的,因此蘋(píng)果另外搞了一個(gè)東西叫Provisioning Profile,一個(gè)Provisioning Profile里面就包含了證書(shū)以及上述提到的所有額外信息,以及所有信息的簽名。
所以最終流程就變成了這樣:
1.在你的Mac上生成一對(duì)公鑰和私鑰,稱(chēng)為公鑰L和私鑰L。
2.蘋(píng)果自己有一對(duì)固定的公鑰和私鑰,私鑰在蘋(píng)果后臺(tái),公鑰內(nèi)置在iOS設(shè)備中,分別稱(chēng)為私鑰A和公鑰A。
3.把公鑰L傳到蘋(píng)果后天,用蘋(píng)果后天的私鑰A去簽名公鑰L,得到一份數(shù)據(jù)包括公鑰L和簽名,這份數(shù)據(jù)稱(chēng)為證書(shū)。
4.在蘋(píng)果后臺(tái)申請(qǐng)好AppID,配置好設(shè)備ID列表,App權(quán)限開(kāi)關(guān),再加上第三步的證書(shū),組成的數(shù)據(jù)用蘋(píng)果后天的私鑰A簽名,把數(shù)據(jù)和簽名一起組成一個(gè)Provisioning Profile文件,下載到本地Mac。
5.在開(kāi)發(fā)時(shí),編譯完一個(gè)App后,用本地的私鑰L對(duì)這個(gè)App進(jìn)行簽名,同時(shí)把第四步生成的Provisionning Profile一起打包進(jìn)App里,文件名為embeded.mobileprovision,把App安裝到手機(jī)。
6.在安裝時(shí),就可以使用iOS設(shè)備里內(nèi)置的公鑰A來(lái)驗(yàn)證Provisioning Profile的數(shù)字簽名是否正確。
7.如果數(shù)字簽名沒(méi)有問(wèn)題,那么就能確保設(shè)備ID,AppID,entitlements,和App都是經(jīng)過(guò)蘋(píng)果認(rèn)證的,可以安裝到iOS設(shè)備上。
上面的步驟對(duì)應(yīng)我們平時(shí)具體操作和概念是這樣的:
1.第一步對(duì)應(yīng)的是從keychain里“從這證書(shū)頒發(fā)機(jī)構(gòu)請(qǐng)求證書(shū)”,這樣就在本地生成了一對(duì)公私鑰,保存的額CertificateSigningRequest就是公鑰,公鑰保存在本地電腦里。
2.第二步蘋(píng)果處理,不用管。
3.第三步把CertificateSigningRequest上傳到蘋(píng)果后天,生成證書(shū),并下載到本地。
4.第四步是在蘋(píng)果網(wǎng)站操作的,配置AppID,設(shè)備ID,權(quán)限等,生成Provisioning Profile文件,并下載Provisioning Profile文件到本地。
5.xcode通過(guò)第三步下載下來(lái)的證書(shū),去找對(duì)應(yīng)的本地私鑰,用本地私鑰去簽名App,并把Provisioning Profile文件一起打包進(jìn)去,安裝進(jìn)iOS設(shè)備。
總結(jié)一些概念:
1.證書(shū):內(nèi)容是公鑰或者私鑰,由其它機(jī)構(gòu)對(duì)其簽名組成的數(shù)據(jù)包。
2.entitlements:包含了App權(quán)限開(kāi)關(guān)列表,AppID,設(shè)備ID等。
3.CertificateSigningRequest:本地公鑰。
4.p12:本地私鑰。
5.Provisioning Profile:包含證書(shū),entitlements等數(shù)據(jù),并由蘋(píng)果后臺(tái)私鑰簽名的數(shù)據(jù)包。
我們平時(shí)的操作
按照上面的流程,那么對(duì)于開(kāi)發(fā)人員來(lái)說(shuō),應(yīng)該是我們每次新建一個(gè)項(xiàng)目也就是有一個(gè)新的AppID時(shí),都應(yīng)該去申請(qǐng)一對(duì)本地公私鑰,上傳公鑰到蘋(píng)果后臺(tái),然后下載證書(shū),但是實(shí)際上我們并沒(méi)有這么做,好像很少需要去keychain請(qǐng)求本地公私鑰,這是為什么呢?
這里的原因就是iOS Team Provisioning Profile。
iOS Team Provisioning Profile是第一次使用xcode添加設(shè)備時(shí),xcode自動(dòng)生成的,它包含了xcode生成的一個(gè)Wildcard AppID(匹配所有應(yīng)用程序,賬戶(hù)里面的所有device,所有Development Certificates),因此team中的所有成員都可以使用這個(gè)iOS Team Provisioning Profile在team的所有設(shè)備上調(diào)試所有的應(yīng)用程序,并且當(dāng)有新設(shè)別添加進(jìn)來(lái)時(shí),xcode會(huì)更新這個(gè)文件。
如此一來(lái),只要我們有一對(duì)本地公私鑰,并且通過(guò)這個(gè)本地的公鑰上傳給蘋(píng)果獲取了證書(shū),那么以后我們運(yùn)行任何App,在任何iOS設(shè)備上運(yùn)行,都可以使用這個(gè)本地私鑰和證書(shū),而沒(méi)有必要每次去創(chuàng)建新的公私鑰和獲取證書(shū)。
下面我從我的項(xiàng)目中找出一個(gè)iOS Team Provisioning Profile,我們可以一起來(lái)看一下它的結(jié)構(gòu):
第一個(gè)是AppID,這里的AppID是我當(dāng)前應(yīng)用的AppID。
第二個(gè)是證書(shū),這就是選擇了我本地的一個(gè)證書(shū),是上面的流程中上傳本地的公鑰得到的證書(shū)。
第三個(gè)是team,這個(gè)是我在項(xiàng)目中選擇的,這個(gè)team決定了我用哪個(gè)證書(shū)。
第五個(gè)是entitlements,就是一系列的權(quán)限開(kāi)關(guān)。
通過(guò)這個(gè)iOS Team Provisioning Profile的結(jié)構(gòu)我們就能明白,iOS Team Provisioning Profile中保存著很多分證書(shū),很多AppID,很多設(shè)備ID,entitlements。當(dāng)我們需要在一個(gè)指定的iOS設(shè)備上運(yùn)行一個(gè)指定的App時(shí),iOS Team Provisioning Profile就會(huì)得到這個(gè)AppID和這個(gè)設(shè)備ID以及它對(duì)應(yīng)entitlements,組成這個(gè)特定的Provisioning Profile,打包進(jìn)APP里面。這樣就不需要我們每次去申請(qǐng)證書(shū),生成Provisioning Profile文件了,非常方便。
注:這篇文章主要是把大牛的文章摘抄了一遍,由于大牛寫(xiě)的太好了,讓我茅塞頓開(kāi),于是想重新寫(xiě)一次加深理解,也方便以后查閱。
大牛博客:
iOS App 簽名的原理
關(guān)于 Certificate、Provisioning Profile、App ID 的介紹及其之間的關(guān)系