1. 程式人生 > >App釋出系列--蘋果開發證書執行機制(一) Code Signing

App釋出系列--蘋果開發證書執行機制(一) Code Signing

最近看了objc.io上第17期中的文章 《Inside Code Signing》 對應的中文翻譯版 《程式碼簽名探析》 ,受益頗深,對iOS程式碼簽名機制有了進一步的認識。想了解詳細內容建議大家還是去看原文好了。

下面是對此文章的理解再結合自己之前對該部分的認識寫出的學習筆記。本文的前提是已經對非對稱加密有了一定的瞭解。

一、數字簽名(digital signature)

對指定資訊使用雜湊演算法,得到一個固定長度的資訊摘要,然後再使用 私鑰 (注意必須是私鑰)對該摘要加密,就得到了數字簽名。所謂的程式碼簽名就是這個意思。

二、數字證書(digital certificate)

證書生成

開發者在申請iOS開發證書時,需要通過keychain生成一個CSR檔案(Certificate Signing Request),提交給蘋果的 Apple Worldwide Developer Relations Certification Authority(WWDR)證書認證中心進行簽名,最後從蘋果官網下載並安裝使用。這個過程中還會產生一個私鑰,證書和私鑰在keychain中得位置如圖:

iphone-developer-keychain.png

證書組成

經過WWDR數字簽名後的數字證書長這個樣子:

20130603170838968.png

其中包含兩大部分:

· 證書本身

包含使用者的公鑰、使用者個人資訊、證書頒發機構資訊、證書有效期等資訊。

· 證書籤名

WWDR將上述證書本身內容的使用雜湊演算法得到一個固定長度的資訊摘要,然後使用自己的私鑰對該資訊摘要加密生成數字簽名,整個過程如圖所示:

20130603170752859.png

證書使用

iOS系統原本就持有WWDR的公鑰,系統首先會對證書內容通過指定的雜湊演算法計算得到一個資訊摘要;然後使用WWDR的公鑰對證書中包含的數字簽名解密,從而得到經過WWDR的私鑰加密過的資訊摘要;最後對比兩個資訊摘要,如果內容相同就說明該證書可信。整個過程如圖所示:

20130603170924312.png

在驗證了證書是可信的以後,iOS系統就可以獲取到證書中包含的開發者的公鑰,並使用該公鑰來判斷程式碼簽名的可用性了。

證書存在的意義

通過證書使用過程可以看出,證書本身只是一箇中間媒介,iOS系統對證書並不關心,它其實只想要證書中包含的開發者的公鑰!!

但是開發者怎麼才能證明公鑰是自己的呢?iOS安全系統怎麼才能相信這個公鑰就是這個開發者的呢?

不管是哪一個開發者對iOS的安全系統說,這個公鑰就是我的,系統是都不相信的,即系統對開發者有著百分之百的不信任感。但是iOS安全系統對自家的WWDR是可信任的,蘋果將WWDR的公鑰內建在了iOS系統中。有了證書,iOS安全系統只需要通過WWDR的公鑰就可以獲取到任何一個開發者的可信任的公鑰了,這就是證書存在的意義!!

三、公鑰(public key)

公鑰被包含在數字證書裡,數字證書又被包含在描述檔案(Provisioning File)中,描述檔案在應用被安裝的時候會被拷貝到iOS裝置中。

iOS安全系統通過證書就能夠確定開發者身份,就能夠通過從證書中獲取到的公鑰來驗證開發者用該公鑰對應的私鑰簽名後的程式碼、資原始檔等有沒有被更改破壞,最終確定應用能否合法的在iOS裝置上合法執行。

四、私鑰(private key)

每個證書(其實是公鑰)都對應有一個私鑰,

私鑰會被用來對程式碼、資原始檔等簽名。只有開發證書和描述檔案是沒辦法正常除錯的,因為沒有私鑰根本無法簽名。

此後的內容基本都是從《程式碼簽名探析》摘抄過來的筆記,建議大家看原文好了。

五、簽名相關命令

快捷檢視系統中能用來對程式碼進行簽名的證書

可以使用如下命令:

1 2 3 $security find-identity -v -p codesigning   1) F10B42FFDE18DF28BA21190121439F2E04BEE4B8 "iPhone Developer: weizheng li (P7QJ74LFSA)" 1 valid identities found

這就說明當前有一個同時有公鑰和私鑰的可用證書。

對未簽名app手動簽名

使用如下命令:

1 $ codesign -s 'iPhone Developer: Thomas Kollbach (7TPNXN7G6K)' Example.app

對已簽名app重新簽名

為了重新設定簽名,你必須帶上 -f 引數,有了這個引數,codesign 會用你選擇的簽名替換掉已經存在的那一個:

1 $ codesign -f -s 'iPhone Developer: Thomas Kollbach (7TPNXN7G6K)' Example.app

檢視指定app的簽名信息

codesign 還可以為你提供有關一個可執行檔案簽名狀態的資訊,這些資訊在出現不明錯誤時會提供巨大的幫助:

1 $ codesign -vv -d Example.app

會列出以下有關 Example.app 的簽名信息:

1 2 3 4 5 6 7 8 9 10 11 12 13 Executable=/Users/toto/Library/Developer/Xcode/DerivedData/Example-cfsbhbvmswdivqhekxfykvkpngkg/Build/Products/Debug-iphoneos/Example.app/Example   Identifier=ch.kollba.example   Format=bundle with Mach-O thin (arm64)   CodeDirectory v=20200 size=26663 flags=0x0(none) hashes=1324+5 location=embedded   Signature size=4336   Authority=iPhone Developer: Thomas Kollbach (7TPNXN7G6K)   Authority=Apple Worldwide Developer Relations Certification Authority   Authority=Apple Root CA   Signed Time=29.09.2014 22:29:07   Info.plist entries=33   TeamIdentifier=DZM8538E3E   Sealed Resources version=2 rules=4 files=120   Internal requirements count=1 size=184

驗證簽名檔案的完整性

檢查已簽名的檔案是否完整可以使用如下命令:

1 $ codesign --verify Example.app

就像大多數 UNIX 工具一樣,沒有任何輸出代表簽名是完好的。如果修改一下這個二進位制檔案:

1 2 3 $ echo 'lol' >> Example.app/Example $ codesign --verify Example.app Example.app: main executable failed strict validation

和預料中的一樣,修改已經簽名的應用會導致數字簽名驗證不通過。

六、資原始檔簽名

iOS 和 OS X 的應用和框架則是包含了它們所需要的資源在其中的。這些資源包括圖片和不同的語言檔案,資源中也包括很重要的應用組成部分例如 XIB/NIB 檔案,存檔檔案(archives),甚至是證書檔案。所以為一個程式包設定簽名時,這個包中的所有資原始檔也都會被設定簽名。

為了達到為所有檔案設定簽名的目的,簽名的過程中會在程式包(即Example.app)中新建一個叫做 _CodeSignatue/CodeResources 的檔案,這個檔案中儲存了被簽名的程式包中所有檔案的簽名。你可以自己去檢視這個簽名列表檔案,它僅僅是一個 plist 格式檔案。

這個列表檔案中不光包含了檔案和它們的簽名的列表,還包含了一系列規則,這些規則決定了哪些資原始檔應當被設定簽名。伴隨 OS X 10.10 DP 5 和 10.9.5 版本的釋出,蘋果改變了程式碼簽名的格式,也改變了有關資源的規則。如果你使用10.9.5或者更高版本的 codesign 工具,在 CodeResources 檔案中會有4個不同區域,其中的 rules 和 files 是為老版本準備的,而 files2 和 rules2 是為新的第二版的程式碼簽名準備的。最主要的區別是在新版本中你無法再將某些資原始檔排除在程式碼簽名之外,在過去你是可以的,只要在被設定簽名的程式包中新增一個名為 ResourceRules.plist 的檔案,這個檔案會規定哪些資原始檔在檢查程式碼簽名是否完好時應該被忽略。但是在新版本的程式碼簽名中,這種做法不再有效。所有的程式碼檔案和資原始檔都必須設定簽名,不再可以有例外。在新版本的程式碼簽名規定中,一個程式包中的可執行程式包,例如擴充套件 (extension),是一個獨立的需要設定簽名的個體,在檢查簽名是否完整時應當被單獨對待。

七、授權檔案(entitlements)

在 iOS 上你的應用能做什麼依然是沙盒限制的,這些限制大多情況下都由授權檔案(entitlements)來決定。授權機制決定了哪些系統資源在什麼情況下允許被一個應用使用,簡單的說它就是一個沙盒的配置列表。

執行如下命令:

1 $ codesign -d --entitlements - Example.app

會得到類似的結果:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <!--?xml version="1.0" encoding="UTF-8"?-->   <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> </true></dict>   </plist version="1.0">

在 Xcode 的 Capabilities 選項卡下選擇一些選項之後,Xcode 就會生成這樣一段 XML。 Xcode 會自動生成一個 .entitlements 檔案,然後在需要的時候往裡面新增條目。當構建整個應用時,這個檔案也會提交給 codesign 作為應用所需要擁有哪些授權的參考。這些授權資訊必須都在開發者中心的 App ID 中啟用,並且包含在後文介紹的描述檔案中。在構建應用時需要使用的授權檔案可以在 Xcode build setting 中的 code signing entitlements中設定。

在新版本的 Xcode 6 之後,授權資訊列表會以 Example.app.xcent 這樣的名字的檔案形式包含在應用包中。這麼做或許是為了在出現配置錯誤時提供更加有用的錯誤資訊。

八、描述檔案(provisioning file)

在整個程式碼簽名和沙盒機制中有一個組成部分將簽名,授權和沙盒聯絡了起來,那就是描述檔案 (provisioning profiles)。

OS X中儲存目錄

Xcode 將從開發者中心下載的全部配置檔案都放在了這裡:

1 ~/Library/MobileDevice/Provisioning Profiles

檔案格式

描述檔案並不是一個普通的plist檔案,它是一個根據密碼訊息語法 (Cryptographic Message Syntax) 加密的檔案。

以XML格式檢視該檔案的命令:

1 $ security cms -D -i example.mobileprovision

檔案內容

描述檔案主要包含以下內容:

· UUID

每一個配置檔案都有它自己的 UUID 。Xcode 會用這個 UUID 來作為標識,記錄你在 build settings 中選擇了哪一個配置檔案。

· ProvisionedDevices

記錄所有可用於除錯的裝置ID。

· DeveloperCertificates

包含了可以為使用這個配置檔案的應用簽名的所有證書。所有的證書都是基於 Base64 編碼符合 PEM (Privacy Enhanced Mail, RFC 1848) 格式的。

· Entitlements