iOS程式碼簽署理解

來源:互聯網
上載者:User

標籤:

前言

做了幾年iOS app coder了,對於認證的產生、使用流程爛熟於心,然而對於這套機制的原理卻一直不甚理解。近來由於工作需要仔細研究了一下,特將自己的學習經驗記錄於此,以供大家學習指正。

問題

通常的iOS應用的簽名流程是這樣的(這裡只是大概寫一下,具體流程網上有很多):

  1. 訪問鑰匙串,使用“認證助理”的“從憑證授權單位請求認證”產生一個CertificateSigningRequest.certSigningRequest檔案。
  2. 開啟https://developer.apple.com,使用剛才產生的CSR檔案產生認證(certifcate)。
  3. 在網站的Identitifiers裡建立一個應用的ID。
  4. 在Devices裡添加你想要測試使用的裝置的UDID。
  5. 在Provisioning Profiles裡產生.mobileprovision檔案,需要勾選ID、UDIDs以及certificates。
  6. 將.cer檔案和.mobileprovision檔案下載到mac本地,雙擊,此時即可以在Xcode裡的Build Settings頁面進行選擇,進行簽名打包了。
  7. 此時可以通過鑰匙串將對應的p12檔案匯出,在其他mac電腦上雙擊,它們也可以用此認證打包了。

那麼問題來了,整個流程都被封裝起來了,我們往返於鑰匙串、開發人員網站、Xcode之間,並不知道當中具體發生了什麼。因而也就有了下面這些囉嗦。

概念

蘋果需要我們對自己的應用簽名,這樣當使用者從App Store裡面下載到我們的應用時,使用者就可以確定我們的應用來源於我們,並且從來沒有被別人更改過。數位簽章最重要的兩個概念一個是認證,另一個是公開金鑰加密(非對稱式加密)。蘋果的數位簽章機制使用的是X.509標準,依賴的密碼編譯演算法是RSA。

Certificate

  數位簽章的整個流程大概是這樣的:對於要加密的檔案,首先產生其對應的hash(一份檔案對應唯一的hash),然後使用私密金鑰對其加密,並記錄下來。使用者收到檔案後,使用公開金鑰解密得到hash,然後跟自己計算的檔案的hash作對比。如果一樣,則說明檔案沒有被修改過,且檔案來源於與此公開金鑰配對的私密金鑰的持有人。

  但是只靠這一操作仍然有邏輯上的漏洞,即沒人能證明使用者手中的公開金鑰是合法的。即A說B是合法的,但沒人能證明A是合法的;如果A不合法,那也就不能保證B也是合法的。也就是說公開金鑰證明了簽名的私密金鑰是和它配對的,但是並不能確定私密金鑰確實來自它所說的簽名者。

  因此,X.509協議引入了認證(certificate)來解決這一問題。認證就像我們的身份證一樣,可以唯一的標識我們的身份,並且包含著一些我們自身的相關資訊。認證是由權威機構(CA,certification authority)頒布的(就像身份證由公安機關頒布,只要公私密金鑰的持有人都信任它即可),並且具有有效期間,如所示(在鑰匙串中),CA是Apple Worldwide Developer Relations Certification Authority,到期日期是2016.11.16。

  另一方面,認證中包含著公開金鑰,終端可以用它來驗證資訊。這樣,整個鏈條就完整了:CA->證明認證(公開金鑰)的合法性->公開金鑰證明私密金鑰加密資訊的合法性,這樣使用者就可以確認資料來源於它所標識來自的人,並且沒被修改過。

RSA

除了認證,另一個重要的概念就是RSA。RSA演算法可用於加密(公開金鑰加私密金鑰解),也可用於數位簽章(私密金鑰加公開金鑰解)。這裡我們使用openssl來作為研究的工具。

首先我們產生一對盡量簡單的金鑰組,當然這很不安全:

openssl genrsa -out private.key 32openssl rsa -in private.key -pubout > pubkey.pubkey

-----BEGIN RSA PRIVATE KEY-----
MCsCAQACBQCtbWYxAgMBAAECBDpl33UCAwDepwIDAMdnAgI/dwICcT8CAjMj
-----END RSA PRIVATE KEY-----

-----BEGIN PUBLIC KEY-----
MCAwDQYJKoZIhvcNAQEBBQADDwAwDAIFAK1tZjECAwEAAQ==
-----END PUBLIC KEY-----

