iOS簽名原理
在iOS真機(jī)調(diào)試和發(fā)布上線的時候,我們可能已經(jīng)習(xí)慣了配置各種證書、描述文件,等這一繁瑣的步驟。但是對于背后我們?yōu)槭裁匆渲眠@些東西,以及其背后的原理之前一直沒有做過分析研究,最近有空就簡單的研究了一下!
一、背景
我們都知道蘋果手機(jī)的正版APP只能去App Store下載,而其他系統(tǒng)的手機(jī)比如安卓手機(jī)的APP現(xiàn)在的途徑有很多,這些軟件是不需要簽名的。而蘋果為了控制每一個安裝在蘋果手機(jī)上的APP都是經(jīng)過蘋果官方認(rèn)證的,于是就采用了簽名機(jī)制。
1.對稱加密
對稱加密是密碼學(xué)中一類加密算法的統(tǒng)稱,這類算法在加密與解密時使用相同的密鑰,或者使用兩個可以簡單的相互推算的密鑰。常見的對稱加密算法有DES、3DES、AES、RC5等。相比非對稱加密算法,對稱加密算法的優(yōu)點是加解密的速度很快。
2.非對稱加密
非對稱加密是指加密密鑰與解密密鑰是成對出現(xiàn)的,其中一個對外公開,叫公鑰,另一個末公開的叫私鑰,幾乎不能從一個密鑰計算出另一個密鑰。通過私鑰加密的只能通過公鑰解密,公鑰加密的只能通過私鑰解密。最著名的非對稱加密算法是RSA算法。當(dāng)然如此強大可靠的安全性是在犧牲加密速度的基礎(chǔ)上得到的。
通常我們所說的簽名就是數(shù)字簽名,它是基于非對稱加密算法實現(xiàn)的。對稱加密是通過同一份密鑰加密和解密數(shù)據(jù),而非對稱加密則有兩份密鑰,分別是公鑰和私鑰,用公鑰加密的數(shù)據(jù),要用私鑰才能解密;用私鑰加密的數(shù)據(jù),要用公鑰才能解密。這里的非對稱加密就是我們所熟知的RSA,要了解RSA背后的數(shù)學(xué)原理可以參考RSA算法原理(一)(二)
二、從App Store安裝APP
這個過程的簽名方式相對簡單一些。蘋果官方生成一對公私鑰,在蘋果手機(jī)里面內(nèi)置一個公鑰,私鑰由蘋果后臺保存,我們傳App上AppStore時,蘋果后臺用私鑰對App數(shù)據(jù)值的MD5值進(jìn)行簽名,iOS系統(tǒng)下載這個App后,用公鑰驗證這個簽名,若簽名正確,這個App肯定由蘋果后臺認(rèn)證的,并且沒有被修改過,也就達(dá)到了蘋果的需求:保證安裝的每一個App都是經(jīng)過蘋果認(rèn)證允許的。
三、其他方式安裝APP
在實際工作當(dāng)中,我們還有一些其他的方式把APP安裝到手機(jī)上:
開發(fā)App時可以直接把開發(fā)中的應(yīng)用安裝進(jìn)手機(jī)調(diào)試;
In-House企業(yè)內(nèi)部分發(fā),可以直接安裝企業(yè)證書簽名后的App;
AD-Hoc相當(dāng)于企業(yè)分發(fā)的限制版,限制安裝設(shè)備數(shù)量,較少用。
蘋果對這幾種方式安裝的控制過程就變得復(fù)雜了,即要保證APP的安裝時經(jīng)過蘋果認(rèn)證的,又要控制APP不能被隨便安裝到其他設(shè)備上,以及一些其他的權(quán)限,為了達(dá)到這樣的目的,蘋果采用的流程大致是這個樣子
基本流程:

1、在Mac上生成一對公私鑰,這里稱公鑰M,私鑰M。
2、蘋果自己有固定的一對公私鑰,跟上面AppStore例子一樣,私鑰在蘋果后臺,公鑰內(nèi)置在每個iOS設(shè)備上,這里稱為公鑰A,私鑰A。
3、把公鑰M上傳到蘋果后臺,用蘋果后臺里的私鑰A去簽名公鑰M。得到一份數(shù)據(jù)包含了公鑰M以及其簽名(也就是公鑰的HASH值),把這份數(shù)據(jù)稱為證書。
4、在開發(fā)時,編譯完一個App后,用本地的私鑰M對這個App進(jìn)行簽名,同時把第三步得到的證書一起打包進(jìn)App里,安裝到手機(jī)。
5、在安裝時,iOS系統(tǒng)取得證書,通過系統(tǒng)內(nèi)置的公鑰A,去驗證證書的數(shù)字簽名是否正確。
驗證證書確保公鑰M是蘋果認(rèn)證過的,再用公鑰M去驗證App的簽名,這里就間接驗證了這個App的安裝行為是否經(jīng)過蘋果官方允許。(這里只驗證安裝行為,不驗證App是否被改動,因為開發(fā)階段App內(nèi)容總是不斷變化的,蘋果不需要管)。
最終流程:
上述流程只解決了上面第一個需求,也就是需要經(jīng)過蘋果允許才可以安裝,還未解決第二個避免被濫用的問題。怎么解決呢?蘋果加了兩個限制,一是限制在蘋果后臺注冊過的設(shè)備才可以安裝;二是限制簽名只能針對某一個具體的App。
那么它到底是怎么添加這兩個限制的呢?在上述第三步,蘋果用私鑰A簽名我們的本地公鑰M時,實際上除了簽名本地公鑰M外,還可以加上無限多數(shù)據(jù),這些數(shù)據(jù)都可以保證是經(jīng)過蘋果官方認(rèn)證的,不會有被篡改的可能。

