細(xì)談證書與Provisioning Profile

iOS程序員大多對(duì)證書和Provisioning Profile懵逼過(guò)吧,是時(shí)候整理一下思路了,把這個(gè)問(wèn)題講講清楚。所有配置都在https://developer.apple.com,大家都可以上去摸索一下。

證書

打開鑰匙串訪問(wèn)可以看到里面有證書我的證書兩項(xiàng),其中證書包含系統(tǒng)安裝的所有證書,我的證書則僅包含電腦上有私鑰的證書。

screenshot.png

證書的私鑰是用來(lái)簽名的,通過(guò)簽名可以確保程序是沒(méi)有被篡改的。其中私鑰放在自己電腦上,公鑰則放在蘋果服務(wù)器上。

模擬器運(yùn)行App是不需要簽名的,真機(jī)調(diào)試和上傳AppStore的包都需要簽名,主程序、動(dòng)態(tài)庫(kù)、資源都要簽名。

//模擬器也調(diào)用了codesign,但是沒(méi)有選擇證書。
CodeSign /Users/henshao/Library/Developer/Xcode/DerivedData/CloudConsoleApp-gszptqenpqtwraajgpawanvtjtny/Build/Products/Debug-iphonesimulator/CloudConsoleApp.app
    cd /Users/henshao/cloudconsole-iOS
    export CODESIGN_ALLOCATE=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    
Signing Identity:     "-"

    /usr/bin/codesign --force --sign - --timestamp=none /Users/henshao/Library/Developer/Xcode/DerivedData/CloudConsoleApp-gszptqenpqtwraajgpawanvtjtny/Build/Products/Debug-iphonesimulator/CloudConsoleApp.app

//真機(jī)codesign選擇了證書
CodeSign /Users/henshao/Library/Developer/Xcode/DerivedData/CloudConsoleApp-gszptqenpqtwraajgpawanvtjtny/Build/Products/Debug-iphoneos/CloudConsoleApp.app
    cd /Users/henshao/cloudconsole-iOS
    export CODESIGN_ALLOCATE=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    
Signing Identity:     "iPhone Developer: 智杰 付 (WTLVCA9D68)"
Provisioning Profile: "iOS Team Provisioning Profile: a.b.c.d"
                      (e6dc1bc1-140c-4003-b91b-817535a46ae4)

    /usr/bin/codesign --force --sign AB9AB462D723988FB08AF1265BE1B574DD273DCB --entitlements /Users/henshao/Library/Developer/Xcode/DerivedData/CloudConsoleApp-gszptqenpqtwraajgpawanvtjtny/Build/Intermediates/CloudConsoleApp.build/Debug-iphoneos/CloudConsoleApp.build/CloudConsoleApp.app.xcent --timestamp=none /Users/henshao/Library/Developer/Xcode/DerivedData/CloudConsoleApp-gszptqenpqtwraajgpawanvtjtny/Build/Products/Debug-iphoneos/CloudConsoleApp.app

解壓開ipa可以發(fā)現(xiàn),里面有一個(gè)_CodeSignature/CodeResources文件,這是一個(gè)XML文件,打開可以看到里面的內(nèi)容。

<key>Frameworks/AFNetworking.framework/AFNetworking</key>
<dict>
    <key>hash</key>
    <data>
    gF72IVSBtCx2WdGfkervPKcCmZw=
    </data>
    <key>hash2</key>
    <data>
    kySS3fap6mMdMztbVQ0BPcQqGHwDKowHON1fHzQ6P4k=
    </data>
</dict>
<key>Frameworks/AFNetworking.framework/Info.plist</key>
<dict>
    <key>hash</key>
    <data>
    6AnKOHTiBpi15MSyr4X4yhapLxc=
    </data>
    <key>hash2</key>
    <data>
    D5xPZO6XisdE1hSvSAioGQv9RHryi33o8j6GwaNOOT4=
    </data>
</dict>
<key>Frameworks/AFNetworking.framework/_CodeSignature/CodeResources</key>
<dict>
    <key>hash</key>
    <data>
    +eORwOY/EikcBmsFZfD7QEDo3Qw=
    </data>
    <key>hash2</key>
    <data>
    bqs1RQcVX2sq7r0OewcocgvkivH0PSm6hgcqh/99iDI=
    </data>
</dict>