這是一對長度為32位的金鑰組,這個長度實際上是模數(modulus)的二進位長度,也就是說這個模數大於2的31次方小於2的32次方。我們可以看到內容是base64編碼的,且私密金鑰檔案內容要比公開金鑰長一些。通過命令可以看一下裡面具體包含著什麼:

openssl rsa -in private.key -text -noout輸出:Private-Key: (32 bit)modulus: 2909627953 (0xad6d6631)publicExponent: 65537 (0x10001)privateExponent: 979754869 (0x3a65df75)prime1: 56999 (0xdea7)prime2: 51047 (0xc767)exponent1: 16247 (0x3f77)exponent2: 28991 (0x713f)coefficient: 13091 (0x3323)openssl rsa -pubin -in pubkey.pubkey -text -noout輸出:Modulus (32 bit): 2909627953 (0xad6d6631)Exponent: 65537 (0x10001)

其中:

  1. modulus長度為32位,值為2909627953(10101101011011010110011000110001)
  2. modulus = prime1 * prime2
  3. publicExponent * privateExponent mod (prime1-1)*(prime2-1)=1
  4. (modulus, publicExponent) 公開金鑰
  5. (modulus, privateExponent) 私密金鑰

因而,如果其他人擷取了我們的私密金鑰檔案,他就擁有了我們加密的一切。蘋果開發人員們的私密金鑰一般儲存在鑰匙串中。RSA密碼編譯演算法與解密演算法一樣(加密實際上與簽名也是同一個演算法,只是公私密金鑰所擔當的角色不同),假設A為明文,B為密文,則:

A=B^privateExponent mod modulus;B=A^publicExponent mod modulus

而公私密金鑰可以互換使用,即:

A=B^publicExponent mod modulus;B=A^privateExponent mod modulus

關於如何使用openssl對檔案進行簽名、驗證,這裡不做過多描述,有興趣可以看一下參考資料裡的內容。

具體到iOS

那麼,上面的概念在蘋果的體系當中是如何工作的呢?大概是這樣的: 

  1. 首先,在鑰匙串助手中產生CSR(.certSigningRequest)檔案,此時你的電腦上已經產生了私密金鑰和公開金鑰。CSR檔案中包含著公開金鑰,以及一些其他的資訊。
  2. 登入蘋果開發人員網站,如果你的帳號有對應許可權,你的資訊就被確認合法了。然後將CSR上傳到蘋果開發人員網站(CA),它會用它自己的私密金鑰對檔案資訊進行加密,這會得到一個certificate檔案,這包含著你的公開金鑰以及其他的你的資訊。
  3. 這樣,開發人員就得到了一對認證過的金鑰組。然後在網站上產生provision profile,這個檔案的作用是跟隨app,使app只能在指定的iPhone上運行(例如Ad Hoc的只能在添加過UDID的手機上運行)。provision profile包含著應用的App ID以及誰能運行這個應用的資訊。其中還包含著應用的certificate(公開金鑰),這樣使用者的手機就能檢測App的內容是否合法了。
  4. 當我們打包時,我們需要選擇要使用的Code Signing Identity(在Build Settings中選擇),這對應著鑰匙串中的私密金鑰。產生.app時,代碼編譯成二進位檔案,而資源被拷貝到.app裡。然後系統使用指定的私密金鑰對每個檔案的hash進行加密,資源檔的結果儲存在.app下的_CodeSignature下的CodeResources裡,而二進位檔案寫入到了檔案裡。一同被包含進來的還有包含著公開金鑰的描述檔案embedded.mobileprovision。
  5. 當使用者安裝應用後,iOS會用自己的公開金鑰,也就是Apple Worldwide Developer Relations Certification Authority對embedded.mobileprovision中的認證進行驗證,驗證合法再使用認證中的公開金鑰對app目錄下的檔案進行驗證,如果也都合法則此app合法。

下面,將對每一步做一些詳細的分析,看整個流程都發生了一些什麼事。

Key

首先,我們要產生整個流程最核心的東西,即私密金鑰、公開金鑰;蘋果使用的是2048位的密鑰:

openssl genrsa -out private.key 2048

這樣我們就產生了私密金鑰,而公開金鑰可以從私密金鑰檔案中匯出(由上文可知私密金鑰檔案中實際上包含著公開金鑰的資訊):

openssl rsa -in private.key -pubout > pubkey.pubkey