可以把允許安裝的設(shè)備ID列表和App對應(yīng)的AppID等數(shù)據(jù),都在第三步這里跟公鑰M一起組成證書,再用蘋果私鑰A對這個證書簽名。在最后第5步驗證時就可以拿到設(shè)備ID列表,判斷當(dāng)前設(shè)備是否符合要求。根據(jù)數(shù)字簽名的原理,只要數(shù)字簽名通過驗證,第5步這里的設(shè)備IDs/AppID/公鑰M就都是經(jīng)過蘋果認(rèn)證的,無法被修改,蘋果就可以限制可安裝的設(shè)備和APP,避免濫用。
到這里這個證書已經(jīng)變得很復(fù)雜了,有很多額外信息,實際上除了設(shè)備ID/AppID,還有其他信息也需要在這里用蘋果簽名,像App里iCloud、push、后臺運行 等權(quán)限蘋果都想控制,蘋果把這些權(quán)限開關(guān)統(tǒng)稱為Entitlements,它也需要通過簽名去授權(quán)。
實際上一個證書本來就有規(guī)定的格式規(guī)范,上面我們把各種額外的信息塞入證書里是不合適的,于是蘋果另外搞了一個東西,叫Provisioning Profile,一個Provisioning Profile里就包含了證書以及上述提到的所有額外信息,以及所有信息的簽名。
所以,就成這樣了:
在 Mac 上生成一對公私鑰,這里稱為公鑰M,私鑰M。
蘋果自己有固定的一對公私鑰,跟上面 AppStore 例子一樣,私鑰在蘋果后臺,公鑰在每個iOS設(shè)備上。這里稱為公鑰A,私鑰A。A:Apple
把公鑰M傳到蘋果后臺,用蘋果后臺里的私鑰A去簽名公鑰M。得到一份數(shù)據(jù)包含了公鑰M以及其簽名,把這份數(shù)據(jù)稱為證書。
在蘋果后臺申請AppID,配置好設(shè)備ID列表和APP可使用的權(quán)限,再加上第3步的證書,組成的數(shù)據(jù)用私鑰A簽名,把數(shù)據(jù)和簽名一起組成一個Provisioning Profile文件,下載到本地Mac開發(fā)機(jī)。
在開發(fā)時,編譯完一個APP后,用本地的私鑰M對這個APP進(jìn)行簽名,同時把第4步得到的Provisioning Profile文件打包進(jìn)APP里,文件名為 embedded.mobileprovision,把APP安裝到手機(jī)上。
在安裝時,iOS系統(tǒng)取得證書,通過系統(tǒng)內(nèi)置的公鑰A,去驗證 embedded.mobileprovision的數(shù)字簽名是否正確,里面的證書簽名也會再驗一遍。
確保了embedded.mobileprovision里的數(shù)據(jù)都是蘋果授權(quán)以后,就可以取出里面的數(shù)據(jù),做各種驗證,包括用公鑰M驗證APP簽名,驗證設(shè)備ID是否在ID列表上,AppID是否對應(yīng)得上,權(quán)限開關(guān)是否跟APP里的Entitlements對應(yīng)等。
開發(fā)者證書從簽名到認(rèn)證最終蘋果采用的流程大致是這樣,還有一些細(xì)節(jié)像證書有效期/證書類型等就不細(xì)說了。
上面的步驟對應(yīng)到我們平常具體的操作和概念是這樣的:
第1步對應(yīng)的是keychain里的“從證書頒發(fā)機(jī)構(gòu)請求證書”,這里就本地生成了一對公私鑰,保存的CertificateSigningRequest就是公鑰,私鑰保存在本地電腦里。
第2步蘋果自己處理,我們不用管。
第3步對應(yīng)把CertificateSigningRequest傳到蘋果后臺生成證書,并下載到本地。這時本地有兩個證書,一個是第1步生成的,一個是這里下載回來的,keychain會把這兩個證書關(guān)聯(lián)起來,因為它們的公私鑰是對應(yīng)的,在Xcode選擇下載回來的證書的時,實際上會找到keychain里面對應(yīng)的私鑰去簽名。這里私鑰只有生成它的這臺Mac才有,如果別的Mac也要編譯簽名這個App,把私鑰導(dǎo)出給其他Mac使用,在keychain里面導(dǎo)出私鑰,就會存成.p12文件,其他Mac打開后就導(dǎo)入私鑰。
第4步都是在蘋果網(wǎng)站上操作,配置AppID、權(quán)限、設(shè)備等,最后下載 Provisioning Profile文件。
第5步Xcode會通過第3步下載回來的證書(存著本地公鑰),在本地找到對應(yīng)的私鑰(第1步生成的),用本地私鑰去簽名App,并把Provisioning Profile文件命名為embedded.mobileprovision一起打包進(jìn)去。這里對App的簽名數(shù)據(jù)保存分為兩部分,Mach-O可執(zhí)行文件會把簽名直接寫入這個文件里,其他資源文件則會保存在_CodeSignature目錄下。
第6、7步的打包和驗證都是 Xcode 和 iOS 系統(tǒng)自動做的事。
四、總結(jié)
幾個概念:
證書:內(nèi)容是公鑰或私鑰,由其他機(jī)構(gòu)對其簽名組成的數(shù)據(jù)包。
Entitlements:包含了App權(quán)限開關(guān)列表。
CertificateSigningRequest:本地公鑰。
.p12:本地私鑰,可以導(dǎo)入到其他電腦。
Provisioning Profile:包含了 證書/Entitlements 等數(shù)據(jù),并由蘋果后臺私鑰簽名的數(shù)據(jù)包。
其他發(fā)布方式
前面以開發(fā)包為例子說了簽名和驗證的流程,另外兩種方式In-House企業(yè)簽名和AD-Hoc流程也是差不多的,只是企業(yè)簽名不限制安裝的設(shè)備數(shù),另外需要用戶在iOS系統(tǒng)設(shè)置上手動點擊信任這個企業(yè)才能通過驗證。
而AppStore的簽名驗證方式有些不一樣,前面我們說到最簡單的簽名方式,蘋果在后臺直接用私鑰簽名App就可以了,實際上蘋果確實是這樣做的,如果去下載一個AppStore的安裝包,會發(fā)現(xiàn)它里面是沒有embedded.mobileprovision文件的,也就是它安裝和啟動的流程是不依賴這個文件,驗證流程也就跟上述幾種類型不一樣了。
因為上傳到AppStore的包蘋果會重新對內(nèi)容加密,原來的本地私鑰簽名就沒有用了,需要重新簽名,從AppStore下載的包蘋果也并不打算控制它的有效期,不需要內(nèi)置一個embedded.mobileprovision去做校驗,直接在蘋果用后臺的私鑰重新簽名,iOS安裝時用本地公鑰驗證App簽名就可以了。
那為什么發(fā)布AppStore的包還是要跟開發(fā)版一樣搞各種證書和Provisioning Profile,因為蘋果想做統(tǒng)一管理,Provisioning Profile里包含一些權(quán)限控制,AppID 的檢驗等,蘋果不想在上傳AppStore 包時重新用另一種協(xié)議做一遍這些驗證,就不如統(tǒng)一把這部分放在 Provisioning Profile里,上傳AppStore時只要用同樣的流程驗證這個 Provisioning Profile是否合法就可以了。
所以 App 上傳到AppStore后,就跟你的 證書 / Provisioning Profile 都沒有關(guān)系了,無論他們是否過期或被廢除,都不會影響AppStore 上的安裝包。
以上就是整個簽名的大致分析。
iOS簽名簡潔過程
第一步:從KeyChain生成 CertificateSigningRequest.certSigningRequest。
得到 本地文件CertificateSigningRequest.certSigningRequest包含用戶信息,公鑰。
得到 Access|Keys中一對Public/Private Key ,公鑰/私鑰。
第二步申請證書:
通過CSR文件的公鑰生成證書,包含開發(fā)者信息,公鑰信息,使用蘋果私鑰加密。下載到本地使用蘋果公鑰解密,得到KeyChain中的證書文件,
第三步 申請授權(quán)描述文件:
使用蘋果私鑰加密,包含AppId,證書,功能授權(quán)列表,設(shè)備列表等信息。
第四步 App打包生成:
私鑰簽名
第五步 App安裝驗證:
設(shè)備使用CA證書(WWDRCA.cer)的公鑰解密Provisioning Profile得到app 公鑰和app內(nèi)容摘要,驗證Provisioning Profile的合法性。
使用app公鑰解密app來判斷App的合法性,使用app內(nèi)容摘要判斷app是否被篡改。
重簽名
重簽名的解釋:
當(dāng)在Xcode進(jìn)行archive或者通過腳本打包ipa的時候,通常到最后一步,要對包有一個簽名過程,對簽名起到關(guān)鍵作用的是配置好證書和描述文件。也就是一套證書,描述文件最終簽名好一個對應(yīng)的ipa。
而所謂的重簽名的概念就是,可以把一個已經(jīng)存在的ipa重新配置一套證書和描述文件,再簽名生成一個新的ipa包。
ios重新簽名的核心原理是使用?codesign?命令,當(dāng)然也許完成一些額外的操作。
大致流程
1、解壓ipa

2、刪除舊的簽名

3、復(fù)制新的描述文件

4、用新的證書簽名

5、壓縮成ipa

在這個過程中,最重要是這個 entitlements.plist文件的問題。
entitlements.plist是一個比較重要的文件,涉及到app的權(quán)限及簽名相關(guān)問題。
那么,如何得到這個文件呢?我們可以通過這條命令

獲取到這個文件的內(nèi)容,大致格式如下:

注意:這里我們我么需要把紅色的地方替換成新的簽名文件的bundleid和teamid.
當(dāng)然,有的朋友可能會問,有沒有更簡單的操作呢?
實際上是有的。
著名的自動化工具fastlane,就封裝了一個resign的工具,專門可以用來對app進(jìn)行重簽名的。具體可看:https://docs.fastlane.tools/actions/sigh/#resign
使用方法也很簡單, 一行命令即可。

參考相關(guān)文章:
鏈接:?Ios下app重簽名的原理及使用教程 - Jason.z博客?