有趣的是動(dòng)態(tài)庫(kù)也會(huì)擁有自己有的_CodeSignature/CodeResources,但是里面的內(nèi)容非常簡(jiǎn)單,只有Info.plist的,簽名結(jié)果跟外面的是一樣的。AppStore里面所有動(dòng)態(tài)庫(kù)都要簽名,加載的時(shí)候要檢查簽名,所以不可能實(shí)現(xiàn)下發(fā)動(dòng)態(tài)庫(kù)做升級(jí)或者h(yuǎn)otpatch。但是企業(yè)證書可以這樣做。

//AFNetworking.framework/_CodeSignature
<dict>
    <key>Info.plist</key>
    <data>
    6AnKOHTiBpi15MSyr4X4yhapLxc=
    </data>
</dict>

任何有效的證書都可以用來(lái)簽名。所以這樣不牢靠對(duì)不對(duì)?當(dāng)年塞班時(shí)候,改簽真的很容易,輕松安裝各種程序。蘋果要想的更加周到一些,比如下面要講的Provisioning Profile。當(dāng)然不遵守規(guī)則重簽也是可以做到的,只是稍微增加了一些難度。

Provisioning Profile

解壓開ipa文件,可以在Payload目錄發(fā)現(xiàn)有一個(gè)embedded.mobileprovision文件。

[~/Downloads/CloudConsoleApp-Release/Payload]$ ll CloudConsoleApp.app/embedded.mobileprovision 
-rw-r--r-- 1 henshao staff 7.8K 10 21 03:01 CloudConsoleApp.app/embedded.mobileprovision

Provisioning Profile是一個(gè)很復(fù)雜的東西,包含了很多東西,可以在https://developer.apple.com里面試著生成一個(gè)Provisioning Profile,看看每個(gè)步驟都需要填些什么。Provisioning Profile包含了證書(公鑰)、App ID、entitlements、device list等關(guān)乎App能否正常啟動(dòng)的所有信息。證書是給ipa簽名的,在打包的時(shí)候起作用;而Provisioning Profile要打進(jìn)ipa里面,在啟動(dòng)的時(shí)候起作用

//使用這個(gè)命令可以解開文件看看里面的內(nèi)容
security cms -D -i embedded.mobileprovision
screenshot.png
screenshot.png
screenshot.png

Provisioning Profile是被蘋果簽名的,里面的內(nèi)容不能被修改。如果要對(duì)App做重簽,需要構(gòu)造一份新的Provisioning Profile。找一些iOS重簽名的文章:iOS重簽名探索,可以發(fā)現(xiàn),重簽名之前,要把自己的mobileprovision拷貝到ipa指定目錄里面。

Team

Xcode為了簡(jiǎn)化配置,設(shè)計(jì)了一個(gè)team的概念。只要在https://developer.apple.com里面添加了Apple ID,然后該用戶在Xcode里面登錄一下。遇到簽名的問(wèn)題,直接fix issue就可以了。這個(gè)過(guò)程中把證書和Provisioning Profile相關(guān)的配置都做好了。

一個(gè)用戶可能加入多個(gè)team,如何區(qū)分這個(gè)用戶在不同team里面創(chuàng)建的App ID呢?為了解決這問(wèn)題,蘋果設(shè)計(jì)了Team IDTeam ID加上Bundle ID構(gòu)成完整的App IDApp ID跟App的各種service有很大的關(guān)系,從https://developer.apple.com也可以看出來(lái),service的配置都是在App ID里面的。

screenshot.png

帶通配符的App ID可以表示一類App,Provisioning Profile使用這種App ID可用于team里面所有的App。Xcode fix issue喜歡創(chuàng)建這種類型的Provisioning Profile。

Paste_Image.png

推送證書

推送服務(wù)器跟蘋果APNs發(fā)消息,是需要認(rèn)證的。蘋果提供下面兩種認(rèn)證的方式。

//目前還沒(méi)有聽說(shuō)誰(shuí)在用這種方式
Token-based connection trust A provider using the HTTP/2-based API can use JSON web tokens (JWT) to validate the provider’s connection with APNs. In this scheme, the provider does not require a certificate-plus-private key to establish connection. Instead, you provision a public key to be retained by Apple, and a private key which you retain and protect. Your providers then use your private key to generate and sign JWT authentication tokens. Each of your push requests must include an authentication token.

//一般都用這種方式
Certificate-based connection trust A provider can, alternatively, employ a unique provider certificate and private cryptographic key. The provider certificate, provisioned by Apple when you establish your push service in your online developer account, identifies the topics supported by the provider. Each topic is the bundle ID associated with one of your apps.
screenshot.png
screenshot.png

