準(zhǔn)備工作
代碼簽名 (code signing) 對(duì)一個(gè)App來講至關(guān)重要绳姨,是iOS系統(tǒng)安全的重要組成部分甥捺,決定了App的哪些功能是被授權(quán)或者禁止的兆衅。盡管各種證書烛谊、配置文件等讓初學(xué)這頭痛不已风响,但確實(shí)給用戶帶來了極大的安全保障。
文件及環(huán)境:
- 一個(gè)蘋果的認(rèn)證部門 Apple Worldwide Developer Relations CA頒發(fā)的Certificate
- 基于該Certificate生成的mobileprovision文件
- 系統(tǒng)環(huán)境 OSX 10.11.5
證書及文件
Certificate
Certificate是如何生成的呢丹禀?
首先需要開發(fā)者生成一個(gè)CertificateSigningRequest.certSigningRequest文件状勤,具體步驟為鑰匙串訪問?證書助理?從證書頒發(fā)機(jī)構(gòu)請(qǐng)求證書,按照提示填寫相關(guān)信息双泪,保存到磁盤即可持搜。
等到CertificateSigningRequest.certSigningRequest后,通過 openssl asn1parse -i -in CertificateSigningRequest.certSigningRequest
查看如下:
這個(gè)文件包含:
1焙矛,申請(qǐng)者信息
2葫盼,申請(qǐng)者公鑰,此信息是申請(qǐng)者使用的私鑰對(duì)應(yīng)的公鑰
3村斟,摘要算法(SHA)和公鑰加密算法(RSA)
此文件包含了我的信息贫导,使用了sha1摘要算法和RSA公鑰加密算法。蘋果的Meber Center在拿到certSigningRequest文件后蟆盹,將信息記錄下來孩灯,并簽發(fā)出相關(guān)的證書(Certificate),car證書包含哪些信息呢逾滥?又是如何使用certSigningRequest文件中的公鑰呢钱反?我們用openssl來看一下證書的內(nèi)容:
openssl x509 -inform der -in ios_distribution.cer -noout -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
05:33:03:2c:57:2a:ad:6c
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, O=Apple Inc., OU=Apple Worldwide Developer Relations, CN=Apple Worldwide Developer Relations Certification Authority
Validity
Not Before: Jul 10 07:45:50 2014 GMT
Not After : Jul 9 07:45:50 2017 GMT
Subject:......
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (2048 bit)
Modulus (2048 bit):
Signature Algorithm: sha1WithRSAEncryption
a8:b8:b5:ea:74:e9:06:d0:52:90:21:10:10:f1:f6:ce:1f:e7:
73:08:4f:ca:02:f8:73:66:06:36:48:5b:46:7d:ac:be:bd:c4:
......
d5:46:0a:7b
Data
域即為證書的實(shí)際內(nèi)容,與Data
域平級(jí)的Signature Algorithm
實(shí)際就是蘋果的CA(Certificate Authority)的公鑰匣距,Data域包含我的蘋果賬號(hào)信息面哥,其中最為重要的是我的公鑰(Subject Public Key Info),這個(gè)公鑰與我本機(jī)的私鑰是對(duì)應(yīng)的毅待。當(dāng)我們雙擊安裝完證書后尚卫,KeyChain會(huì)自動(dòng)將這對(duì)密鑰關(guān)聯(lián)起來
,所以在KeyChain中可以看到類似的效果:
后續(xù)在程序上真機(jī)的過程中尸红,會(huì)使用這個(gè)私鑰吱涉,對(duì)代碼進(jìn)行簽名,而公鑰會(huì)附帶在mobileprovision
文件中外里,打包進(jìn)app怎爵。那么mobileprovision又從哪里來?有什么作用呢盅蝗?
mobileprovision
首先來看一張圖:
在Apple Developer Center通過之前生成的Certificate來生成mobileprovision配置文件鳖链,它將授權(quán)和沙盒聯(lián)系了起來,可以用于讓應(yīng)用在你的開發(fā)設(shè)備上可以被運(yùn)行和調(diào)試墩莫,也可以用于內(nèi)部測試 (ad-hoc) 或者企業(yè)級(jí)應(yīng)用的發(fā)布芙委。配置文件并不是一個(gè) plist 文件,它是一個(gè)根據(jù)密碼訊息語法 (Cryptographic Message Syntax) 加密的文件(下文中會(huì)簡稱 CMS)狂秦。security 也可以解碼這個(gè) CMS 格式灌侣,那么我們就用 security 來看看一個(gè) mobileprovision 文件內(nèi)部是什么樣子:
$ security cms -D -i example.mobileprovision
你會(huì)得到一個(gè) XML 格式的 plist 文件內(nèi)容輸出,DeveloperCertificates 這項(xiàng)裂问,這一項(xiàng)是一個(gè)列表侧啼,包含了可以為使用這個(gè)配置文件的應(yīng)用簽名的所有證書。如果你用了一個(gè)不在這個(gè)列表中的證書進(jìn)行簽名堪簿,無論這個(gè)證書是否有效痊乾,這個(gè)應(yīng)用都無法運(yùn)行。ProvisionedDevices戴甩,在這一項(xiàng)里包含了所有可以用于測試的設(shè)備列表符喝。因?yàn)榕渲梦募枰惶O果簽名,所以每次你添加了新的設(shè)備進(jìn)去就要重新下載新的配置文件甜孤。具體如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>DeveloperCertificates</key>
<array>
<data>MIIF0DCCBLi......Oo7Clog==</data>
</array>
<key>ProvisionsAllDevices</key>
<true/>
<key>TeamIdentifier</key>
<array>
<string>C789GLWV85</string>
</array>
<key>Version</key>
<integer>1</integer>
</dict>
</plist>
證書(及其對(duì)應(yīng)的私鑰)和配置文件(mobileprovision)是簽名和打包的兩個(gè)必要文件协饲,如果要重新簽名一個(gè)App,就需要在這兩個(gè)上面動(dòng)手腳了缴川。
首先來了解一個(gè)已經(jīng)簽名了的App包含的內(nèi)容茉稠,$ codesign -vv -d Example.app
會(huì)列出一些有關(guān) Example.app
的簽名信息:
Executable=/Users/pandora/Desktop/shell/gcdsample/Payload/GCDSample.app/GCDSample
Identifier=com.baidu.GCDSample
Format=app bundle with Mach-O universal (armv7 arm64)
CodeDirectory v=20200 size=851 flags=0x0(none) hashes=19+5 location=embedded
Signature size=4700
Authority=iPhone Developer: 張三 (AFCH46B9XZ)
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=Jul 18, 2016, 17:23:01
Info.plist entries=26
TeamIdentifier=RYRPKMVKDL
Sealed Resources version=2 rules=12 files=7
Internal requirements count=1 size=180
Authority=iPhone Developer: 張三 (AFCH46B9XZ)
就是我的證書,iPhone Developer是開發(fā)使用把夸,如果是發(fā)布證書的話則顯示為 iPhone Distribution而线,Identifier是我在 Xcode 中設(shè)置的 bundle identifier。TeamIdentifier用于標(biāo)識(shí)我的工作組(系統(tǒng)會(huì)用這個(gè)來判斷應(yīng)用是否是由同一個(gè)開發(fā)者發(fā)布),可以通過私鑰共享的方式進(jìn)行團(tuán)隊(duì)開發(fā)膀篮,首先嘹狞,將存儲(chǔ)在keychain中的證書所對(duì)應(yīng)的私鑰導(dǎo)出為p12文件,其他團(tuán)隊(duì)成員將此文件導(dǎo)入自己電腦誓竿,然后從member center下載對(duì)應(yīng)的mobileprovision文件磅网,就可以進(jìn)行真機(jī)開發(fā)以及打包發(fā)布了。
值得一提的是在新發(fā)布的Xcode8中新增了支持自動(dòng)管理證書和自定義管理證書筷屡,自動(dòng)管理證書將會(huì)根據(jù)你所選擇的開發(fā)者賬號(hào)自動(dòng)為你處理配置文件和簽名證書的設(shè)置涧偷,并且只在必要時(shí)進(jìn)行提醒。這為開發(fā)者節(jié)省了不少時(shí)間和經(jīng)歷毙死。
上面提到證書(及其對(duì)應(yīng)的私鑰)和配置文件(mobileprovision)是簽名和打包的兩個(gè)必要文件燎潮,那么,mobileprovision在哪里呢扼倘?
App在簽名的過程中會(huì)在程序包中新建一個(gè)叫做_CodeSignatue/CodeResources
的文件确封,這個(gè)文件中存儲(chǔ)了被簽名的程序包中所有文件的簽名“π浚可以查看plist 格式文件隅肥,它不光包含了文件和簽名的列表,還包含了一系列規(guī)則袄简,這些規(guī)則決定了哪些資源文件應(yīng)當(dāng)被設(shè)置簽名腥放。在 CodeResources文件中會(huì)有4個(gè)不同區(qū)域,其中的rules和files是為老版本準(zhǔn)備的绿语,而 files2和rules2是為新的第二版的代碼簽名準(zhǔn)備的秃症。最主要的區(qū)別是在新版本中你無法再將某些資源文件排除在代碼簽名之外,在過去(OS X 10.9.5 之前)你是可以的吕粹,只要在被設(shè)置簽名的程序包中添加一個(gè)名為 ResourceRules.plist的文件种柑,這個(gè)文件會(huì)規(guī)定哪些資源文件在檢查代碼簽名是否完好時(shí)應(yīng)該被忽略。但是在新版本的代碼簽名中匹耕,這種做法不再有效聚请。在新版本的代碼簽名規(guī)定中,所有的代碼文件和資源文件都必須設(shè)置簽名稳其,不再可以有例外驶赏,一個(gè)程序包中的可執(zhí)行程序包,例如擴(kuò)展 (extension)既鞠,是一個(gè)獨(dú)立的需要設(shè)置簽名的個(gè)體煤傍,在檢查簽名是否完整時(shí)應(yīng)當(dāng)被單獨(dú)對(duì)待。
所以嘱蛋,比如微信這種多target的App蚯姆,在做重簽名的時(shí)候五续,不僅是主工程,還包括watch app及其他extension龄恋,都需要重新簽名才可以疙驾。
以開源中國為例,先從Appstore下載ipa文件篙挽,首先執(zhí)行$ unzip oschina.ipa
荆萤,解壓ipa包,進(jìn)入Payload文件夾內(nèi)铣卡,找到iosapp.app包,$ codesign -vv -d oschina.app
會(huì)列出一些有關(guān) Example.app
的簽名信息偏竟,通過security
命令產(chǎn)看keychain中已經(jīng)安裝的證書文件$ security find-identity -p codesigning
煮落,顯示結(jié)果如下:
Policy: Code Signing
Matching identities
1) C552814957BC5691121564774AC86E036B9E2AEE "iPhone Developer: abc (WGDSKET7K5)" (CSSMERR_TP_CERT_EXPIRED)
2) 630648B3BF32E6D349EDE08C4517CAFA9B12FD6B "iPhone Developer: def (UX59QF88DD)" (CSSMERR_TP_CERT_EXPIRED)
Valid identities only
1) 32F000F9C845C6626E8E18919E58C386065C5D16 "iPhone Distribution: abc Co.,Ltd."
2) 38A279804C22853C3F2575FD06BF26E225C08569 "iPhone Distribution: def Co., Ltd."
Matching identities下顯示所有已經(jīng)安裝的證書,Valid identities only代表當(dāng)前可用的證書踊谋。
真正的開始
由于Appstore的的應(yīng)用都是經(jīng)過DRM加密的蝉仇,如果想重簽名App,需要將從Appstore下載的ipa文件解密殖蚕,否則就算簽名成功轿衔,安裝成功,app還是會(huì)閃退睦疫。通過逆向Appstore應(yīng)用(一)中的方法害驹,可以拿到解密后開源中國iosapp.decrypted文件,替換Payload目錄下ios.app內(nèi)的名為iosapp的二進(jìn)制文件蛤育,此時(shí)就可以得到解密后的iosapp.app文件了宛官。在Payload目錄下執(zhí)行:
$ codesign -s "iPhone Distribution: abc" iosapp.app
提示:iosapp.app: is already signed,說明此app文件已經(jīng)被簽名過了瓦糕,需要加上-f參數(shù):
$ codesign -f -s "iPhone Distribution: abc" iosapp.app
顯示:iosapp.app: replacing existing signature底洗,簽名成功,執(zhí)行codesign -vv -d
查看簽名信息如下:
? Payload codesign -vv -d iosapp.app
Executable=/Users/pandora/Desktop/shell/oschina/Payload/iosapp.app/iosapp
Identifier=net.oschina.iosapp
Format=app bundle with Mach-O universal (armv7 arm64)
CodeDirectory v=20200 size=64082 flags=0x0(none) hashes=1997+3 location=embedded
Signature size=4758
Authority=iPhone Distribution: abc.
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=Sep 6, 2016, 18:08:09
Info.plist entries=36
TeamIdentifier=C789GLWV85
Sealed Resources version=2 rules=12 files=473
Internal requirements count=1 size=212
然后咕娄,壓縮已經(jīng)簽名的Payload文件夾為zip格式亥揖,然后修改zip為ipa格式即可得到解密后的ipa文件了。使用同步推安裝提示ApplicationVerificationFail圣勒,提示認(rèn)證失敗费变,這是因?yàn)榕渲梦募?mobileprovision)認(rèn)證失敗,決定了某一個(gè)應(yīng)用是否能夠在某一個(gè)特定的設(shè)備上運(yùn)行灾而,接下來需要給替換開源中國的mobileprovision文件胡控。找到證書"iPhone Distribution: abc"對(duì)應(yīng)的配置文件A.mobileprovision,修改名稱為:embedded.mobileprovision旁趟,copy至iosapp.app包內(nèi)昼激,重新壓縮轉(zhuǎn)換為ipa文件庇绽,安裝時(shí)依然提示:ApplicationVerificationFail。
這是因?yàn)槿鄙倭薳ntitlements文件橙困,它決定了哪些系統(tǒng)資源在什么情況下允許被一個(gè)應(yīng)用使用瞧掺。簡單的說它就是一個(gè)沙盒的配置列表,上面列出了哪些行為被允許凡傅,哪些會(huì)被拒絕辟狈,Xcode 會(huì)將這個(gè)文件作為 --entitlements
參數(shù)的內(nèi)容傳給codesign。在 Xcode 的 Capabilities 選項(xiàng)卡下選擇一些選項(xiàng)之后夏跷, Xcode 會(huì)自動(dòng)生成一個(gè) entitlements文件哼转,然后在需要的時(shí)候往里面添加條目。比如將應(yīng)用添加進(jìn)了一個(gè) App Group (比如說為了與extensions 共享數(shù)據(jù)槽华,com.apple.security.application-groups
)壹蔓, 或者開啟了推送功能 (aps-environment),如果有將它連接到調(diào)試器的需求猫态,這就需要將 get-task-allow
設(shè)為true等等佣蓉。
可以通過如下命令查看entitlements文件:
codesign -d --entitlements - /Users/pandora/Desktop/shell/oschina/開源中國\ 3.7.1/Payload/iosapp.app
得到的信息如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>keychain-access-groups</key>
<array>
<string>WSUBE85MHP.net.oschina.iosapp</string>
</array>
<key>com.apple.developer.team-identifier</key>
<string>WSUBE85MHP</string>
<key>application-identifier</key>
<string>WSUBE85MHP.net.oschina.iosapp</string>
</dict>
</plist>
所以,重新簽名一個(gè)app時(shí)亲雪,除了替換Certificate證書與mobileprovision配置文件外勇凭,還需要生成對(duì)應(yīng)的entitlements.plist文件,分別執(zhí)行以下兩個(gè)命令:
$ security cms -D -i "extracted/Payload/$APPLICATION/embedded.mobileprovision" > t_entitlements_full.plist
$ /usr/libexec/PlistBuddy -x -c 'Print:Entitlements' t_entitlements_full.plist > t_entitlements.plist
t_entitlements.plist內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>application-identifier</key>
<string>C789GLWV85.com.baidu.abc</string>
<key>aps-environment</key>
<string>production</string>
<key>com.apple.developer.associated-domains</key>
<string>*</string>
<key>com.apple.developer.team-identifier</key>
<string>C789GLWV85</string>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.baidu.abc</string>
</array>
<key>get-task-allow</key>
<false/>
<key>keychain-access-groups</key>
<array>
<string>C789GLWV85.*</string>
</array>
</dict>
</plist>
得到t_entitlements.plist后义辕,把它作為參數(shù)傳遞給codesign虾标,重新簽名app文件:
codesign -f -s "iPhone Distribution: abc" /Users/pandora/Desktop/shell/oschina/Payload/iosapp.app/ --entitlements t_entitlements.plist
最后,壓縮Payload文件夾终息,轉(zhuǎn)化為ipa格式的文件夺巩,終于安裝成功!本次重簽名未更改bundleID周崭,安裝的時(shí)候會(huì)覆蓋之前從Appstore下載的應(yīng)用柳譬。如果希望兩個(gè)或多個(gè)App共存的話,需要先替換App包中Info.plist的Bundle identifier的值:
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.baidu.abc" ./Payload/iosapp.app/Info.plist
然后再重新執(zhí)行:
codesign -f -s "iPhone Distribution: abc" /Users/pandora/Desktop/shell/oschina/Payload/iosapp.app/ --entitlements t_entitlements.plist
就得到了更改bundle id后的App包续镇,將其壓縮轉(zhuǎn)換為ipa文件美澳,使用同步堆安裝至手機(jī),就可以實(shí)現(xiàn)多個(gè)相同的App共存了摸航≈聘總結(jié),共需6步:
#1酱虎,解密二進(jìn)制文件
#2雨膨,替換embedded.mobileprovision
#3,修改Bundle ID
$ /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.baidu.bac" ./Payload/iosapp.app/Info.plist
#4读串,生成mobileprovision證書對(duì)應(yīng)的entitlements文件
$ security cms -D -i "./Payload/iosapp.app/embedded.mobileprovision" > t_entitlements_full.plist
$ /usr/libexec/PlistBuddy -x -c 'Print:Entitlements' t_entitlements_full.plist > t_entitlements.plist
#5聊记,將Certificate和entitlements作為參數(shù)撒妈,傳遞給codesign簽名
$ codesign -f -s "iPhone Distribution: abc" /Users/pandora/Desktop/shell/demo/oschina/Payload/iosapp.app/ --entitlements t_entitlements.plist
# 成功提示:replacing existing signature
#6,壓縮簽名后的Payload文件夾排监,轉(zhuǎn)換zip為ipa格式狰右,安裝即可。