私自安装的程序为何不能运行?XX 助手下载的应用为何频频闪退?codesign failed 究竟是何人所为?missing private key 又是怎么回事?流氓公司集体从良的背后又隐藏着什么?这一切的背后,是 XX 还是 XXX 。。。。咳咳,编不下去了(๑•̀ㅂ•́)و✧,反正只要明白本文是讲 iOS 的签名机制就对了。
但是在讲解签名机制之前,我们需要搞清楚它为什么会存在?苹果为什么要花费这么大的精力来建立签名机制?这需要我们从苹果公司的角度来思考下面的问题:
用户下载应用的过程中被黑客攻击了,程序遭到了篡改,用户的个人隐私遭到泄漏,财产也遭受损失,怎么办?
开发者呕心沥血开发出了一款应用,结果发布之后盗版横行,盗版商发了一笔大财,开发者却饿死家中,怎么办?
流氓应用遍地跑,互相唤醒,全家桶安装,无底限收集用户隐私,卸都卸不掉,用户苦不堪言,怎么办?�
......
以上都是很现实的问题,所以苹果公司为了避免发生这些情况,就必须做到对 app 的安装具有绝对的控制权,也就是只有被苹果认可的应用才可以安装到用户的手机上,而 iOS 的签名机制就是为了这一目的建立的(同时还有 App Store 以及应用审核机制)。这些机制带来了非常多的好处,包括确保应用不被篡改、盗版应用无法运行、流氓厂商面临要么下架要么从良的选择等等,这不仅使苹果的应用生态能够健康可持续的发展,也保证了用户以及开发者的利益。
非对称加密算法
非对称加密算法是 iOS 签名机制的基础,它使用两份密钥(公钥、私钥)来对信息进行加密以及解密:对外公开的密钥为公钥,签名方自己持有的密钥为私钥,通过私钥加密的内容只能用公钥解密,而通过公钥加密的内容也只有私钥能够解密。由于通过公钥反推私钥的难度非常大,理论上只要密钥的位数足够长,反推基本是不可能的,所以非对称加密算法是被广泛使用的安全性非常高的加密算法。
常用的非对称加密算法有 RSA、ECC、Diffie-Hellman 等,以下几篇文章是介绍 RSA 算法的,感兴趣的同学可以了解下:RSA原理与实例、RSA算法原理(一)、RSA算法原理(二)。
Hash 算法
由于非对称加密算法对明文的长度有要求,即不能超过密钥的长度,所以在使用非对称加密算法之前,会使用 Hash 算法对明文进行处理。
任意长度的明文在经过 Hash 算法处理后,都会得到一串长度固定的 Hash 值,并且整个过程是不可逆的,因此 Hash 算法常被用来处理关键数据。常见的 Hash 算法有:MD5、SHA-1、SHA-2 等。
App Store
那么签名机制是如何保证用户下载的应用是被苹果认证的呢?在 iOS 设备上,App Store 是用户下载应用的唯一渠道,苹果公司建立这一渠道的目的就是为了获得应用生态的控制权。开发者在上传应用后,苹果公司会对应用进行非常严格的审核,只有通过审核才可以进入 App Store 展示给用户。审核通过后,苹果公司会按照下图所示的步骤生成应用的签名:
首先使用 Hash 算法对 App 数据进行处理,得到摘要
接下来会使用 Apple 私钥对摘要进行加密,得到签名
用户在 App Store 上购买应用后,签名会和 App 一起下载到用户的手机上,接下来用户的 iOS 设备会进行如下处理:
使用相同的 Hash 算法对下载到用户手机的 App 进行处理,得到摘要1
使用 Apple 公钥对签名进行解密,得到摘要2(Apple 公钥是事先内置在 iOS 设备中的)
判断摘要1和摘要2是否相等,如果相等,那么认证成功,App 可以安装,否则认证失败,App 无法安装
开发者视角
上述认证流程可以满足一个普通用户的需求,但却无法满足开发者。因为开发者在开发应用的过程中会无数次的在手机上调试 app,如果每次安装应用时都要上传到苹果那边签名,然后再下载,这是无法接受的,所以苹果又为开发者建立了独特的本地签名的流程。
开发者会在自己的电脑上生成用于签名认证的公钥和私钥,以后安装 app 到手机上时会在本地进行认证,而无需将 app 上传到苹果的服务器上签名。但是苹果也必须要维护自己控制 app 安装的权利,那么苹果会怎么做呢?答案就是使用数字证书。
数字证书
开发者通过 钥匙串访问->证书助理->从证书颁发机构请求证书
生成 Certificate Signing Request 文件(CSR 文件),同时也在本地自动生成了相应的公钥和私钥,在填好必要的信息后,开发者将 CSR 文件上传给苹果用于生成数字证书。
CSR 文件记录了开发者的个人信息、公钥、加密算法以及 Hash 算法等内容,可以使用以下命令来查看文件内容:
openssl asn1parse -i -in CertificateSigningRequest.certSigningRequest
苹果收到 CSR 文件后,会使用自己的私钥对开发者的公钥进行签名,最终生成一个数字证书(包含开发者账号信息、公钥以及相应的签名)返回给开发者,查看证书信息的命令如下:
openssl x509 -inform der -in ios_development.cer -noout -text
点击安装证书后,钥匙串会自动将证书和相应的私钥对应起来,同时你还需要在 Xcode 中将签名证书设置为你刚刚安装的证书。此后在编译完 app 时,系统会使用数字证书对应的本地私钥对 app 进行签名,然后同数字证书一起安装到手机上,接下来 iOS 设备会通过 Apple 公钥来验证数字证书是否有效,验证通过后会使用数字证书中的公钥来验证 app 是否有效,最终判断 app 是否可以被安装。
Provisioning Profile
仅有数字证书是不够的,为了防止权限被滥用,开发者还需要在苹果官网上注册用于开发的设备,仅有注册后的设备才被允许按照以上流程安装 app。
个人开发者账号可以注册的设备是100台,Enterprise 账号不限制数量,但是不能将应用发布到 App Store 上,关于开发者账号的种类以及区别可以参考 Choosing a Membership。
除此之外,苹果还需要对 app 的权限进行控制,例如是否能够使用 iCloud、Wallet、Maps 等,苹果将这些功能授权统称为 Entitlements。开发者将上述权限配置完成后,需要从苹果后台下载并安装相应的 mobileprovision 文件,该文件包含:
AppId,每个 app 都会有对应的 mobileprovision 文件
功能授权列表
已注册的设备列表
数字证书
苹果签名
没错,你没看错,苹果对你下载的 mobileprovision 文件也会签名,所以你是无法更改文件内容的。因此你需要注意的是,如果你在 Xcode 中的 Capabilities 开启了 app 的某项功能,那么你一定要去苹果后台更新 app 的权限,然后下载最新的 mobileprovision 安装,否则会有诡异的事情发生。
当然,如果有需要的话,你可以使用以下命令来查看 mobileprovision 文件内容:
security cms -D -i embedded.mobileprovision
app 编译完成之后,也会将 mobileprovision 文件打包到 app 中。
综合上述信息,最终流程是这样的:
开发者在本地生成私钥和公钥,然后将公钥上传给苹果并获取苹果签名的数字证书
开发者在苹果后台配置 AppId,注册开发设备,配置功能权限,最后下载并安装 mobileprovision 文件
开发者用本地私钥签名 app,并将 mobileprovision 文件随 app 安装进 iOS 设备
iOS 设备使用 Apple 公钥验证 mobileprovision 文件的签名以及数字证书的签名
使用数字证书包含的公钥来验证 app 的签名
验证安装 app 的设备是否在设备列表中,AppId 是否一致,Entitlements 和 app 中的权限是否对的上等等
其他发布流程
除了 App Store 以及开发者真机调试外,苹果还设置了其他流程来发布应用,包括 In-House 以及 Ad-Hoc。只有 Enterprise 账号才可以进行 In-House 发布,其目的是为了在企业内部使用 app,现在常见的 XX 助手就是使用 Enterprise 账号来发布盗版应用,对于这种滥用行为,苹果只能一个个吊销账号,并没有太好的办法。而 Ad-Hoc 则是用于小范围发布给用户进行测试,限制的数量是100台,参与 Ad-Hoc 的设备必须要事先注册。关于发布流程可以参考这篇文章。
补充
你可能听人说过 p12 文件,那么这个是什么呢?p12 文件常用于 iOS 证书共享,你可以在钥匙串中以 p12 格式导出你的本地私钥,配合数字证书、 mobileprovision 文件共享给团队中的其他成员,这样其他人也可以用你的证书开发程序了。
这里再额外提一下,我们最终生成的 app 都是 ipa 格式,里面包含可执行文件、资源文件、第三方 Framework 等,可执行文件的签名会直接保存在可执行文件里,其他文件的签名会放在 _CodeSignature/CodeResources 中,而 Frameworks 内部会包含自身的签名信息。你还可以在 ipa 中找到 embedded.mobileprovision 文件,然而如果你是从 App Store 上下载的 app,那么是没有 mobileprovision 文件的。
如果你想进一步了解苹果签名,你可以参考以下文档:Code Signing Guide、App Distribution Guide、代码签名探析。