開啟這兩個檔案,可以看到這兩個檔案分別被這樣的字串包圍著:

-----BEGIN RSA PRIVATE KEY——…-----END RSA PRIVATE KEY——-----BEGIN PUBLIC KEY——…-----END PUBLIC KEY——

中間的核心內容是base64的密鑰(以及其他資訊)。這樣我們就有了一對未被CA認可的金鑰組。

CSR

下一步我們需要產生給CA驗證的CSR檔案:

openssl req -new -key private.key -out ios-dev.csr

執行這個命令時會問你詢問一些你的資訊,這裡填的資訊並沒有什麼用,產生認證的時候蘋果會用我們的開發人員賬戶相關資訊填進去。而真正重要的是它攜帶著的公開金鑰,我們可以看一下:

openssl req -in ios-dev.csr -noout -pubkey

可以發現,公開金鑰資訊與之前產生的公開金鑰是一樣的。CSR包含著兩部分資訊,分別是subject資訊,也就是之前問的你的那些資訊,以及你建立的公開金鑰。

Certificate

下一步,我們需要登入developer.apple.com來產生認證。具體步驟這裡就不多說了,總之結果就是我們得到了認證(.cer)。我們要關心的是這一步發生了什麼呢?

這一步CA需要驗證你的資訊是否值得信任,如果值得信任,則將你提供的CSR中的公開金鑰用它的私密金鑰進行簽名,並取一些其他的資訊,最終產生certificate,並返回給你。如何驗證你的資訊呢?之前你申請開發人員帳號時其實就已經驗證過了。你的個人資訊、銀行卡賬戶資訊等等,只要符合蘋果的規定,它就認為你是合法的。所以,這一步蘋果只是將你的公開金鑰用它的私密金鑰加密了而已。

我們可以將公開金鑰從certificate中匯出(假設蘋果返回的是ios_distribution.cer):

openssl x509 -inform der -in ios_distribution.cer -pubkey -noout > cer_pub.pubkey 

對比可以發現cer_pub.pubkey和我們之前產生的pubkey.pubkey是相同的。

然後,我們可以看一下裡面具體有什麼:

openssl x509 -inform der -in ios_distribution.cer -text -noout

可以看到,這個認證的CA是Apple Worldwide Developer Relations Certification Authority,以及subject資訊等等。

現在,我們需要將認證匯入到鑰匙串中:

openssl x509 -inform der -in ios_distribution.cer -out ios_distribution.pemopenssl pkcs12 -export -in ios_distribution.pem -inkey private.key -out test.p12 -name "Test Sign”

輸入一個密碼。此時,會在目前的目錄下產生一個test.p12檔案。雙擊即可匯入鑰匙串(沒密碼將會匯入失敗)。或者通過下面的命令匯入:

security import test.p12 -k ~/Library/Keychains/login.keychain

開啟鑰匙串,選擇“登入”、“認證”,將會找到我們剛產生的認證: 

可以看到,認證和私密金鑰都有,因而這個認證是可以用來簽名的。

那麼在iOS端是如何驗證這個認證是否合法,裡面的公開金鑰可用的呢?由於蘋果引入了鑰匙串,所以openssl應該是不好完成這件事的(而且根據蘋果的說法,由於openssl版本之間的API一直在變動,所以它從來沒有作為iOS的一部分提供過)。蘋果在Mac系統裡提供了一個命令列工具security,它可以用來處理鑰匙串相關的操作。我們可以驗證一下我們剛才產生的certificate:

security verify-cert -c ios_distribution.pem輸出:...certificate verification successful.

我們可以建立一個自簽名的認證:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout selfsign.key -out selfsign.crt

然後驗證一下:

security verify-cert -c selfsign.crt輸出:Cert Verify Result: CSSMERR_TP_NOT_TRUSTED

可以看到,這個認證是不被信任的。因而我們可以想見iOS中大概也是用類似的方式來確認embedded.mobileprovision中的認證的可信性的。

Sign

萬事俱備,只欠東風。當我們編寫完代碼之後,我們就需要對代碼進行簽名了。蘋果提供了一個工具codesign大大簡化了工作的流程(當然也把具體的流程封裝了起來)。只要知道了code sign identity(也就是認證的CN;認證需要儲存著鑰匙串中)就可以對app進行簽名了:

codesign -s -f ‘{code sign identity}’ test.app

