1. 代碼簽名
代碼簽名并不是每一個iOS開發(fā)人員的首要任務(wù)坪郭,但是對代碼簽名工作原理的深入了解對于解決問題以及在開發(fā)團(tuán)隊(duì)中樹立自己的形象非常有用。沒有什么比一個可以重新簽署過時的Swift 2.2 iOS應(yīng)用程序的開發(fā)人員更“值得一提”了格二,而不是在時間不允許的情況下修復(fù)潛在的數(shù)千個Swift編譯器錯誤。
下面會講解代碼簽名工作原理的基本概述竣蹦。在下面的鏈接找到開源iOS Wordpress v10.9應(yīng)用程序:
在發(fā)送到iTunes Connect Store
之前顶猜,我們會探索應(yīng)用程序代碼簽名的各個階段。此外痘括,我們會重新簽署Wordpress
應(yīng)用程序长窄,以便它可以在我們自己的iOS設(shè)備上運(yùn)行!
1.1 準(zhǔn)備工作
我們需要一個合適的iOS蘋果開發(fā)人員帳戶來生成配置文件纲菌;需要一個真機(jī)來安裝Wordpress iOS應(yīng)用程序挠日;還需要獲取并安裝mobdevim
命令,可在此處獲得:
這個小命令行工具可以做很多事情翰舌,包括在設(shè)備上安裝應(yīng)用程序嚣潜、查詢設(shè)備信息和連接到iOS設(shè)備時獲取控制臺日志。使用此工具可以更輕松地在設(shè)備上安裝iOS應(yīng)用程序椅贱,并在應(yīng)用程序簽名不正確時查找錯誤懂算。
按照mobdevsim
上的說明安裝該工具。安裝mobdevim
后夜涕,連上iOS設(shè)備并進(jìn)行測試運(yùn)行。
mobdevim -f
1.2 專業(yè)用語
要真正了解代碼簽名的工作原理属愤,我們需要了解三個關(guān)鍵組件:公鑰/私鑰女器、授權(quán)文件和配置文件。我們將從廣度優(yōu)先開始住诸,然后深入到深度優(yōu)先驾胆。
公鑰/私鑰用于簽署我們的應(yīng)用程序。這是我們的數(shù)字簽名贱呐,蘋果知道如何進(jìn)行驗(yàn)證丧诺。私鑰用于對應(yīng)用程序及其授權(quán)文件進(jìn)行密碼簽名。授權(quán)文件實(shí)際上只是嵌入在應(yīng)用程序中的一個XML
字符串奄薇,它表示應(yīng)用程序可以做什么驳阎,不能做什么。
授權(quán)文件馁蒂、已批準(zhǔn)設(shè)備的列表和用于驗(yàn)證代碼簽名的公鑰都捆綁在應(yīng)用程序的配置文件中呵晚。所有信息都是通過私鑰創(chuàng)建的簽名強(qiáng)制執(zhí)行的,并且可以通過公鑰進(jìn)行驗(yàn)證沫屡。
1.3 公鑰/私鑰
在學(xué)習(xí)代碼簽名過程時饵隙,這可能是最難理解的事情。因?yàn)楣€/私鑰引入了密碼學(xué)沮脖,不斷深入將是個無底洞金矛。
簡單地說芯急,有兩種不同類型的密碼學(xué):對稱加密和非對稱加密。
- 對稱加密驶俊,是一種只包含一個密鑰的密碼娶耍。如果A試圖向B發(fā)送一條秘密消息徘六,他們都必須知道共享的秘密才能對該消息進(jìn)行加密和解密胎撤。
- 在非對稱加密中顺少,有兩個密鑰:公鑰和私鑰与殃。A和B都有自己獨(dú)特的私鑰和獨(dú)特的公鑰普碎。這樣作箍,他們就可以在任何人不知道對方私鑰的情況下共享信息浩销。
我們在設(shè)置Apple開發(fā)人員帳戶時账磺,已經(jīng)完成了從證書頒發(fā)機(jī)構(gòu)請求證書的過程购披。我們創(chuàng)建了一對公鑰/私鑰杖挣,將公鑰發(fā)送到Apple服務(wù)器(通過.csr文件)。最終的結(jié)果是創(chuàng)建了一個由蘋果簽署的簽名刚陡,這就是蘋果如何唯一地識別你惩妇。這意味著蘋果公司和我們都使用非對稱加密技術(shù)來分發(fā)應(yīng)用程序。
我們可以使用以下終端命令查看用于為應(yīng)用程序簽名的公鑰/私鑰對的名稱或標(biāo)識:
security find-identity -p codesigning -v
此命令查詢macOS系統(tǒng)的keychain
筐乳,查找包含私鑰(-v
)且其類型可以進(jìn)行代碼簽名(-p
代碼簽名)的有效標(biāo)識歌殃。
此輸出將顯示有效的標(biāo)識,這些標(biāo)識可以生成代碼簽名的應(yīng)用程序蝙云。如果我們查找包含短語“iPhone Developer”的標(biāo)識氓皱,則該標(biāo)識可能用于在設(shè)備上簽名iOS應(yīng)用程序。
請注意勃刨,我們有一個公鑰或證書波材,以及下面的私鑰。證書可以重新創(chuàng)建身隐,但私鑰的價(jià)值超過黃金廷区。永遠(yuǎn)不要刪除私鑰!如果這樣做了贾铝,你就要拿出你是你的證據(jù)隙轻,就需要通過蘋果重新創(chuàng)造一個新的身份。
從這個意義上說垢揩,證書只是公鑰大脉。因此,如果要使用keychain
訪問來導(dǎo)出標(biāo)識水孩,并且希望將其格式化為.cer
格式镰矿,那么我們將只會導(dǎo)出公鑰。如果還要導(dǎo)出私鑰俘种,則必須使用PKCS12格式(.p12
)正確導(dǎo)出完整標(biāo)識秤标、私鑰和其他信息绝淡。
這一點(diǎn)很重要,因?yàn)槲覀冃枰朗欠褚獙?dǎo)出標(biāo)識苍姜,以便另一個開發(fā)人員可以生成具有匹配分發(fā)標(biāo)識的構(gòu)建牢酵。但要小心:無論誰擁有私鑰,都可以為那家公司承擔(dān)全部身份衙猪,至少從蘋果的角度來看是這樣馍乙!
可以使用以下命令導(dǎo)出公共證書:
security find-certificate -c ${name} -p
這將把名字叫${name}
的公共證書輸出到stdout
,并將其格式化為PEM
格式垫释。顯示證書有兩種方法:DER
和PEM
丝格。PEM
可以被終端讀取(因?yàn)樗?code>base64編碼的棵譬;DER
显蝌,用高度專業(yè)的編碼術(shù)語來說,會產(chǎn)生官話并使終端發(fā)出很多嗶嗶聲订咸。
重復(fù)上述命令并將輸出寫入/tmp/public_cert.cer
曼尊。
security find-certificate -c ${name} -p > /tmp/public_cert.cer
然后就可以在終端查看了。
cat /tmp/public_cert.cer
-----BEGIN CERTIFICATE-----
MIIFnDCCBISgAwIBAgIIFMKm2AG4HekwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
...
這就是我們怎么知道這個證書在PEM
中脏嚷。我們可以使用openssl
命令查詢公共的x509
證書:
openssl x509 -in /tmp/public_cert.cer -inform PEM -text -noout
-
x509
選項(xiàng)表示openssl
命令應(yīng)該使用x509
證書骆撇。 - 提供公共證書路徑的
-in
,解碼格式為PEM
(-inform PEM
)父叙。 - 希望證書采用可讀的文本格式
-text
選項(xiàng)神郊。 - 指定不希望輸出證書中帶有
-noout
參數(shù)。
有關(guān)此公共證書的信息將顯示在終端中高每。
記住這個openssl
命令屿岂。當(dāng)我們閱讀到將這些公共證書嵌入其中的配置文件時践宴,將重新接觸到x509
證書的概念鲸匿。
1.4 授權(quán)文件
嵌入在幾乎每個編譯的應(yīng)用程序中的是一組授權(quán)文件。這是嵌入在應(yīng)用程序中的XML
字符串阻肩,表示應(yīng)用程序可以做什么和不能做什么带欢。其他程序?qū)z查授權(quán)文件中的權(quán)限,并相應(yīng)地授予或拒絕請求烤惊。
其中許多權(quán)限檢查是由其他檢查程序權(quán)限的守護(hù)程序執(zhí)行的乔煞。例如,App Groups
柒室、iCloud Services
渡贾、Push Notifications
、Associated Domains
都將修改應(yīng)用程序的授權(quán)信息雄右。Xcode中顯示的這些功能只是蘋果平臺上的一小部分權(quán)利空骚,因?yàn)榇蠖鄶?shù)功能都是蘋果私有的纺讲,并通過代碼簽名來實(shí)現(xiàn)。
可能最重要的授權(quán)囤屹,是get-task-allow
授權(quán)熬甚,可以在使用開發(fā)人員證書編譯的所有軟件上找到。它允許相關(guān)程序附著到調(diào)試器上肋坚。
在macOS上乡括,可以通過為任何不具有get-task-allow: true
的應(yīng)用程序禁用SIP來解決缺少此權(quán)限的問題。在iOS上智厌,我們將嘗試調(diào)試不具有此權(quán)限的應(yīng)用程序诲泌,除非通過越獄禁用了代碼驗(yàn)證。
可以通過codesign
命令查看應(yīng)用程序的授權(quán)信息峦剔,比如Finder
:
codesign -d --entitlements :- /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder
-d
選項(xiàng)表示在命令后面立即顯示--entitlements
選項(xiàng)档礁。:-
有兩種作用:
-
-
打印到stdout
-
:
表示省略有信息和長度。
就像在Mach-O
中一樣吝沫,代碼簽名信息是用一個magic header
和長度存儲的呻澜。:
表示從輸出中刪除此頭信息,并且只顯示實(shí)際的XML
授權(quán)字符串惨险。
1.5 配置文件
設(shè)置配置文件把公共x509
證書羹幸、已批準(zhǔn)設(shè)備的列表以及授權(quán)文件嵌入到一個文件中。
配置文件的默認(rèn)位置可以在以下位置找到:
~/Library/MobileDevice/Provisioning Profiles/
// 通過ls命令查看文件夾下的所有證書
ls ~/Library/MobileDevice/Provisioning\ Profiles/
不幸的是辫愉,配置概要文件是由它們的UUID
命名的栅受,而不是由我們(或Xcode)為它們?nèi)〉拿Q命名的。
幸運(yùn)的是恭朗,我們可以再次使用security
命令來顯示原始信息屏镊。選擇任何一個.mobileprovision
文件并執(zhí)行security
命令,如下所示:
PP_FILE=$(ls ~/Library/MobileDevice/Provisioning\ Profiles/ *mobileprovision | head -1)
security cms -D -i "$PP_FILE"
第一個命令獲取一個配置文件并將其分配給PP_FILE
變量痰腮。PP_FILE
變量被傳遞到security
命令中而芥。該命令對設(shè)置配置文件的加密消息語法cms
格式進(jìn)行-D
解碼,并通過-i
選項(xiàng)指定輸入路徑膀值。
下面將討論某一個配置文件的輸出棍丐。輸出中有一些有意思的點(diǎn):
- AppIDName是綁定到此設(shè)置配置文件的ID的名稱,可在https://developer.apple.com/account/ios/identifier/bundle上找到沧踏。
- TeamIdentifier是Apple為團(tuán)隊(duì)標(biāo)識提供的唯一團(tuán)隊(duì)標(biāo)識歌逢。蘋果將為付款的每個賬戶生成一個特定的團(tuán)隊(duì)ID。例如翘狱,對于應(yīng)用商店版本秘案,會有一個唯一的團(tuán)隊(duì)ID;而對于企業(yè)版本,會有一個不同的團(tuán)隊(duì)ID阱高。
-
授權(quán)包含應(yīng)用程序使用此簽名可以做什么和不能做什么师骗。這通常是Xcode生成的配置文件出現(xiàn)問題的原因。因?yàn)閄code需要更新
App ID
配置(本質(zhì)上是授權(quán))讨惩,然后生成具有正確值的新配置文件辟癌。 - IsXcodeManaged是一個布爾值。表明Xcode是否管理這個配置文件荐捻。整個代碼簽名過程給開發(fā)者帶來了很多麻煩黍少,蘋果公司正試圖在IDE上做更多的工作,包括用我們的發(fā)行證書為應(yīng)用程序簽名处面。這是一把雙刃劍厂置,讓Xcode代為管理方便了我們,但是如果Xcode做了一些我們沒有預(yù)料到的事情魂角,那么潛在的錯誤可能更難追蹤昵济。
- Name是在https://developer.Apple.com/account/ios/profile/limited上顯示的用于標(biāo)識配置文件的名稱。
- ProvisionedDevices授權(quán)設(shè)備的列表野揪。
-
DeveloperCertificates是包含
base64
編碼的x509
證書的數(shù)組访忿。這將包含先前通過security find certificate
命令提取的相同公共證書。在對應(yīng)用程序進(jìn)行代碼簽名時斯稳,這些證書也被編碼到實(shí)際的可執(zhí)行文件中海铆。
1.6 探索WordPress app
與iOS設(shè)備上的典型調(diào)試工作流一樣,在將應(yīng)用程序發(fā)送到Apple iTunes Connect
之前挣惰,必須編譯具有配置文件的應(yīng)用程序卧斟。每個上架前的App都有一個名為embedded.mobileprovision的配置文件。正是這個配置文件告訴iOS應(yīng)用程序是有效的憎茂,并且來自我們珍语。
打開Pre App Store
目錄中的WordPress.app
。為WORDPRESS.app
的完整路徑分配一個終端變量WORDPRESS
竖幔,如下所示:
WORDPRESS="/full/path/to/WordPress.app/"
配置文件
在WordPress
應(yīng)用程序中找到embedded.mobileprovision配置文件板乙,并對其使用security命令。
security cms -D -i "$WORDPRESS/embedded.mobileprovision"
在這個特定的配置文件中赏枚,可以看到以下內(nèi)容:
- 蘋果公司分配給Automattic, Inc.公司的團(tuán)隊(duì)標(biāo)識是
3TMU3BH3NK
亡驰。 - Wordpress應(yīng)用程序使用iCloud服務(wù):在授權(quán)字典中有com.apple.developer.icloud鍵晓猛。它還希望使用某些擴(kuò)展饿幅,如App Groups。
- get-task-allow為false戒职,意味著我們無法附著調(diào)試器栗恩。因?yàn)閼?yīng)用程序是使用分發(fā)簽名標(biāo)識簽名的。
從DeveloperCertificates密鑰復(fù)制base64
編碼的數(shù)據(jù)洪燥。通過終端磕秤,將此值分配給名為CERT_DATA的變量:
CERT_DATA=MIIFozCCBIu...
變量CERT_DATA現(xiàn)在包含用于簽署應(yīng)用程序的base64
編碼x509
證書∪槲冢現(xiàn)在,解碼這個base64數(shù)據(jù)
并將其導(dǎo)出到/tmp/wordpress_cert.cer
市咆。之后執(zhí)行openssl命令進(jìn)行查看汉操。
echo "$CERT_DATA" | base64 -D > /tmp/wordpress_cert.cerecho "$CERT_DATA" | base64 -D > /tmp/wordpress_cert.cer
openssl x509 -in /tmp/wordpress_cert.cer -inform DER -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 786948871528664923 (0xaebcdd447dc4f5b)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, O=Apple Inc., OU=Apple Worldwide Developer Relations, CN=Apple Worldwide Developer Relations Certification Authority
Validity
Not Before: Jan 17 13:26:41 2018 GMT
Not After : Jan 17 13:26:41 2019 GMT
Subject: UID=PZYM8XX95Q, CN=iPhone Distribution: Automattic, Inc. (PZYM8XX95Q), OU=PZYM8XX95Q, O=Automattic, Inc., C=US
...
這意味著,在Automattic, Inc的工作人員的keychain
上有一個名為“iPhone Distribution: Automattic, Inc. (PZYM8XX95Q)”的標(biāo)識蒙兰,用于簽署此應(yīng)用程序磷瘤。
嵌入的可執(zhí)行文件
如果應(yīng)用程序包含擴(kuò)展(即share
擴(kuò)展、today
小部件或其他的)搜变,則在./Plugins目錄中會找到更多簽名的打包包采缚,其中包含它們自己的應(yīng)用程序標(biāo)識符和embedded.mobileprovision配置文件。
這些功能為應(yīng)用程序提供了應(yīng)用程序之外的附加功能挠他。
在深入./Plugins
目錄中的容器時扳抽,可以使用相同的安全命令來驗(yàn)證這一點(diǎn),分別查看每個embedded.mobileprovision文件殖侵。
_CodeSignature文件夾
包含在真正的iOS應(yīng)用程序包(不在模擬器中)中的是一個名為_CodeSignature的文件夾贸呢,其中包含一個名為CodeResources的文件。這是一個XML plist文件拢军,它是該目錄中找到的每個不可執(zhí)行文件的校驗(yàn)和贮尉。例如:
cat "_CodeSignature/CodeResources" | head -10
<?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>files</key>
<dict>
<key>AboutViewController.nib</key>
<data>
rSZAWMReahogETtlwDpstztW6Ug=
</data>
可以看到AboutViewController.nib文件的校驗(yàn)和
rSZAWMReahogETtlwDpstztW6Ug=。
這個值可以通過openssl
自己計(jì)算:
openssl sha1 -binary "AboutViewController.nib" | base64
rSZAWMReahogETtlwDpstztW6Ug=
蘋果已經(jīng)開始用Xcode 10為iOS應(yīng)用程序從SHA-1
校驗(yàn)和向SHA-256
過渡朴沿,為兩種算法生成校驗(yàn)和猜谚。
這個CodeResources
文件本身有一個對文件執(zhí)行的校驗(yàn)和,它被嵌入到了實(shí)際的WordPress
應(yīng)用程序中赌渣!這意味著魏铅,如果用戶要修改任何文件,甚至在.app
目錄中添加一個目錄而不重新簽名WordPress
應(yīng)用程序坚芜,iOS應(yīng)用程序都無法在用戶手機(jī)上進(jìn)行安裝览芳。
1.7 對WordPress app重新簽名
我們可以使用Apple簽名重新簽名應(yīng)用程序,將WordPress應(yīng)用程序安裝到iOS設(shè)備上鸿竖。
從高層的角度來看沧竟,您需要執(zhí)行以下操作:
- 將有效的配置文件復(fù)制到應(yīng)用程序
WordPress .app
目錄中的embedded.mobileprovision。 - 將
Info.plist
鍵CFBundleIdentifier
更改為新的配置文件中新的應(yīng)用程序標(biāo)識符缚忧。 - 使用適當(dāng)?shù)氖跈?quán)信息(也包括在配置文件中)通過嵌入的配置文件中包含的標(biāo)識為
WordPress
應(yīng)用程序重新簽名悟泵。
如果你有一個有效的、未過期的闪水、包含你的iOSUDID
的配置文件糕非。連接上iOS設(shè)備,通過執(zhí)行mobdevim -f
命令來獲取設(shè)備的UDID
。
復(fù)制配置文件
無論是創(chuàng)建一個新的配置文件或使用現(xiàn)有的配置文件朽肥,我們將使用它來重新簽名WordPress
應(yīng)用程序禁筏。用它覆蓋WordPress.app
文件夾下面的embedded.mobileprovision
文件。
PP_PATH=~/Downloads/Code_Signing_Example_ProvisProfile_92618.mobileprovision
cp "$PP_PATH" "$WORDPRESS/embedded.mobileprovision"
刪除插件
WordPress
應(yīng)用程序在主應(yīng)用程序中嵌入了幾個./Plugins
目錄中的擴(kuò)展應(yīng)用程序衡招。每個配置文件都包含一個具有唯一應(yīng)用程序標(biāo)識符的唯一配置文件篱昔。我們可以為每個擴(kuò)展本身提供一個唯一的配置文件進(jìn)行簽名。但這個太麻煩了始腾,不是重點(diǎn)旱爆。我們不使用這些擴(kuò)展,刪除WordPress
應(yīng)用程序的整個插件目錄窘茁。
修改Info.plist
希望你記住了配置文件的應(yīng)用程序ID的名稱怀伦,現(xiàn)在需要把它設(shè)置到Info.plist
的鍵CFBundleIdentifier
中。如果不記得山林,可以從配置文件中查詢它房待。以下是獲取這些信息的方法:
security cms -D -i "$PP_PATH" | grep application-identifier -A1
<key>application-identifier</key>
<string>H4U46V6494.com.selander.code-signing</string>
下面替換這個CFBundleIdentifier
鍵中的值。
plutil -replace CFBundleIdentifier -string H4U46V6494.com.selander.code-signing "$WORDPRESS/Info.plist"
我們還可以更改它的顯示名稱:
plutil -replace CFBundleDisplayName -string "Woot" "$WORDPRESS/Info.plist"
提取授權(quán)信息
下一個任務(wù)是使用配置文件中的有效授權(quán)重新簽名應(yīng)用程序驼抹。
由于授權(quán)在配置文件中嵌入為字典而不是XML桑孩,因此可能更容易先從主可執(zhí)行文件中提取權(quán)限,然后用配置文件中找到的新權(quán)限修補(bǔ)該文件框冀。提取授權(quán)信息導(dǎo)出到/tmp/ent.xml
:
codesign -d --entitlements :/tmp/ent.xml "$WORDPRESS/WordPress"
可以通過cat
進(jìn)行查看:
cat /tmp/ent.xml
如果授權(quán)有效流椒,則可以從當(dāng)前配置文件中提取授權(quán)并將其放入這個新文件中。
首先明也,將配置文件XML寫入名為/tmp/scratch
的文件:
security cms -D -i "$PP_PATH" > /tmp/scratch
使用xpath
命令只將權(quán)利信息提取到剪貼板宣虾。
xpath /tmp/scratch '//*[text() = "Entitlements"]/following-sibling::dict' | pbcopy
現(xiàn)在剪貼板中有有效的授權(quán)信息。打開/tmp/ent.xml
温数,刪除包含的<dict>
之間的內(nèi)容并替換為剪貼板的內(nèi)容绣硝。最終的/tmp/ent.xml
文件應(yīng)該如下所示:
<?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>keychain-access-groups</key>
<array>
<string>H4U46V6494.*</string>
</array>
<key>get-task-allow</key>
<true />
<key>application-identifier</key>
<string>H4U46V6494.com.selander.code-signing</string>
<key>com.apple.developer.team-identifier</key>
<string>H4U46V6494</string>
</dict>
</plist>
重新簽名WordPress
我們已經(jīng)執(zhí)行了所有設(shè)置。我們有一個有效的簽名標(biāo)識撑刺;在embedded.mobileprovision
的WordPress
應(yīng)用程序中嵌入了一個有效的配置文件鹉胖;刪除了插件目錄;并且擁有在/tmp/ent.xml
中找到的新配置文件的授權(quán)够傍。
在WordPress
應(yīng)用程序Frameworks目錄中使用codesign
命令和簽名標(biāo)識:
codesign -f -s "iPhone Developer: Derek Selander (8AW8QLCX5U)" "$WORDPRESS"/Frameworks/*
然后對文件夾進(jìn)行簽名:
codesign --entitlements /tmp/ent.xml -f -s "iPhone Developer: Derek Selander (8AW8QLCX5U)" "$WORDPRESS"
看看能否安裝WordPress
應(yīng)用程序甫菠。在終端中,鍵入:
mobdevim -i "$WORDPRESS"
如果我們使用開發(fā)人員配置文件簽署了應(yīng)用程序冕屯。那么我們將擁有get-task-allow
權(quán)限寂诱,這意味著我們可以調(diào)試這個WordPress
應(yīng)用程序。