項(xiàng)目需求
批量打上百個(gè)應(yīng)用,項(xiàng)目的代碼沒(méi)有變動(dòng)京痢,應(yīng)用有不同的icon,啟動(dòng)圖篷店,bundleID祭椰,第三方賬號(hào),和其他一些業(yè)務(wù)相關(guān)的差異疲陕。
思路
自動(dòng)打包
-
重新簽名
- 先自動(dòng)打包生成一個(gè)母包方淤,然后跑腳本對(duì)母包進(jìn)行重新簽名得到一個(gè)個(gè)子包。
- bundleid替換
- icon蹄殃、啟動(dòng)圖和第三方配置信息携茂。
分發(fā)部署
對(duì)稱加密和非對(duì)稱加密
- 對(duì)稱加密(symmetric cryptography):加密和解密用的是同一個(gè)秘鑰
- 非對(duì)稱加密(asymmetric cryptography):加密和解密是不同的鑰匙
客戶端(Client)和服務(wù)器(Server)進(jìn)行通訊,Client和Server約定好相同的一把秘鑰诅岩,Client發(fā)送的明文通過(guò)這把秘鑰進(jìn)行加密讳苦,Server在收到這段加密后的密文后通過(guò)事先約定好的那邊秘鑰進(jìn)行解密得到明文。理論上只要保證秘鑰不被泄露就可以保證安全按厘,但是實(shí)際上這種方式很不安全医吊,如果秘鑰被破解,又恰好服務(wù)器只用了這一個(gè)秘鑰(這可能是最糟糕的情況)逮京,那么服務(wù)器和其他的客戶端之間的通訊基本上就是完全暴露了卿堂。這個(gè)例子說(shuō)的是對(duì)稱加密。
還有一種情況懒棉,比上面的想法更加出色一點(diǎn)草描。每個(gè)端都生成一個(gè)“私鑰-公鑰”對(duì),私鑰是自己保管策严,公鑰可以隨便分享穗慕,用公鑰可以解開(kāi)私鑰,用私鑰可以解開(kāi)公鑰妻导。例如服務(wù)端生成了一個(gè)“私鑰-公鑰”對(duì)逛绵,自己保留了一份私鑰怀各,把公鑰給客戶端,客戶端對(duì)發(fā)送的消息通過(guò)公鑰進(jìn)行加密术浪,服務(wù)端在收到這個(gè)公鑰后用自己的私鑰進(jìn)行解密還原得到明文瓢对。
- 常見(jiàn)的對(duì)稱加密有:DES,AES等
- 常見(jiàn)的非對(duì)稱加密有:SSL,HTTPS,TLS,RSA。
秘鑰長(zhǎng)度越長(zhǎng)破解的難度越大胰苏。我在iOS持久化數(shù)據(jù)時(shí)用過(guò)AES硕蛹,其他的沒(méi)有用過(guò)。非對(duì)稱加密中RSA是很有名硕并,被應(yīng)用的很廣泛的數(shù)字證書(shū)法焰。
數(shù)字簽名和數(shù)字證書(shū)
看下面的一個(gè)場(chǎng)景:
A生成了一對(duì)“私鑰-公鑰”,然后把公鑰給了B倔毙,B用公鑰加密一段消息埃仪,發(fā)給A,A收到消息后用私鑰解密普监,接著A用私鑰加密一段文字給B贵试,B收到后用公鑰解密查看明文。如此反復(fù)凯正。
上面是一個(gè)非對(duì)稱加密聊天的具體場(chǎng)景毙玻,雖然經(jīng)典,但是還是有一些問(wèn)題廊散。
1.A在收到B的信息的時(shí)候不能保證B發(fā)的信息是最原始的桑滩,即傳輸?shù)膬?nèi)容有可能被篡改
2.黑客C獲取了B的公鑰,然后偽裝成B和A進(jìn)行通信
上面的情況都是可能出現(xiàn)的允睹,現(xiàn)在這樣运准,A為了保證數(shù)據(jù)不被改動(dòng),先對(duì)數(shù)據(jù)用hash函數(shù)生成一個(gè)摘要(digest),然后用私鑰對(duì)這個(gè)摘要進(jìn)行加密缭受,生成了“數(shù)字簽名”(signature)胁澳,B在收到這個(gè)消息時(shí)先用公鑰對(duì)摘要進(jìn)行解密得到摘要并且對(duì)數(shù)據(jù)內(nèi)容也進(jìn)行一次hash,比較這次的hash是否與之前解密得到的摘要一致,如果一致米者,說(shuō)明數(shù)據(jù)沒(méi)有被改動(dòng)韭畸。我們把對(duì)內(nèi)容數(shù)據(jù)進(jìn)行hash后再加密生成的一段內(nèi)容稱為“數(shù)字簽名”。
黑客C進(jìn)行偽裝交流的可能性也是有的蔓搞,為了解決這個(gè)辦法胰丁,A去找了一個(gè)權(quán)威的“證書(shū)中心”(certificate authority,簡(jiǎn)稱CA),為公鑰做驗(yàn)證喂分。CA用自己的私鑰對(duì)A的公鑰和一些相關(guān)信息一起加密锦庸,生成了“數(shù)字證書(shū)”(Digital Certificate)。這樣A在進(jìn)行消息傳遞時(shí)也附帶上數(shù)字證書(shū)蒲祈,B在收到消息時(shí)用CA的公鑰解開(kāi)數(shù)字證書(shū)拿到真實(shí)的公鑰甘萧,通過(guò)這樣的方式驗(yàn)證身份萝嘁。
阮一峰的數(shù)字簽名是什么很形象的說(shuō)明了其中的意思。
蘋(píng)果的證書(shū)
在Mac上的鑰匙串訪問(wèn)app是專門(mén)用來(lái)管理證書(shū)的幔嗦。iOS開(kāi)發(fā)者在申請(qǐng)iOS開(kāi)發(fā)證書(shū)時(shí)酿愧,需要通過(guò)keychain生成CSR文件(Certificate Signing Request),提交給蘋(píng)果的Apple Worldwide Developer Relations Certification Authority(WWDR)
證書(shū)認(rèn)證中心進(jìn)行簽名邀泉,最后從蘋(píng)果官網(wǎng)下載并安裝使用。這個(gè)過(guò)程中還會(huì)產(chǎn)生一個(gè)私鑰钝鸽,證書(shū)和私鑰在keychain中得位置如圖:
- iOS系統(tǒng)原本就持有WWDR的公鑰汇恤,系統(tǒng)首先會(huì)對(duì)證書(shū)內(nèi)容通過(guò)指定的哈希算法計(jì)算得到一個(gè)信息摘要;
- 然后使用WWDR的公鑰對(duì)證書(shū)中包含的數(shù)字簽名解密拔恰,從而得到經(jīng)過(guò)WWDR的私鑰加密過(guò)的信息摘要因谎;
- 最后對(duì)比兩個(gè)信息摘要,如果內(nèi)容相同就說(shuō)明該證書(shū)可信颜懊。
整個(gè)過(guò)程如圖所示:
在驗(yàn)證了證書(shū)是可信的以后财岔,iOS系統(tǒng)就可以獲取到證書(shū)中包含的開(kāi)發(fā)者的公鑰,并使用該公鑰來(lái)判斷代碼簽名的可用性了
在objc.io
上面有篇《Inside Code Signing》(中文翻譯篇:代碼簽名探析)上詳細(xì)的講述了一個(gè)已簽名應(yīng)用的組成和一些其他知識(shí)
自動(dòng)打包
應(yīng)用的構(gòu)建過(guò)程和組成
想象一下平時(shí)的打包過(guò)程河爹,在Xcode中選擇對(duì)應(yīng)的appid,bundleid,還要選擇正確的配置文件(provisioning profile)匠璧,然后點(diǎn)擊run
,我們看到Xcode上面有內(nèi)容在不斷的更新咸这,正如猜想的夷恍,更新的內(nèi)容實(shí)際就是app包編譯的過(guò)程。
這個(gè)過(guò)程大概經(jīng)過(guò)了:配置(編譯器確定當(dāng)前系統(tǒng)環(huán)境)-> 確定標(biāo)準(zhǔn)庫(kù)和頭文件的位置->確定依賴關(guān)系 ->頭文件預(yù)編譯(precompilation) -> 預(yù)處理(preprocessing) -> 編譯(compilation) -> 連接(Linking) -> 打包 媳维,大致步驟是這些酿雪,但其中還有一些過(guò)程是沒(méi)有講的。
對(duì)于iOS的包來(lái)講侄刽,在構(gòu)建完成之后還會(huì)自動(dòng)調(diào)用codesign
命令進(jìn)行簽名指黎,這個(gè)時(shí)候我們之前選擇的bundleid啊,配置文件啊等等就排上用場(chǎng)了州丹。經(jīng)過(guò)簽名后的應(yīng)用是個(gè)相對(duì)來(lái)講安全的應(yīng)用醋安,通過(guò)簽名確保了包的來(lái)源合法,也能確保包的內(nèi)容是否被修改過(guò)(理論知識(shí)上篇已講過(guò))当叭。最后的包的本質(zhì)實(shí)際上是一個(gè)Mach-O格式的二進(jìn)制可執(zhí)行文件(簽名的數(shù)據(jù)就在這個(gè)二進(jìn)制文件中)和一些資源文件茬故。
Mach-O可執(zhí)行文件
Mach是一種操作系統(tǒng)內(nèi)核。它的大致歷史是:Mach內(nèi)核被NeXT公司的NeXTSTEP操作系統(tǒng)使用蚁鳖,NeXT是喬布斯蘋(píng)果被趕出蘋(píng)果后創(chuàng)建的公司磺芭。1996年,喬布斯將NeXTSTEP帶回蘋(píng)果醉箕,成為了OS X的內(nèi)核基礎(chǔ)钾腺。在Mach上徙垫,一種可執(zhí)行的文件格是就是Mach-O(Mach Object file format)。iOS是從OS X演變而來(lái)放棒,所以同樣支持Mach-O格式的可執(zhí)行文件姻报。
ipa包實(shí)際上就是一個(gè)zip壓縮包斩个,解壓之后會(huì)有一個(gè)Payload
文件夾撑蚌,其中有個(gè)XXX.app
這樣的.app
文件履羞,它里面除了有個(gè)各種資源挂脑、圖片等太闺,還有個(gè)和包名相同的文件——這個(gè)就是二進(jìn)制可執(zhí)行文件愧旦〖炙洌可以用file
命令查看文件類型:
從上面看是支持arm7和arm64兩種處理器架構(gòu)的通用程序包足绅,里面的格式是Mach-O摩泪。將可執(zhí)行文件用Sublime打開(kāi)笆焰,二進(jìn)制開(kāi)始部分如下:
開(kāi)頭的4個(gè)字節(jié)是cafebabe,這被稱為“魔數(shù)”见坑,反映文件的類型嚷掠。查了下相關(guān)文章,OS X上還有如下幾個(gè)標(biāo)識(shí):
- cafebabe:就是跨處理器架構(gòu)的通用格式
- feedface和feedfacf則分別是某一處理器架構(gòu)下的Mach-O格式
- 腳本的就很常見(jiàn)了荞驴,比如#!/bin/bash開(kāi)頭的shell腳本不皆。
其他資源文件
解壓后的包中除了可執(zhí)行文件還有其他資源文件,圖片啊戴尸,plist啊等等粟焊。蘋(píng)果對(duì)安全確實(shí)重視,這些資源文件其實(shí)大多數(shù)也是需要被設(shè)置簽名的孙蒙,可以見(jiàn)到的是包中還有一個(gè)_CodeSignature
文件夾项棠,這個(gè)文件夾中的CodeResources
文件中存儲(chǔ)了被簽名的程序包中所有需要被簽名文件的簽名。更詳細(xì)的介紹參見(jiàn)《代碼簽名探析》,從這些細(xì)節(jié)不難看出蘋(píng)果對(duì)于安全的重視挎峦。
自動(dòng)打包
- 蘋(píng)果自帶的
xcodebuild
命令行工具 -
xctool
1.相比較xcodebuild輸出的log雜亂香追,xctool更有結(jié)構(gòu)
2.xctool有人性化的顏色輸出
3.facebook聲稱xctool更快,據(jù)說(shuō)能快2坦胶、3倍
4.完全用Ojbective-C實(shí)現(xiàn)
xctool
xctool是可以使用homebrew安裝的透典,或者下源碼然后運(yùn)行 xctool.sh
腳本,homebrew安裝命令如下:
brew install xctool
實(shí)戰(zhàn)
自動(dòng)打包是基礎(chǔ)顿苇,打包完之后可以根據(jù)母包重新簽名生成相似的包峭咒,生成的包可以自動(dòng)部署。只要攻克這三個(gè)點(diǎn)就能實(shí)現(xiàn)全自動(dòng)化
為了講的更清楚纪岁,新建了一個(gè)項(xiàng)目PackageExample
(Demo已上傳到這里)凑队,并且使用了CocoaPods(實(shí)驗(yàn)起見(jiàn)僅引用了AFNetworking),項(xiàng)目的證書(shū)是dev狀態(tài)的幔翰。PackageExample
項(xiàng)目在我機(jī)器上的路徑和目錄如下截圖:
和PackageExample
同目錄的還有PackageShell
漩氨,里面的buildipa.sh
為編譯腳本西壮,由于最后的目標(biāo)是要做成可以隨意配置的,所以還有一個(gè)PackageConfig
文件夾叫惊,里面有配置文件packageExample.mobileprovision
和packageExample.plist
款青,配置文件主要用來(lái)簽名,plist文件的內(nèi)容為可配置的霍狰,例如里面有app_Prefix抡草、app_Name、app_ID等信息蔗坯。Package
文件夾為打的包的存放的地方渠牲。
完整的編譯腳本如下:
#!/bin/sh
#從plist文件中讀取ipa包名和配置文件名
profile_Name=`/usr/libexec/PlistBuddy -c "print profile_Name" ./PackageConfig/packageExample.plist`
ipa_Name=`/usr/libexec/PlistBuddy -c "print app_Name" ./PackageConfig/packageExample.plist`
#進(jìn)入工程目錄
cd ../PackageExample
echo "go to packageExample workspace path"
#報(bào)名時(shí)根據(jù)時(shí)間戳命名的,所以這里有用到
buildTime=$(date +%Y%m%d%H%M)
profile="${profile_Name}"
echo $profile $ipa_Name
#一下方法主要是創(chuàng)建打包的路徑和最后導(dǎo)出的ipa的路徑
if [ ! -d "../PackageShell/Package" ]; then
mkdir ../PackageShell/Package
fi
if [ ! -d "../PackageShell/Package/ArchiveProduction" ]; then
mkdir ../PackageShell/Package/ArchiveProduction
fi
if [ ! -d "../PackageShell/Package/ArchiveProduction/QA" ]; then
mkdir ../PackageShell/Package/ArchiveProduction/QA
echo "Create ArchiveProduction path"
fi
if [ ! -d "../PackageShell/Package/ipa" ]; then
mkdir ../PackageShell/Package/ipa
fi
if [ ! -d "../PackageShell/Package/ipa/QA" ]; then
mkdir ../PackageShell/Package/ipa/QA
echo "Create ipa path"
fi
buildConfiguration="QA"
buildPath="../PackageShell/Package/ArchiveProduction/QA/${ipa_Name}_${buildTime}.xcarchive"
ipaName="../PackageShell/Package/ipa/QA/${ipa_Name}_${buildTime}.ipa"
#先進(jìn)行clean操作,clean的目的是進(jìn)行清理緩存
#項(xiàng)目是workspace步悠,所以這里必須要對(duì)應(yīng),如果是project則是project瘫镇,
xctool -workspace PackageExample.xcworkspace -scheme PackageExample -configuration ${buildConfiguration} clean
#打包的命令鼎兽,包的格式為xxx.xcarchive
xctool -workspace PackageExample.xcworkspace -scheme PackageExample -configuration ${buildConfiguration} archive -archivePath ${buildPath}
#導(dǎo)出ipa包的命令,
xcodebuild -exportArchive -exportFormat IPA -archivePath ${buildPath} -exportPath ${ipaName} -exportProvisioningProfile "$profile"
PlistBuddy
腳本的開(kāi)頭有PlistBuddy
命令铣除,它是Mac下一個(gè)用來(lái)讀寫(xiě)plist文件的工具谚咬,在/usr/libexec/下。
xxx.xcarchive
包目錄結(jié)構(gòu)如圖:
├── Info.plist
├── Products
├── SCMBlueprint
├── SwiftSupport
└── dSYMs
重新簽名和授權(quán)機(jī)制
快捷查看系統(tǒng)中能用來(lái)對(duì)代碼簽名的證書(shū)
$security find-identity -v -p codesigning
設(shè)置簽名
$ codesign -s 'iPhone Developer: Thomas Kollbach (7TPNXN7G6K)' Example.app
重新設(shè)置簽名尚粘,你必須帶上 -f 參數(shù)择卦,有了這個(gè)參數(shù),codesign 會(huì)用你選擇的簽名替換掉已經(jīng)存在的那一個(gè):
$ codesign -f -s 'iPhone Developer: Thomas Kollbach (7TPNXN7G6K)' Example.app 重新簽名
列出一些有關(guān) Example.app的簽名信息
$ codesign -vv -d Example.app 會(huì)
驗(yàn)證簽名是否完好郎嫁,若無(wú)任何輸出則說(shuō)明簽名完好
$ codesign --verify Example.app
授權(quán)文件(entitlements)
授權(quán)機(jī)制決定了哪些系統(tǒng)資源在什么情況下允許被一個(gè)應(yīng)用使用秉继,即沙盒的配置列表。授權(quán)機(jī)制也是按照 plist 文件格式來(lái)列出的泽铛,Xcode 會(huì)將這個(gè)文件作為 –entitlements 參數(shù)的內(nèi)容傳給 codesign 尚辑,這個(gè)文件內(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>7TPNXN7G6K.ch.kollba.example</string>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.team-identifier</key>
<string>7TPNXN7G6K</string>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>7TPNXN7G6K.ch.kollba.example</string>
</array>
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
<string>7TPNXN7G6K.ch.kollba.example</string>
<key>com.apple.security.application-groups</key>
<array>
<string>group.ch.kollba.example</string>
</array>
<key>get-task-allow</key>
<true/>
</dict>
</plist>
在 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)信息必須都在開(kāi)發(fā)者中心的 App ID 中啟用,并且包含在配置文件中舀透,稍后我們會(huì)詳細(xì)討論這一點(diǎn)栓票。在構(gòu)建應(yīng)用時(shí)需要使用的授權(quán)文件可以在 Xcode build setting 中的 code signing entitlements 中設(shè)置。
描述文件(provisioning file)
在整個(gè)代碼簽名和沙盒機(jī)制中有一個(gè)組成部分將簽名盐杂,授權(quán)和沙盒聯(lián)系了起來(lái)逗载,那就是描述文件 (provisioning profiles)哆窿。
OS X中保存目錄
Xcode 將從開(kāi)發(fā)者中心下載的全部配置文件都放在了這里:
~/Library/MobileDevice/Provisioning Profiles
文件格式
描述文件并不是一個(gè)普通的plist文件,它是一個(gè)根據(jù)密碼訊息語(yǔ)法 (Cryptographic Message Syntax) 加密的文件厉斟。
以XML格式查看該文件的命令:
$ security cms -D -i example.mobileprovision
文件內(nèi)容:
UUID
每一個(gè)配置文件都有它自己的 UUID 挚躯。Xcode 會(huì)用這個(gè) UUID 來(lái)作為標(biāo)識(shí),記錄你在 build settings 中選擇了哪一個(gè)配置文件擦秽。ProvisionedDevices
記錄所有可用于調(diào)試的設(shè)備ID码荔。DeveloperCertificates
包含了可以為使用這個(gè)配置文件的應(yīng)用簽名的所有證書(shū)。所有的證書(shū)都是基于 Base64 編碼符合 PEM (Privacy Enhanced Mail, RFC 1848) 格式的感挥。Entitlements
有關(guān)前面講到的配置文件的所有內(nèi)容都會(huì)被保存在這里缩搅。
重新簽名的實(shí)例
設(shè)計(jì)思路如下圖:
實(shí)例的Resign-ipa
文件夾目錄結(jié)構(gòu)如下圖:
templates
文件夾中存放的是和授權(quán)文件相關(guān)的配置文件,build
文件夾主要存放最后被重新簽名的包触幼,module
目錄下存放重簽名的配置文件硼瓣、資源文件和描述文件等。這里的思路是遍歷module
文件夾下的內(nèi)容置谦,然后根據(jù)內(nèi)容重新簽名母包堂鲤,將得到的包放到build
,例子中是兩個(gè)媒峡,實(shí)際如果版本非常多瘟栖,只需要向module
增加文件夾及配置內(nèi)容即可。
resign.sh
為重簽名的腳本谅阿,內(nèi)容也不算多:
#!/bin/bash
for file2 in `ls -a ./module`
do
if [ x"$file2" != x"." -a x"$file2" != x".." -a x"$file2" != x".DS_Store" ]; then
echo $file2
#Conf file
CONF=./module/$file2/resign.conf
echo $CONF
#Datetime
NOW=$(date +"%Y%m%d_%s")
#Load config
if [ -f ${CONF} ]; then
. ${CONF}
fi
#Temp
TEMP="temp"
if [ -e ${TEMP} ]; then
echo "ERROR: temp already exists"
exit 1
fi
#Check app ID
if [ -z ${APP_ID} ]; then
echo "ERROR: missing APP_ID"
exit 1
fi
echo ${APP_ID}
#Create build dir
if [[ ! -d ${BUILD_PATH} ]]; then
mkdir ${BUILD_PATH}
fi
#Copy mother package
if [[ ! -f "../Package/ipa/QA/packageExample.ipa" ]]; then
echo "mother package not exists"
exit 1
fi
cp ../Package/ipa/QA/packageExample.ipa ./module/$file2${ASSETS_PATH}/packageExample.ipa
#Unzip the mother ipa
echo "Unzip ipa"
unzip -q ./module/$file2${ASSETS_PATH}${IPA_NAME}.ipa -d ${TEMP}
#Remove old Codesignature
echo "Remove old CodeSignature"
rm -r "${TEMP}/Payload/${APP_NAME}.app/_CodeSignature" "${TEMP}/Payload/${APP_NAME}.app/CodeResources" 2> /dev/null | true
#Replace embedded mobil provisioning profile
echo "Replace embedded mobile provisioning profile"
cp "./module/$file2${ASSETS_PATH}${PROFILE_NAME}.mobileprovision" "${TEMP}/Payload/${APP_NAME}.app/embedded.mobileprovision"
#Change icon
echo "Change icon"
cp "./module/$file2${ASSETS_PATH}/icon_120.png" "${TEMP}/Payload/${APP_NAME}.app/AppIcon60x60@2x.png"
cp "./module/$file2${ASSETS_PATH}/icon_180.png" "${TEMP}/Payload/${APP_NAME}.app/AppIcon60x60@3x.png"
#Change Bundleversion
if [[ ! -z ${APP_BUNDLE_VERSION} ]]; then
/usr/libexec/PlistBuddy -c "Set CFBundleVersion ${APP_BUNDLE_VERSION}" ${TEMP}/Payload/${APP_NAME}.app/Info.plist
fi
#Change CFBundleShortVersionString
if [[ ! -z ${APP_BUNDLE_SHORT_VERSION_STRING} ]]; then
/usr/libexec/PlistBuddy -c "Set CFBundleShortVersionString ${APP_BUNDLE_SHORT_VERSION_STRING}" ${TEMP}/Payload/${APP_NAME}.app/Info.plist
fi
#Change Bundleidentifier
/usr/libexec/PlistBuddy -c "Set CFBundleIdentifier ${APP_ID}" ${TEMP}/Payload/${APP_NAME}.app/Info.plist
#Create entitlements from template
ENTITLEMENTS=$(<./templates/entitlements.template)
ENTITLEMENTS=${ENTITLEMENTS//#APP_ID#/$APP_ID}
ENTITLEMENTS=${ENTITLEMENTS//#APP_PREFIX#/$APP_PREFIX}
echo ${ENTITLEMENTS} > ${TEMP}/entitlements.temp
#Re-sign
#這里注意命令參數(shù)的不同
#/usr/bin/codesign -f -s "${CERTIFICATE_TYPE}: ${CERTIFICATE_NAME}" --identifier "${APP_ID}" --entitlements "${TEMP}/entitlements.temp" --resource-rules "${TEMP}/Payload/${APP_NAME}.app/ResourceRules.plist" "${TEMP}/Payload/${APP_NAME}.app"
/usr/bin/codesign -f -s "${CERTIFICATE_TYPE}: ${CERTIFICATE_NAME}" --identifier "${APP_ID}" --entitlements "${TEMP}/entitlements.temp" "${TEMP}/Payload/${APP_NAME}.app"
#Remove copyed mother package
echo "Remove mother package"
rm -rf ./module/$file2${ASSETS_PATH}packageExample.ipa
#Re-package
echo "Re-package"
cd ${TEMP}
zip -qr "${IPA_NAME}_resigned_${NOW}.ipa" Payload
mv ${IPA_NAME}_resigned_${NOW}.ipa ../${BUILD_PATH}/${IPA_NAME}_${file2}_${NOW}.ipa
#Remove temp
cd ../
rm -rf ${TEMP}
fi
done
exit 0
代碼中已經(jīng)做了注釋半哟,做簡(jiǎn)單解釋:
- 最外面對(duì)
module
文件夾for循環(huán) - 讀取
conf
配置文件(這些配置文件均是自己配置,實(shí)際也可以是plist文件签餐,也比較方便) - 把母包從外面拷貝進(jìn)來(lái)
- 對(duì)拷貝過(guò)來(lái)的包進(jìn)行解壓寓涨,移除
CodeResources
等,替換描述文件贱田,替換icon缅茉,修改BundleId及版本信息,修改授權(quán)文件(授權(quán)文件也是需要注意的點(diǎn)男摧,inhouse和develop也有一點(diǎn)點(diǎn)區(qū)別蔬墩,但整體沒(méi)變) - 然后就是
codesign
命令,代碼注釋也指出了--resource-rules
參數(shù)的問(wèn)題耗拓,我原本找到的是帶這個(gè)參數(shù)的拇颅,但是現(xiàn)在用不到。具體原因《代碼簽名探析》這里說(shuō)是:“伴隨 OS X 10.10 DP 5 和 10.9.5 版本的發(fā)布乔询,蘋(píng)果改變了代碼簽名的格式樟插,也改變了有關(guān)資源的規(guī)則。如果你使用10.9.5或者更高版本的 codesign 工具,在 CodeResources 文件中會(huì)有4個(gè)不同區(qū)域黄锤,其中的 rules 和 files 是為老版本準(zhǔn)備的搪缨,而 files2 和 rules2是為新的第二版的代碼簽名準(zhǔn)備的。最主要的區(qū)別是在新版本中你無(wú)法再將某些資源文件排除在代碼簽名之外鸵熟,在過(guò)去你是可以的副编,只要在被設(shè)置簽名的程序包中添加一個(gè)名為 ResourceRules.plist 的文件,這個(gè)文件會(huì)規(guī)定哪些資源文件在檢查代碼簽名是否完好時(shí)應(yīng)該被忽略流强。但是在新版本的代碼簽名中痹届,這種做法不再有效。所有的代碼文件和資源文件都必須設(shè)置簽名打月,不再可以有例外队腐。” 。簡(jiǎn)單的說(shuō)奏篙,資源文件現(xiàn)在也必須重新簽名了柴淘。 - 重新壓縮包,并移動(dòng)到
build
中秘通。
整個(gè)流程走完后我們?cè)?code>build中得到了重新簽名后的包悠就,并且可以通過(guò)iTunes按照到設(shè)備上且能夠正常打開(kāi),但是我們發(fā)現(xiàn)icon被換掉了充易,內(nèi)部的bundleId、版本號(hào)信息等也被換了荸型,于是乎盹靴,就這樣輕輕松松的“換皮了”。我的手機(jī)上最后的截圖如下:
優(yōu)勢(shì):
- 節(jié)省勞動(dòng)
- 配置方便瑞妇,例子中主要修改icon稿静,其實(shí)還可以加一些配置文件來(lái)配置顏色啊字體啊文字啊等等
- 對(duì)于邪惡點(diǎn)的公司,申請(qǐng)很多app賬號(hào)辕狰,然后用這個(gè)方法改备,快速換皮,app內(nèi)部的內(nèi)容接口根據(jù)bundleid等信息來(lái)配置蔓倍,里面放放廣告悬钳,對(duì)主版本進(jìn)行導(dǎo)流量等。
自動(dòng)部署
通過(guò)自動(dòng)部署偶翅,我們可以直接將包發(fā)到AppStore默勾、fir\蒲公英這樣的第三方平臺(tái)、以及自己的服務(wù)器上聚谁。這里重點(diǎn)推薦mattt大神的——SHENZHEN母剥。
SHENZHEN的安裝和使用
通過(guò)gem安裝
$ gem install shenzhen
具體的用法可以參見(jiàn)這里,畢竟在中國(guó),主要提下FIR和蒲公英已經(jīng)上傳AppStore的命令:
FIR
$ ipa distribute:fir -u USER_TOKEN -a APP_ID
蒲公英 (PGYER)
$ ipa distribute:pgyer -u USER_KEY -a APP_KEY
USER信息到各自注冊(cè)賬號(hào)查找
iTunes Connect Distribution
$ ipa distribute:itunesconnect -a me@email.com -p myitunesconnectpassword -i appleid --upload
我們?nèi)匀皇强梢酝ㄟ^(guò)讀取配置信息环疼,來(lái)寫(xiě)個(gè)腳本跑部署习霹,這部分就不再舉例了,如果你不小心看到這個(gè)系列我覺(jué)得你應(yīng)該會(huì)了炫隶,或者我們也可以相互商討(畢竟我的blog人讀的少 o(╯□╰)o)淋叶。實(shí)際上,我的同事已經(jīng)實(shí)現(xiàn)了等限。
論持續(xù)化集成
雖然iOS好像能使用Jenkins進(jìn)行持續(xù)化集成(好像我也用了一下Jenkins爸吮,貌似不是很好用,可能是我沒(méi)有堅(jiān)持用吧)望门,但是通過(guò)這個(gè)序列的文章形娇,其實(shí)我們自己就實(shí)現(xiàn)了一套持續(xù)化集成了。剛開(kāi)始用蒲公英那會(huì)兒我把ipa包傳給他們筹误,他們就能放到平臺(tái)給其他人測(cè)試桐早,我覺(jué)得好神奇啊,后來(lái)想想厨剪,無(wú)非就是重新簽名哄酝。持續(xù)化集成其實(shí)我的同事也實(shí)現(xiàn)了,我們用了一臺(tái)服務(wù)器祷膳,定時(shí)的拉取代碼陶衅,跑腳本,然后上傳到測(cè)試服務(wù)器供人下載使用直晨。只是公司內(nèi)部推廣不好搀军,畢竟不是大廠也好像不是那么工程師文化,所以巴拉巴拉勇皇。
參考:
- SSL(https)中的對(duì)稱加密和非對(duì)稱加密
- RSA算法原理(一)
- Bypassing OpenSSL Certificate Pinning in iOS Apps
- Understanding provisioning profiles and certificates
- Code Signing explained
- Mach-O可執(zhí)行文件
- iOS開(kāi)發(fā)中的各種證書(shū)
- 代碼簽名探析
- 蘋(píng)果證書(shū)和公鑰私鑰加密
- iOS8以后CodeSign失效問(wèn)題
- iOS證書(shū)及ipa包重簽名探究
- ipa包部署網(wǎng)頁(yè)安裝
- iOS Code Signing: Under The Hood
- How iOS developers use code signing to get their apps on iPhones
- iOS Code Signing 學(xué)習(xí)筆記
- 蘋(píng)果開(kāi)發(fā)者賬號(hào)那些事兒(二)
- Inside Code Signing
- 公開(kāi)密鑰加密
- 數(shù)字簽名