然後,我們可以看一下app的簽名資訊:

codesign -vv -d test.app

輸出:

Executable=/Users/hy/Library/Developer/Xcode/DerivedData/test-hekagtjqikppkueajznajyogtpmc/Build/Products/Debug-iphoneos/test.app/test

Identifier=com.xxx.test

Format=app bundle with Mach-O universal (armv7 arm64)

CodeDirectory v=20200 size=618 flags=0x0(none) hashes=14+3 location=embedded

Signature size=4690

Authority=iPhone Distribution: xxx

Authority=Apple Worldwide Developer Relations Certification Authority

Authority=Apple Root CA

Signed Time=2016年8月8日 下午4:19:19

Info.plist entries=27

TeamIdentifier=VWEN6QTM5A

Sealed Resources version=2 rules=12 files=7

Internal requirements count=1 size=168

可以看到,app是由iPhone Distribution: xxx簽名的,它是由Apple Worldwide Developer Relations Certification Authority簽名的,而這個認證又是由蘋果的根憑證Apple Root CA簽名的,因此整個鏈條是完整的。

具體的密碼編譯演算法、hash演算法都是經過蘋果封裝的,因而我們也無法知道簽名是怎麼計算出來的。只能大略的知道資源檔的簽名儲存在_CodeSignature/CodeResources裡;二進位檔案的簽名則直接寫入了該檔案裡面。感興趣的話可以自己用不同認證簽下名對比一下。

Provision Profile

 下一步,我們需要將應用安裝到手機上,這時就輪到Provision Profile(.mobileprovision)出場了。每個iOS的應用都要攜帶一個對應的描述檔案,它在app根目錄下名為embeded.mobileprovision。iOS需要通過這個描述檔案來確定應用是否有許可權運行。在Mac系統中,它儲存在~/Library/MobileDevices/Provisioning Profiles目錄下。在打包應用時,Xcode只是簡單的把對應的認證從這個目錄下拷貝到app目錄下。下面讓我們看一下它具體包含哪些東西。

使用命令來查看它的內容:

security cms -D -i test.mobileprovision

此時會輸出一個XML,我們能夠看到這個檔案裡包含著這些我們感興趣的內容:

  • DeveloperCertificates 包含著之前產生的認證
  • application-identifier 應用ID
  • ProvisionedDevices 此認證可以安裝的所有裝置的UDID,僅出現在develop描述檔案裡

其實從網站的產生流程中我們也能看出它包含著什麼。因為我們首先要建立一個App ID;然後如果是測試認證的話還需要錄入測試裝置的UDID;然後我們需要選擇它包含哪個認證。

下面我們來驗證一下裡面是否包含著之前建立的認證:

security cms -D -i test.mobileprovision | perl -nle ‘print $& if m{(?<=<data>).*?(?=</data>)}‘ | base64 -D > mobile.cer

上面這句命令就是先解析描述檔案,其結果作為下面一句正則的輸入;正則擷取<data></data>之間的內容,也就是DeveloperCertificates所包含的認證(假設這裡只有一個);取出的內容使用base64解碼存入mobile.cer裡。

比較mobile.cer和之前產生的ios_distribution.cer,發現兩個檔案是一樣的。因而也就可以知道描述檔案裡確實包含著用於驗證簽名的公開金鑰。

總結

以上就是我對蘋果的程式碼簽署機制的一些理解。文中忽略了一些內容,有的是因為時間問題,有的是因為與本文主題無關,有的是因為自己的疏忽。對於文中的不足以及紕漏,請不吝指教,先行謝過。

參考資料

RSA演算法

http://www.matrix67.com/blog/archives/5100

http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html

http://doctrina.org/How-RSA-Works-With-Examples.html

https://www.cs.cornell.edu/courses/cs5430/2015sp/notes/rsa_sign_vs_dec.php

認證

https://technet.microsoft.com/en-us/library/bb123848(v=exchg.65).aspx

https://www.sslshopper.com/what-is-a-csr-certificate-signing-request.html

http://www.shellhacks.com/en/HowTo-Decode-CSR

OpenSSL

http://www.cnblogs.com/interdrp/p/4881116.html

http://ngs.ac.uk/ukca/certificates/advanced

iOS程式碼簽署

https://www.objc.io/issues/17-security/inside-code-signing/

http://www.stencyl.com/help/view/ios-certificates-guide

 

iOS程式碼簽署理解

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.