iOS-批量自動(dòng)打包和部署

項(xiàng)目需求

批量打上百個(gè)應(yīng)用,項(xiàng)目的代碼沒(méi)有變動(dòng)京痢,應(yīng)用有不同的icon,啟動(dòng)圖篷店,bundleID祭椰,第三方賬號(hào),和其他一些業(yè)務(wù)相關(guān)的差異疲陕。

思路

  1. 自動(dòng)打包

  2. 重新簽名

    • 先自動(dòng)打包生成一個(gè)母包方淤,然后跑腳本對(duì)母包進(jìn)行重新簽名得到一個(gè)個(gè)子包。
    • bundleid替換
    • icon蹄殃、啟動(dòng)圖和第三方配置信息携茂。
  3. 分發(fā)部署

對(duì)稱加密和非對(duì)稱加密

image
  • 對(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ì)稱加密。


對(duì)稱加密

還有一種情況懒棉,比上面的想法更加出色一點(diǎn)草描。每個(gè)端都生成一個(gè)“私鑰-公鑰”對(duì),私鑰是自己保管策严,公鑰可以隨便分享穗慕,用公鑰可以解開(kāi)私鑰,用私鑰可以解開(kāi)公鑰妻导。例如服務(wù)端生成了一個(gè)“私鑰-公鑰”對(duì)逛绵,自己保留了一份私鑰怀各,把公鑰給客戶端,客戶端對(duì)發(fā)送的消息通過(guò)公鑰進(jìn)行加密术浪,服務(wù)端在收到這個(gè)公鑰后用自己的私鑰進(jìn)行解密還原得到明文瓢对。

非對(duì)稱加密
  • 常見(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中得位置如圖:

私鑰

  1. iOS系統(tǒng)原本就持有WWDR的公鑰汇恤,系統(tǒng)首先會(huì)對(duì)證書(shū)內(nèi)容通過(guò)指定的哈希算法計(jì)算得到一個(gè)信息摘要;
  2. 然后使用WWDR的公鑰對(duì)證書(shū)中包含的數(shù)字簽名解密拔恰,從而得到經(jīng)過(guò)WWDR的私鑰加密過(guò)的信息摘要因谎;
  3. 最后對(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)打包

image

應(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命令查看文件類型:

image.png

從上面看是支持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ī)器上的路徑和目錄如下截圖:

image

PackageExample同目錄的還有PackageShell漩氨,里面的buildipa.sh為編譯腳本西壮,由于最后的目標(biāo)是要做成可以隨意配置的,所以還有一個(gè)PackageConfig文件夾叫惊,里面有配置文件packageExample.mobileprovisionpackageExample.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ì)思路如下圖:


image

實(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)單解釋:

  1. 最外面對(duì)module文件夾for循環(huán)
  2. 讀取conf配置文件(這些配置文件均是自己配置,實(shí)際也可以是plist文件签餐,也比較方便)
  3. 把母包從外面拷貝進(jìn)來(lái)
  4. 對(duì)拷貝過(guò)來(lái)的包進(jìn)行解壓寓涨,移除CodeResources等,替換描述文件贱田,替換icon缅茉,修改BundleId及版本信息,修改授權(quán)文件(授權(quán)文件也是需要注意的點(diǎn)男摧,inhouse和develop也有一點(diǎn)點(diǎn)區(qū)別蔬墩,但整體沒(méi)變)
  5. 然后就是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)在也必須重新簽名了柴淘。
  6. 重新壓縮包,并移動(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ì):

  1. 節(jié)省勞動(dòng)
  2. 配置方便瑞妇,例子中主要修改icon稿静,其實(shí)還可以加一些配置文件來(lái)配置顏色啊字體啊文字啊等等
  3. 對(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)部推廣不好搀军,畢竟不是大廠也好像不是那么工程師文化,所以巴拉巴拉勇皇。

參考:

  1. SSL(https)中的對(duì)稱加密和非對(duì)稱加密
  2. RSA算法原理(一)
  3. Bypassing OpenSSL Certificate Pinning in iOS Apps
  4. Understanding provisioning profiles and certificates
  5. Code Signing explained
  6. Mach-O可執(zhí)行文件
  7. iOS開(kāi)發(fā)中的各種證書(shū)
  8. 代碼簽名探析
  9. 蘋(píng)果證書(shū)和公鑰私鑰加密
  10. iOS8以后CodeSign失效問(wèn)題
  11. iOS證書(shū)及ipa包重簽名探究
  12. ipa包部署網(wǎng)頁(yè)安裝
  13. iOS Code Signing: Under The Hood
  14. How iOS developers use code signing to get their apps on iPhones
  15. iOS Code Signing 學(xué)習(xí)筆記
  16. 蘋(píng)果開(kāi)發(fā)者賬號(hào)那些事兒(二)
  17. Inside Code Signing
  18. 公開(kāi)密鑰加密
  19. 數(shù)字簽名
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末罩句,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子敛摘,更是在濱河造成了極大的恐慌门烂,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兄淫,死亡現(xiàn)場(chǎng)離奇詭異屯远,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)捕虽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)氓润,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人薯鳍,你說(shuō)我怎么就攤上這事咖气“ご耄” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵崩溪,是天一觀的道長(zhǎng)浅役。 經(jīng)常有香客問(wèn)我,道長(zhǎng)伶唯,這世上最難降的妖魔是什么觉既? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮乳幸,結(jié)果婚禮上瞪讼,老公的妹妹穿的比我還像新娘。我一直安慰自己粹断,他們只是感情好符欠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著瓶埋,像睡著了一般希柿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上养筒,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天曾撤,我揣著相機(jī)與錄音,去河邊找鬼晕粪。 笑死挤悉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的巫湘。 我是一名探鬼主播尖啡,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼剩膘!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起盆顾,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤怠褐,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后您宪,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體奈懒,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年宪巨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了磷杏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捏卓,死狀恐怖极祸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤遥金,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布浴捆,位于F島的核電站,受9級(jí)特大地震影響稿械,放射性物質(zhì)發(fā)生泄漏选泻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一美莫、第九天 我趴在偏房一處隱蔽的房頂上張望页眯。 院中可真熱鬧,春花似錦厢呵、人聲如沸窝撵。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)忿族。三九已至,卻和暖如春蝌矛,著一層夾襖步出監(jiān)牢的瞬間道批,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工入撒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留隆豹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓茅逮,卻偏偏與公主長(zhǎng)得像璃赡,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子献雅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容