推送平臺(tái)需要一個(gè)包含證書和私鑰的p12文件,而只有當(dāng)初生成推送證書的人才能正常導(dǎo)出p12文件。為了避免緊急情況下,找不到當(dāng)事人,所以我們?cè)贏pp代碼根目錄下存放了一份p12文件,并且標(biāo)明了過(guò)期時(shí)間,可確保萬(wàn)無(wú)一失。

$ tree CertificateExpireAt20170324 
CertificateExpireAt20170324
├── AliyunMobileApp-APNs-prod.p12
└── push.passwd

更多詳細(xì)信息可以參看:Local and Remote Notifications Overview

推送測(cè)試

命令行

使用Houston給iOS APP推送信息,拿到證書和token就可以在命令行下面做一些自動(dòng)化的推送工作。

圖形化工具

Easy APNs Provider這款工具很不錯(cuò),在Mac App Store可以下載到。證書直接用cer就好了。Houston需要把證書轉(zhuǎn)換成pem格式才行。

Snip20161130_3.png

打包平臺(tái)

大公司都會(huì)開發(fā)很多個(gè)App,因?yàn)樘O果設(shè)備數(shù)量的限制,必須注冊(cè)很多開發(fā)者賬號(hào),才能保證各個(gè)團(tuán)隊(duì)都能做真機(jī)測(cè)試。但是并不是每個(gè)團(tuán)隊(duì)都可以申請(qǐng)企業(yè)證書,這樣就沒(méi)法發(fā)布企業(yè)內(nèi)測(cè)包了。

為了解決這個(gè)問(wèn)題,公司會(huì)搭建一個(gè)打包的平臺(tái),用一個(gè)企業(yè)證書給所有的App簽名,然后分發(fā)給PD、視覺、交互、測(cè)試掃描做測(cè)試和驗(yàn)收。這里的問(wèn)題是一旦a.b.c.d這個(gè)Bundle ID被團(tuán)隊(duì)自己注冊(cè)了,打包平臺(tái)就不能用這個(gè)Bundle ID了,需要注冊(cè)一個(gè)a.b.c.d.e這樣的Bundle ID,還得去手動(dòng)生成對(duì)應(yīng)的Provisioning Profile,所以說(shuō)做一個(gè)通用的打包平臺(tái)也是一件麻煩事。

好在現(xiàn)在有一個(gè)工具:fastlane,可以使用腳本來(lái)生成證書和Provisioning Profile。可以通過(guò)fastlane來(lái)提高打包平臺(tái)的自動(dòng)化程度。

AppStore的特殊性

App提交蘋果審核之后,蘋果會(huì)對(duì)App重簽名。通過(guò)codesign可以看到mtl打出來(lái)的包都是我們自己證書簽名的,而AppStore的包簽名的證書都是蘋果的。

//企業(yè)包
[~/Downloads/CloudConsoleApp-Release/Payload]$ codesign -vv -d CloudConsoleApp.app/
Executable=/Users/henshao/Downloads/CloudConsoleApp-Release/Payload/CloudConsoleApp.app/CloudConsoleApp
Identifier=com.aliyun.wstudio.amc.AliyunMobileApp
Format=app bundle with Mach-O universal (armv7 arm64)
CodeDirectory v=20200 size=193958 flags=0x0(none) hashes=6053+5 location=embedded
Signature size=4737
Authority=iPhone Distribution: Alibaba Cloud Computing Ltd. (QBMN2BBW3K)
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=2016年10月21日 上午3:02:00
Info.plist entries=40
TeamIdentifier=QBMN2BBW3K
Sealed Resources version=2 rules=13 files=1451
Internal requirements count=1 size=220

//AppStore包
[~/wechat]$ codesign -vv -d WeChat.app/
Executable=/Users/henshao/wechat/WeChat.app/WeChat
Identifier=com.tencent.xin
Format=app bundle with Mach-O universal (armv7 arm64)
CodeDirectory v=20200 size=191179 flags=0x0(none) hashes=9550+5 location=embedded
Signature size=3487
Authority=Apple iPhone OS Application Signing
Authority=Apple iPhone Certification Authority
Authority=Apple Root CA
Info.plist entries=43
TeamIdentifier=88L2Q4487U
Sealed Resources version=2 rules=13 files=3005
Internal requirements count=1 size=96

參考文章

  1. 代碼簽名探析
  2. 關(guān)于Certificate、Provisioning Profile、App ID的介紹及其之間的關(guān)系
  3. 蘋果開發(fā)者賬號(hào)那些事兒(三)
  4. 漫談iOS程序的證書和簽名機(jī)制
  5. 阮一峰的網(wǎng)絡(luò)日志 博客上也有一些關(guān)于HTTPS、SSL、證書的科普文值得一看。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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