Code Signing
。
作为一个 iOSer 可能在处理证书问题的时候(尤其是多人协作),看到 Keychain、Certificates、Private Key、AppID、Provisioning Profile、CertificateSigningRequest、entitlements 以及 P12等一堆概念简直头都要秃了。
本文从原理入手,从为什么有这些概念入手,对 App Signing
原理进行一遍梳理,再来探究怎么做才是最佳实践,来为大家揭开黑幕背后的 Code Signing
。
使用惯了 Windows PC 以及 安卓手机的朋友可能已经习惯了随便哪里都能下载到软件,甚至于去下载破解亦或是盗版软件,这也对直接导致了系统性的风险,成为一些别有用心人士的肉鸡。在 AppStore 诞生的时候,Apple 试图去解决这些问题,来对 iOS 以及运行于之上的第三方 Apps 有绝对的掌控权,来保证用户手机上的每一个 App 都是经过认证的、安全的。那么怎么做呢?没错,苹果发明了一套签名机制,在 Mac OS X Leopard 10.5 (也是第一台 iPhone 发布的时候)上首次运用了这项技术。
公开密钥加密(英语:Public-key cryptography),也称为非对称加密(英语:asymmetric cryptography),是密码学的一中演算法,它需要两個密钥,一個是公开密钥,另一個是私有密钥;一個用作加密的時候,另一個則用作解密。使用其中一個密钥把明文加密后所得的密文,只能用相对应的另一個密钥才能解密得到原本的明文;甚至连最初用來加密的密钥也不能用作解密。由于加密和解密需要兩個不同的密钥,故被称为非对称加密;不同于加密和解密都使用同一個密钥的对称加密。(引用自维基百科)。
我们已经大致了解了非对称加密的概念,那么什么是数字签名呢?数字签名就是类似于物理签名,但是使用了公钥加密领域的技术实现,用于鉴别数字信息的方法。一套数字签名通常定义了两种互补的算法,一个用来签名,一个用来验证。数字签名了的文件的完整性是很容易验证的,而且数字签名具有不可抵赖性。
一言以蔽之,数字签名就是将公钥密码反过来使用。签名者把数据(通常是散列值)用私钥加密,用公钥将加密的数据解密并比对。
Keychain Access 是 macOS 中保存用户账户密码的工具,用户的公钥以及私钥也都被 keychain 管理着。我们可以使用 keychain 的证书助手来申请公私钥。生成的 CertificateSigningRequest
就是公钥,私钥保存在 keychain 中。
AppID 是由 TeamID 和 BundleID 组合而成,类似于 A1B2C3D4.com.domain.appName
形式。分为 Wildcard App ID 和 Explicit App ID,前者适用于一组 Apps,后者只是用于单个 App。
Entitlements 包含了 App 关于 iCloud、Push、backgroundMode 等权限信息。
Provisioning Profile 里边包含了数字签名信息、授权设备列表信息、Entitlements 等信息以及这些信息的签名。
p12 是一种文件格式,里边包含了我们的私钥信息以及符合 X.509
标准的公钥。
前边我们提到了数字签名的过程,只需要一对公私钥就可以实现如下图的签名流程
可是为什么在 Apple 的体系中又多了上述那么多额外的概念?这是因为我们从 App Store 下载应用的时候,上述的数字签名过程虽然可以满足我们的需求,但是对于 iOS 应用来说,在开发阶段我们还可以借助 Xcode 来把应用安装在我们的手机上,还可以使用 In-House
以及 Ad-hoc
形式来安装我们的应用。这就产生了新的问题:
1) IPA 文件不用经过苹果的服务器,就能经过认真安装到我们的手机上。
2) 苹果对应用掌握绝对的主动权:
3) 只有经过苹果允许才能安装;
4) 避免开发阶段的 app 被安装在任意手机。
为了实现解决这些问题,苹果的工程师使用了双层签名,大致流程如下:
在你的 Mac 开发机器生成一对公私钥,这里称为公钥L,私钥L。L:Local 苹果自己有固定的一对公私钥,跟上面 AppStore 例子一样,私钥在苹果后台,公钥在每个 iOS 设备上。这里称为公钥A,私钥A。A:Apple
把公钥 L 传到苹果后台,用苹果后台里的私钥 A 去签名公钥 L。得到一份数据包含了公钥 L 以及其签名,把这份数据称为证书。
在苹果后台申请 AppID,配置好设备 ID 列表和 APP 可使用的权限,再加上第③步的证书,组成的数据用私钥 A 签名,把数据和签名一起组成一个 Provisioning Profile 文件,下载到本地 Mac 开发机。
在开发时,编译完一个 APP 后,用本地的私钥 L 对这个 APP 进行签名,同时把第④步得到的 Provisioning Profile 文件打包进 APP 里,文件名为 embedded.mobileprovision,把 APP 安装到手机上。
在安装时,iOS 系统取得证书,通过系统内置的公钥 A,去验证 embedded.mobileprovision 的数字签名是否正确,里面的证书签名也会再验一遍。
确保了 embedded.mobileprovision 里的数据都是苹果授权以后,就可以取出里面的数据,做各种验证,包括用公钥 L 验证APP签名,验证设备 ID 是否在 ID 列表上,AppID 是否对应得上,权限开关是否跟 APP 里的 Entitlements 对应等。
第 1 步对应的是 keychain 里的 “从证书颁发机构请求证书”,这里就本地生成了一对公私钥,保存的 CertificateSigningRequest 就是公钥,私钥保存在本地电脑里。
第 2 步苹果处理,不用管。
第 3 步对应把 CertificateSigningRequest 传到苹果后台生成证书,并下载到本地。这时本地有两个证书,一个是第 1 步生成的,一个是这里下载回来的,keychain 会把这两个证书关联起来,因为他们公私钥是对应的,在XCode选择下载回来的证书时,实际上会找到 keychain 里对应的私钥去签名。这里私钥只有生成它的这台 Mac 有,如果别的 Mac 也要编译签名这个 App 怎么办?答案是把私钥导出给其他 Mac 用,在 keychain 里导出私钥,就会存成 .p12 文件,其他 Mac 打开后就导入了这个私钥。
第 4 步都是在苹果网站上操作,配置 AppID / 权限 / 设备等,最后下载 Provisioning Profile 文件。
第 5 步 XCode 会通过第 3 步下载回来的证书(存着公钥),在本地找到对应的私钥(第一步生成的),用本地私钥去签名 App,并把 Provisioning Profile 文件命名为 embedded.mobileprovision 一起打包进去。这里对 App 的签名数据保存分两部分,Mach-O 可执行文件会把签名直接写入这个文件里,其他资源文件则会保存在 _CodeSignature 目录下。
但是显然这么做对于我们来说还是不太方便。在 Xcode8 中,苹果加入了自动管理机制,可以参考 WWDC 2016 - Session 401
。但是对于我来说这还是不够好用,下一节我将来告诉大家我是如何使用 Fastlane
的 Cert
、match
以及sign
工具来管理我的证书。
- END -