关于开发证书配置(Certificates&Identifiers&Provisioning Profiles),相信做iOS开发的同学没少被折腾。对于一个iOS开发小白、半吊子抑或老兵,或多或少会有以下不详、疑问、疑惑甚至困惑:
本文将对相关概念做个系统的梳理串烧。
首先,假设你使用过Apple设备(iMac/iPad/iPhone)且注册过Apple ID(Apple Account)。
其次,你必须加入苹果开发者计划(Enroll in iOS Developer Program to become a member),注册一个开发者账号。
只有拥有开发者账号,才可以申请开发/发布证书及相关配置授权文件,进而在iOS真机上开发调试Apps或发布到App Store。
开发者账号分为Individual和Company/Organization两种类型。如无特别交代,下文基于$99/Year的普通个人开发者(Individual)账号展开。
一.App ID(bundle identifier)
二 .设备( Device)App ID用于标识一个或者一组App,App ID应该和Xcode中的Bundle Identifier是一致的或匹配的。
App ID字符串通常以反域名(reverse-domain-name)格式的Company Identifier(Company ID)作为前缀(Prefix/Seed)。
App ID全名会被追加ApplicationIdentifierPrefix(一般为TeamID.),分为两类:
用户可在网站上删除(Delete)已注册的App IDs。
- Explicit App ID:唯一的App ID,这种App ID用于唯一标识一个应用程序。例如“com.apple.garageband”这个App ID,用于标识Bundle Identifier为“com.apple.garageband”的程序。
- Wildcard App ID:通配符App ID,用于标识一组应用程序。例如“*”(实际上是ApplicationIdentifierPrefix)表示所有应用程序;而“com.apple.*”可以表示以“com.apple.”开头的所有应用程序。
App ID被配置到【XcodeTarget|Info|Bundle Identifier】下;对于Wildcard App ID,只要bundle identifier包含其作为Prefix/Seed即可。
三 .证书( Certificates)Device就是运行iOS系统用于开发调试App的设备,每台设备使用UDID来唯一标识。iOS设备连接Mac后,可通过iTunes->Summary或者Xcode->Window->Devices获取iPhone的UDID(identifier)。
Apple Member Center网站个人账号下的Devices中包含了注册过的所有可用于开发和测试的设备。普通个人开发账号每年累计最多只能注册100个设备,用户可在网站上启用/禁用(Enable/Disable)已注册的Device。
本文的Devices即连接到Xcode被授权用于开发测试的iOS设备(iPhone/iPad)。
- Apps signed by you or your team run only on designated development devices.
- Apps run only on the test devices you specify.
四 .供应配置文件( Provisioning Profiles )顾名思义,证书是用来证明内容(App的executalbe code)的合法性和完整性的。对于想安装到真机或发布到AppStore的应用程序(App),只有经过签名验证(Signature Validated)才能确保来源可信,并且保证App内容是完整、未经篡改的。
证书分为两类:Development和Production(Distribution)。
- Development证书用来开发和调试应用程序:A development certificate identifies you, as a team member, in a development provisioning profile that allows apps signed by you to launch on devices.
- Production主要用来分发应用程序(根据证书种类有不同作用):A distribution certificate identifies your team or organization in a distribution provisioning profile and allows you to submit your app to the store. Only a team agent or an admin can create a distribution certificate.
普通个人开发账号最多可注册iOS Development/Distribution证书各2个,用户可在网站上删除(Revoke)已注册的Certificate。下文主要针对开发调试阶段的Development证书。
首先,iOS以及Mac OS X系统(在安装Xcode时)将自动安装AppleWWDRCA.cer(Apple Worldwide Developer Relations Certification Authority)这个中间证书(Intermediate Certificates)。它实际上就是iOS证书的CA,其公钥用于解密认证证书的可靠性。如果Mac Keychain Access证书助理在申请证书时尚未安装过该证书,请先下载安装(Signing requires that you have both the signing identity and the intermediate certificate installed in your keychain)。
通过Keychain证书助理手动申请开发证书时(也可通过Xcode自动请求生成),keychain将生成一个包含开发者身份信息的CSR(Certificate Signing Request)文件;同时,Keychain Access|Keys中将新增一对Public/Private Key Pair(This signing identity consists of a public-private key pair that Apple issues)。
private key用于签名(CodeSign),始终保存在Mac OS的Keychain Access中;public key一般随证书散布出去,对签名进行校验认证。用户必须保护好本地Keychain中的private key,以防伪冒。
- Keep a secure backup of your public-private key pair. If the private key is lost, you’ll have to create an entirely new identity to sign code.
- Worse, if someone else has your private key, that person may be able to impersonate you.
在Apple开发网站上传该CSR文件,Apple证书颁发机构WWDRCA将使用private key对CSR中的public key和一些身份信息进行加密签名生成数字证书(ios_development.cer)并记录在案(Apple Member Center)。
从Apple Member Center网站下载证书到Mac上双击即可安装。证书安装成功后,在KeychainAccess|Keys中展开创建CSR时生成的Key Pair中的私钥前面的箭头,可以查看到包含其对应公钥的证书(Your requested certificate will be the public half of the key pair.);在Keychain Access|Certificates中展开安装的证书(ios_development.cer)前面的箭头,可以看到其对应的私钥。
Certificate被配置到【Xcode Target|Build Settings|Code Signing|Code Signing Identity】下,下拉选择Identities from Profile "..."(一般先配置Provisioning Profile)。
Provisioning Profile文件包含了上述的所有内容:证书、App ID和设备。
一个Provisioning Profile对应一个Explicit App ID或Wildcard App ID(一组相同Prefix/Seed的App IDs)。在网站上手动创建一个Provisioning Profile时,需要依次指定App ID(单选)、证书(Certificates,可多选)和设备(Devices,可多选)。用户可在网站上删除(Delete)已注册的ProvisioningProfiles。
Provisioning Profile决定Xcode用哪个证书(公钥)/私钥组合(Key Pair/Signing Identity)来签署应用程序(Signing Product),将在应用程序打包时嵌入到.ipa包里。安装应用程序时,Provisioning Profile文件被拷贝到iOS设备中,运行该iOS App的设备也通过它来认证安装的程序。
如果要打包或者在真机上运行一个APP,一般要经历以下三步:
- 首先,需要证书对应的私钥来进行签名,用于标识这个APP是合法、安全、完整的;
- 其次,需要指明它的App ID,并且验证Bundle ID是否与其一致;
- 然后,如果是真机调试,需要确认这台设备是否授权运行该APP。
Provisioning Profile把这些信息全部打包在一起,方便我们在调试和发布程序打包时使用。这样,只要在不同的情况下选择不同的Provisioning Profile文件就可以了。
Provisioning Profile也分为Development和Distribution两类,有效期同Certificate一样。Distribution版本的ProvisioningProfile主要用于提交App Store审核,其中不指定开发测试的Devices(0,unlimited)。App ID为Wildcard App ID(*)。App Store审核通过上架后,允许所有iOS设备(Deployment Target)上安装运行该App。
Xcode将全部供应配置文件(包括用户手动下载安装的和Xcode自动创建的Team Provisioning Profile)放在目录~/Library/MobileDevice/Provisioning Profiles下。
以下为典型供应配置文件*.mobileprovision的构成简析:
(1)Name:该mobileprovision的文件名。
(2)UUID:该mobileprovision文件的真实文件名。
(3)TeamName:Apple ID账号名。
(4)TeamIdentifier:Team Identity。
(5)AppIDName:explicit/wildcard App ID name(ApplicationIdentifierPrefix)。
(6)ApplicationIdentifierPrefix:完整App ID的前缀(TeamIdentifier.*)。
(7)DeveloperCertificates:包含了可以为使用该配置文件应用签名的所有证书
。
证书是基于Base64编码,符合PEM(PrivacyEnhanced Mail, RFC 1848)格式的,可使用OpenSSL来处理(opensslx509 -text -in file.pem)。
从DeveloperCertificates提取之间的内容到文件cert.cer(cert.perm):
-----BEGIN CERTIFICATE-----
将之间的内容拷贝至此
-----END CERTIFICATE-----`
Mac下右键QuickLook查看cert.cer(cert.perm),在Keychain Access中右键Get Info查看对应证书ios_development.cer,正常情况(公私钥KeyPair配对)应吻合;Windows下没有足够信息(WWDRCA.cer),无法验证该证书。
如果你用了一个不在这个列表中的证书进行签名,无论这个证书是否有效,这个应用都将CodeSign Fail。
(8)Entitlements键
对应的 :
keychain-access-groups:$(AppIdentifierPrefix),参见Code Signing Entitlements(*.entitlements)。
每个应用程序都有一个可以用于安全保存一些如密码、认证等信息的keychain,一般而言自己的程序只能访问自己的keychain。通过对应用签名时的一些设置,还可以利用keychain的方式实现同一开发者签证(就是相同bundle seed)下的不同应用之间共享信息的操作。比如你有一个开发者帐户,并开发了两个不同的应用A和B,然后通过对A和B的keychain access group这个东西指定共用的访问分组,就可以实现共享此keychain中的内容。
application-identifier:带前缀的全名,例如$(AppIdentifierPrefix)com.apple.garageband。
com.apple.security.application-groups:App Group ID(group. com.apple),参见Code Signing Entitlements(*.entitlements)。
com.apple.developer.team-identifier:同Team Identifier。
(9)Provisioned Devices:该mobileprovision授权的开发设备的UDID
。
五 .开发组供应配置文件( Team Provisioning Profiles )Provisioning Profile被配置到【XcodeTarget|Build Settings|Code Signing|Provisioning Profile】下,然后在Code Signing Identity下拉可选择Identities from Profile "..."(即Provisioning Profile中包含的Certificates)。
六 .App Group (ID)每个Apple开发者账号都对应一个唯一的Team ID,Xcode3.2.3预发布版本中加入了Team Provisioning Profile这项新功能。
在Xcode中添加Apple Developer Account时,它将与Apple Member Center后台勾兑自动生成iOS Team Provisioning Profile(Managed by Xcode)。
Team Provisioning Profile包含一个为Xcode iOS Wildcard App ID(*)生成的iOS Team Provisioning Profile:*(匹配所有应用程序),账户里所有的Development Certificates和Devices都可以使用它在这个eam注册的所有设备上调试所有的应用程序(不管bundleidentifier是什么)。同时,它还会为开发者自己创建的Wildcard/Explicit App IDs创建对应的iOSTeam Provisioning Profile。
Team Provisioning Profile生成/更新时机:
- Add an Apple ID account to Xcode
- Fix issue "No Provisioning Profiles with a valid signing identity" in Xcode
- Assign Your App to a Team in Xcode project settings of General|Identity
- Register new device on the apple development website or Xcode detected new device connected
利用Xcode生成和管理的iOS Team Provisioning Profile来进行开发非常方便,可以不需要上网站手动生成下载Provisioning Profile。
Team Provisioning Profile同Provisioning Profile,只不过是由Xcode自动生成的,也被配置到【XcodeTarget|Build Settings|Code Signing|Provisioning Profile】下。
七 .证书与签名( Certificate& Signature)WWDC14除了发布了OS X v10.10和switf外,iOS 8.0也开始变得更加开放了。说到开放,当然要数应用扩展(App Extension)了。顾名思义,应用扩展允许开发者扩展应用的自定义功能和内容,能够让用户在使用其他应用程序时使用该项功能,从而实现各个应用程序间的功能和资源共享。可以将扩展理解为一个轻量级(nimble and lightweight)的分身。
扩展和其Containing App各自拥有自己的沙盒,虽然扩展以插件形式内嵌在Containing App中,但是它们是独立的二进制包,不可以互访彼此的沙盒。为了实现Containing App与扩展的数据共享,苹果在iOS 8中引入了一个新的概念——App Group,它主要用于同一Group下的APP实现数据共享,具体来说是通过以App Group ID标识的共享资源区——App Group Container。
用户可在网站上编辑Explicit App IDs的App Group Assignment;可以删除(Delete)已注册的AppGroup (ID)。
Containing App与Extension的Explicit App ID必须Assign到同一App Group下才能实现数据共享,并且Containing App与Extension的App ID命名必须符合规范:
假如Garageband这个App ID为“com.apple.garageband”,则支持从语音备忘录导入到Garageband应用的插件的App ID可能形如“com.apple.garageband.extImportRecording”。
App(ex)
App Group ID
Provisioning Profile
Code Signing Identity
(Certificate Key Pair)
App ID
(bundle identifier)
Devices
(test)
GarageBand
置于同一分组:
group.com.apple
(1)共用同一证书:ios_development.cer
(2)共用证书Key Pair中的Private Key进行CodeSign
com.apple.garageband
授权开发测试设备的UDIDs
GarageBand扩展插件
com.apple.garageband.extImportRecording
关于Provisioning Profile,可以使用自己手动生成的,也可以使用Xcode自动生成的Team Provisioning Profile。
App Group会被配置到【Xcode Target|Build Settings|Code Signing|Code Signing Entitlements】文件(*.entitlements)的键com.apple.security.application-groups下,不影响Provisioning Profile生成流程。
八 .在多台机器上实现开发账户/证书共享
每个证书(其实是公钥)对应的私钥会被用来对内容(executable code,resources such as images and nib files aren’t signed)进行数字签名(CodeSign)——使用哈希算法生成内容摘要(digest)。上面已经提到,公钥被包含在数字证书里,数字证书又被包含在描述文件(Provisioning File)中,描述文件在应用被安装的时候会被拷贝到iOS设备中。
iOS/Mac机上的ios_development.cer可以被AppleWWDRCA.cer中的 public key解密,从而获取每个开发证书中可信任的公钥。
1.iOS/Mac设备(系统)使用CA证书(WWDRCA.cer)来判断App Provisioning Profile(Code Signing Identity)的合法性:
- 若用WWDRCA公钥能成功解密出证书并得到公钥(Public Key)和内容摘要(Signature),证明此证书确乃AppleWWDRCA发布,即证书来源可信;
- 再对证书本身使用哈希算法计算摘要,若与上一步得到的摘要一致,则证明此证书未被篡改过,即证书完整。
2.iOS/Mac设备(系统)使用AppProvisioning Profile(Code Signing Identity)证书来判断App的合法性:
- 若用证书公钥能成功解密出App(executable code)的内容摘要(Signature),证明此App确乃认证开发者发布,即来源可信;
- 再对App(executable code)本身使用哈希算法计算摘要,若与上一步得到的摘要一致,则证明此App(executable code)未被篡改过,即内容完整。
若在Xcode Preferences添加了该Accounts,选中Team条目|ViewDetails:可以查看Signing Identities和Provisioning Profiles。
- 选中欲导出的Account,点击+-之后的☸|ExportAccounts,可导出包含account/code signingidentity/provisioning profiles信息的*.developerprofile(Exporting a Developer Profile)文件供其他机器上的Xcode开发使用(Import该Account)。
- 选中欲导出的Signing Identity条目,点击栏底+之后的☸|Export,必须输入密码,并需授权exportkey "privateKey" from keychain,将导出Certificates.p12;或在Keychain Access|Certificates中选中欲导出的certificate或其下private key,右键Export或者通过菜单File|Export Items导出Certificates.p12。
其他Mac机器上双击Certificates.p12(如有密码需输入密码)即可安装该共享证书,在开发者网站上将欲调试的iOS设备注册到该开发者账号名下,并下载对应证书授权了iOS调试设备的Provisioning Profile文件即可在iOS真机设备上开发调试。
九.证书配置常见错误
1. Xcode Target|Genera|Identity Team下提示"Your build settings specify a provisioning profile with the UUID "xxx",howerver, no such provisioning profile was found."
Xcode Target|BuildSettings|Code Signing|当前配置的指定UDID的provisioningprofile在本地不存在,此时需要更改Provisioning Profile。必要时手动去网站下载或重新生成Provisioning Profile或直接在Xcode中Fix issue予以解决(可能自动生成iOS Team ProvisioningProfile)!
2.Build Settings|CodeSigning的Provisioning Profile中选择了本地安装的provisioningprofile之后,Code Signing Identity中下拉提示No identities from profile “…”or No identities from keychain.
Xcode配置指定UDID的provisioning profile中的DeveloperCertificates在本地KeyChain中不存在(No identities are available)或不一致(KeyPair中的Private Key丢失),此时需去网站检查ProvisioningProfile中的App ID-Certificate-Device配置是否正确。如果是别人提供的共享账号(*.developerprofile)或共享证书(*.p12),请确保导出了对应Key Pair中的Private Key。必要时也直接在Xcode中Fix issue予以解决(可能自动生成iOS Team ProvisioningProfile)。
3."Invalid application-identifier Entitlement"or "Code Signing Entitlements file do not match those specified in your provisioning profile.(0xE8008016)."
(1)检查对应版本(Debug)指定的*.entitlements文件中的“Keychain Access Groups”键值是否与ProvisioningProfile中的Entitlements项相吻合(后者一般为前者的Prefix/Seed)。
(2)也可以将Build Settings|Code Signing的Provisioning Profile中对应版本(Debug)的Entitlements置空。
4.Xcode配置反应有时候不那么及时,可刷新、重置相关配置项开关(若有)或重启Xcode试试。
参考:
《iPhone真机调试应用程序》《iOS Developer:真机测试》
《iOS Development--Certificates, Provisioning Profiles》
《关于Certificate、Provisioning Profile、App ID的介绍及其关系》
《iOS keyChain 研究》
《数字签名和数字证书》
《苹果开发者账号那些事儿》
《iOS Code Signing 学习笔记》
《代码签名探析/Inside Code Signing》
《iOS Code Signing: 解惑/iOS Code Signing: Under The Hood》