本文做者:oschina_2020javascript
iOS簽名類型有Development、AD-Hoc、In-House與App Store,而打包過程當中又涉及到各類證書、Provision Profile、entitlements、CertificateSigningRequest、p十二、AppID......各類概念一大堆,本文將從打包簽名的原理提及,並梳理徹底簽名的總體流程,最後講解重簽名的實現以及簽名機制中有哪些須要注意防禦的要點。java
爲了保證App的分發平臺是可控的,以及保證全部安裝到iOS設備上的App都是通過蘋果官方容許的,蘋果創建了iOS簽名打包機制。要了解iOS簽名機制的實現,咱們首先從簽名機制的原理提及。git
網絡數據的傳輸可使用對稱加密以及不對稱加密的方式進行安全防禦,對稱加密是指數據發送者(A)和接收者(B)雙方進行加解密的密鑰是一致的,但這樣會增長密鑰自身分發的不安全性:好比要如何保證密鑰在傳遞過程當中不被泄露。github
而不對稱加密則由A、B持有一對公私鑰進行加解密,公私鑰鑰匙是成對出現的。對於一個私鑰,有且只有一個與其對應的公鑰,私鑰保密、公鑰公開,可是不能經過公鑰推導出私鑰,使用私鑰加密的文件可用公鑰解密,反過來公鑰加密的文件也只能用私鑰進行解密。加密過程以下:算法
發送方(A)首先生成一對公私鑰鑰匙對,私鑰本身保管,公鑰則任意分發出去(每臺iOS設備終端其實已經包含Apple的公鑰)。安全
發送數據時,發送方使用私鑰對原數據加密成密文傳輸(加密打包ipa);網絡
接收方(B)收到密文後,使用以前已經獲取到的公鑰進行解密獲得數據內容(iOS設備驗證安裝ipa)。架構
這裏主要解決了兩個問題,一個是加密數據大小的問題,另外一個是如何驗證公鑰的有效性。app
1.2.1 信息摘要ide
前面已經講到,iOS打包安裝的過程當中會對ipa包進行加解密驗證。然而ipa安裝包大小動輒就有十幾M,大的有好幾G,那若是對這麼大的數據量進行加解密,確定效率是很是低的。而信息摘要則是解決了加密數據過大的問題,其原理是對信息內容經過一個很難被逆向推導的公式計算獲得一段哈希數值,它具備如下特色:
使用信息摘要技術在數據加密傳輸時,發送方先對文件內容使用哈希算法進行信息摘要計算,再對摘要內容進行加密,以後將文件內容以及摘要內容(已加密)發送出去。
接收方收到數據後,先解密獲得摘要內容,再依據相同的哈希算法對文件內容進行信息摘要計算,最後匹配接收到的哈希值與計算獲得的哈希值是否一致,若是一致那就說明傳輸過程是安全的。
這樣也就避免了對總體原數據加解密的計算過程,從而提升了驗證效率。
1.2.2 簽名證書
不對稱加密中的公鑰是公開的,誰均可以獲得,這樣也就存在了不安全性。好比主動攻擊者C冒充數據發送者A,將本身假裝後的公鑰分發給數據接收者B,從而達到監聽A、B之間通訊的目的,又或者是對A、B之間的通訊數據進行注入攻擊。
那爲了保證獲取公鑰的安全性,這裏引入CA認證(Certificate Authority)。CA是證實公鑰合法性的權威機構(Apple就屬於CA認證機構),它爲每一個使用公開密鑰的用戶發放一個數字證書,數字證書的做用是證實證書中列出的用戶合法擁有證書中列出的公開密鑰。用戶使用CA的公鑰對數字證書上的簽名進行驗證,若是驗證經過,也就認爲證書內包含的公鑰是有效的。
CA認證確保了用戶公鑰使用過程當中的安全性,iOS打包須要向蘋果開發者中心上傳.certSigningRequest
文件,而後配置獲得各類.cer
證書,這些流程中便包括了開發者向Apple CA認證中心註冊公鑰的過程。
.certSigningRequest 文件。從Mac的鑰匙串訪問中生成.certSigningRequest
文件,這個過程會從Mac終端生成一對鑰匙對,私鑰存儲在Mac中,公鑰則包含在.certSigningRequest
中。再將.certSigningRequest
文件上傳到Apple後臺即蘋果開發者中心,則能夠對應生成開發證書或者發佈證書(.cer
文件)。
.cer 文件:Apple後臺使用Apple私鑰對Mac公鑰進行簽名後生成的證書。
.p12 文件:Mac本地生成的鑰匙對私鑰。因爲私鑰是本地私有的,但你可使用.p12
將私鑰導出給其餘團隊成員使用。
Identifiers。Identifiers
是iOS設備安裝應用時用來識別不一樣App的惟一標識,點擊建立App IDs,同時勾選app所包含的權限:APNs、HealthKit、iCloud等。
entitlements。App使用到的各類權限(APNs、HealthKit、iCloud等),也是須要Apple驗證經過後才能生效的,Apple將這些權限開關統一稱爲Entitlements。當第一次在Xcode中勾選權限時,項目中會自動生成一個.entitlements後綴的文件,裏面記錄了App所擁有的權限。
Profiles。.cer
文件只是聲明瞭證書的類型,好比Apple Development、Apple Distribution、APNs推送等等,而至於使用什麼證書打包、AppID是什麼、打包的App包含了哪些功能、能夠在哪些設備上安裝,則是經過Provisioning Profile
描述文件(.mobileprovision
後綴)來講明的,蘋果後臺將全部這些信息組合後再使用Apple私鑰進行簽名,最後生成Provisioning Profile
描述文件:
發佈App至AppStore以前須要通過蘋果後臺審覈,審覈經過蘋果後臺會用Apple私鑰對App數據進行加密簽名生成ipa包;用戶從AppStore下載App後,使用設備內置的Apple公鑰解密驗證,驗證經過安裝成功。因爲AppStore分發的過程當中上傳審覈、下載安裝的整個過程都處在蘋果的生態鏈內,因此只須要一次驗證就能保證安全性。
從AppStore下載安裝App只須要一次數字簽名就足以保證安全性,但除了這種途徑蘋果還有其餘的安裝方式:
那這些安裝App的過程當中蘋果又是怎樣保證流程安全性的呢?答案就是雙重簽名機制,蘋果使用前面講到的Mac本地鑰匙對以及Apple後臺鑰匙對進行屢次數字簽名,從而保證總體流程的可控。
Mac 鑰匙串訪問
在本地生成一對公私鑰鑰匙對,下面默認爲公鑰L、私鑰L(L:Local)。
Apple已有一對公私鑰鑰匙對,私鑰A在Apple後臺,公鑰A內置到每一臺iOS設備終端(A:Apple)。
上傳公鑰L至Apple後臺,使用私鑰A對公鑰L進行數字簽名生成簽名證書.cer,同時使用私鑰L對額外信息(使用什麼證書打包、AppID、打包的App包含了哪些功能、能夠在哪些設備上安裝)進行簽名生成描述文件Provisioning Profile,以後將.cer和Provisioning Profile下載安裝到Mac機器上。
編譯打包app,選擇簽名證書.cer,打包指令會自動找到該證書對應的私鑰L(能匹配是由於鑰匙對是成對出現的,前提是本地必須已經存在L私鑰,也就是p12的安裝),而後使用私鑰L對app進行簽名。
這些簽名數據包含兩部分:Mach-O
可執行文件會把簽名直接寫入這個文件中,其餘資源文件則會保存在_CodeSignature
目錄下。你能夠將打包生成的.ipa
文件另存爲.zip
,解壓後對Payload
文件夾中的.app
文件右鍵、顯示包內容,就能夠看到簽名數據。
另外簽名過程當中對於App內包含的動態庫以及插件(Plugins、Watch、Frameworks
文件夾),每個都會單獨進行一次簽名,並生成各自的Mach-O
可執行文件和_CodeSignature
。
打包的過程當中會將描述文件Provisioning Profile命名爲embedded.mobileprovision放入到打包app中。
安裝/啓動,iOS設備使用內置的公鑰A驗證embedded.mobileprovision是否有效(設備是否在容許安裝列表內),同時再次驗證裏面包含的.cer證書籤名是否有效(證書過時與否)並取出公鑰L。
embedded.mobileprovision驗證經過,就使用公鑰L解密驗證app簽名信息:AppID是否對應、權限開關是否跟app裏的entitlements
一致等等。
全部驗證經過,安裝/啓動完成。
以上流程即是開發調試、AD-Hoc、In-House等方式打包安裝App的過程,區別只在於第⑤步中設備IDs
的匹配規則不一致。開發調試只安裝當前聯調的設備;AD-Hoc容許安裝到已在開發者帳號下注冊過的設備,且每一年最多容許100臺;In-House無設備數量限制,經常使用於企業內部App的分發。
ipa包重簽名主要針對的是非App Store的安裝包,App Store分發最終是上傳ipa文件到蘋果後臺審覈,經過後使用Apple私鑰加密,而後才能發佈安裝,不存在重簽入侵的可能。而開發調試、AD-Hoc、In-House等分發途徑生成的ipa包不存在蘋果後臺驗證的步驟,這也就意味着你能夠對任意的.app、
.ipa
文件進行重簽名。
回顧前面講到的簽名流程,真正對ipa包進行簽名的關鍵步驟(④⑤)是在Mac本地進行的,簽名過程當中須要知足三個條件:App
即軟件代碼編譯生成的產物、p12
證書以及Provisioning Profile
配置文件。其中App
的內容是動態變更的,Apple不會去驗證它,實際上也無需驗證,由於在開發調試過程當中,所開發的App確定是不停的迭代變化的,若是須要上線App Store那Apple只需在審覈階段對App內容進行把關驗證便可,而其餘分發渠道它則管不了。p12
以及Provisioning Profile
則是下載後主動安裝的,大部分狀況下都是由管理員建立下載好以後,導出分發給團隊成員。
iOS簽名調用的是codesign指令,你也能夠直接使用相關指令進行簽名,下面是codesign的經常使用指令:
# MAC終端輸入: codesign --help codesign --help Usage: codesign -s identity [-fv*] [-o flags] [-r reqs] [-i ident] path ... # sign codesign -v [-v*] [-R=<req string>|-R <req file path>] path|[+]pid ... # verify codesign -d [options] path ... # display contents codesign -h pid ... # display hosting paths
查看Xcode的編譯日誌,也能夠看到簽名的詳細信息
# 簽名指令 codesign -f -s "iPhone Distribution: XXX(證書名稱)" --entitlements entitlements.plist(Profile配置文件) XXX.app(簽名app)
首先獲取須要重簽名的ipa包,注意該ipa包必須是未加密的。若是是從App Store下載的ipa,須要砸殼解密後才能進行重簽名,你也能夠從越獄平臺下載。將獲取的.ipa
重命名爲.zip
,而後右鍵解壓,將會生成一個 Payload 文件夾,裏面包含.app
文件。
將簽名證書對應的Provisioning Profile
文件重命名爲 embedded.mobileprovision,並拷貝放到Payload文件夾中。同時右鍵.app
文件,顯示包內容,將前面的embedded.mobileprovision文件再拷貝一份放到.app
文件夾中,替換掉原有的embedded.mobileprovision
。
entitlements.plist是由簽名證書對應的Profile配置導出的簽名文件,它與前面截圖Xcode簽名日誌中的XXX.xcent
文件的做用相同。終端cd到Payload文件夾路徑,執行指令
# cd xxx/Payload,而後執行下面指令 security cms -D -i embedded.mobileprovision
將會打印出Profile配置的內容,找到<key>Entitlements</key>
,而後把<key>Entitlements</key>
下面<dict>...</dict>
的內容拷貝到新建的entitlements.plist
文件中(能夠經過Xcode生成plist文件,選Property List類型),最後將entitlements.plist
文件放到Payload文件夾中。
# 拷貝內容爲:<dict> ... </dict> <key>Entitlements</key> <dict> <key>application-identifier</key> <string>xxx</string> <key>keychain-access-groups</key> <array> <string>xxx</string> </array> <key>get-task-allow</key> <false/> <key>com.apple.developer.team-identifier</key> <string>xxx</string> </dict>
簽名證書名稱能夠在安裝證書後從鑰匙串中心查看
或者在終端使用如下指令查看:
security find-identity -v -p codesigning
準備工做完成,開始重簽名。先右鍵.app
顯示包內容,查看動態庫和插件(Plugins、Watch、Frameworks
文件夾),若是是我的證書須要移除Plugins、Watch
文件夾,由於我的證書無法簽名Extention。若是存在Frameworks
,則執行簽名指令,有多個的話則每個Frameworks都要重籤一次。
codesign -fs "簽名證書名稱" "Frameworks/xxx.framework(動態庫路徑)"
最後對app進行重簽名
codesign -f -s "iPhone Distribution: XXX(證書名稱)" --entitlements entitlements.plist(Profile配置文件) XXX.app(簽名app)
.ipa
,這樣重籤後的ipa便已生成了,你能夠經過iTunes、iTools或其餘途徑安裝到iOS設備上。ipa代碼注入通常經過動態庫來實現。新建動態庫在Xcode中選擇新建 TARTETS — Framework & Library — Framework
,而後在framework中添加自定義代碼,通常都是使用Runtime來注入附加功能。最後選擇framework要支持的架構,編譯後便獲得了最終動態庫。
對須要重簽名的.app右鍵顯示包內容,而後將動態庫拷貝到Framework
文件夾(沒有則新建)中。然而此時動態庫與app還沒創建關聯關係,動態庫須要注入MachO
中才能生效。注入使用yololib工具,下載yololib並編譯,將生產的命令複製到/usr/local/bin
或$PATH
中的其餘路徑,即可以在終端使用yololib
指令
## 經過yololib工具實現注入動態庫 yololib "MachO文件路徑" "須要注入的動態庫路徑"
注入成功後再對全部Framework簽名,最後對app重簽名,而後生成ipa文件。
這裏整理了一份用於重簽名的腳本 CJCodeSign,想了解更多關於簽名指令的內容可點擊查看詳情。
iOS重簽名實現,能夠發現用於簽名的私鑰資源(包括.cer
證書和Provisioning Profile
配置)和實際簽名的app包是沒有強關聯關係的,這也就帶來了兩方面的問題。
.cer
證書和Provisioning Profile
配置被用於其餘App的分發簽名,特別若是是In-House企業類型的證書,那是能夠進行無限制分發的,而一旦蘋果檢測到這種違規簽名的行爲,輕則撤銷證書,重則註銷企業開發者帳號!這也就是爲何必定要嚴格把控 p12
、Provisioning Profile
文件外發的緣由。全文完!
最後再附上重簽名腳本地址: CJCodeSign
lele8446,iOS開發深耕者,愛好分享、深⼊探討有溫度的內容,GitHub地址。