不允许在 App Store 上架,却可以安装证书使用,真是奇怪

图片:William Hook / CC0 不允许在 App Store 上架,却可以安装证书使用,真是奇怪

为什么苹果允许用户安装未受信任的企业级开发者所开发的软件?

黄兢成,iOS / Mac / C++ 开发者

App 上架 AppStore 需要经过苹果的审核,违规 App 不能上架。苹果 iOS 系统有一套完善的加密方案,在不越狱破解的情况下,非 AppStore 下载的 App 需要证书签名,才能在 iOS 上运行。

证书需要向苹果申请,有几类证书。

  • 免费证书。用于入门级开发者,可以用 Xcode 真机调试,不能上架 AppStore。
  • 个人开发者证书。需要收费,支持个人真机调试,可以将上架 AppStore。
  • 公司证书。需要收费,支持多人协作开发,可以将上架 AppStore。
  • 企业证书。需要收费,支持企业自己发布 App, 不可以上架到 AppStore。

某些 App 可以通过 Safari 下载,信任证书后正常使用。这种 App 就是用了企业证书。

为什么苹果公司允许企业证书呢?顾名思义,企业证书是在企业内部使用的。正常情况下,企业证书有如下用途。

某些 App 只是企业内部使用,不方便上架 AppStore。比如企业内部有个 OA 系统,用于员工打卡、请假、收发邮件。企业为 OA 系统开发了 iOS 的 App。这个 App 对企业内部员工很有用,企业外用户完全无用。因此这个 OA 系统 App 不方便上架 AppStore,就算想上架,也不能审核通过。

某些 App 虽然可上架 AppStore 外发,但新版本还在开发测试过程中。使用企业证书签名 App,放到网上,员工打开网页就可下载,方便测试。在开发测试期间,往往一天好几次构建新安装包,内部员工(特别是测试人员)在网上可以下载到最新的测试包。

------------------------

假如通过企业证书,将一些内容违规(比如色情、政策、版权、隐私等)不能上架的 App 外发给用户,属于滥用企业证书。

也有些 App 本身内容并没有什么不妥,实际可以上架 AppStore。但它包含内购,比如某些会员充值,游戏道具购买之类。苹果规定,假如 App 内购买的虚拟物品,是用于 App 本身的,就需要接入苹果的应用内购买(In-App Purchase,IAP), 不能使用第三方支付。但假如使用苹果的 IAP,内购收益需要分给苹果三成。有些 App 不想给苹果分成,想使用微信、支付宝等第三方支付,就用企业证书来分发 App。对于苹果公司来说,这也是企业证书的滥用。

苹果公司不允许滥用企业证书。一旦发现,情况恶劣,会封杀企业证书,企业证书被封杀后,企业自己发布的 App 就闪退不能用了。

另一个惩罚是,将开发者(个人或公司)列入黑名单。列入黑名单后,审核通常会非常慢。有时不仅仅是违规 App,而是这个开发者名下的所有 App 审核都非常慢。正常情况下,App 平均审核时间是两三天。但黑名单上的开发者,有时会审核几周,甚至几个月。几个月不能发布新版本,就会损失很大。

更重的惩罚是,将开发者名下的 App 全部下架,整改后才能重新上架。

有些企业为了避免牵连,有时会用不同的银行卡注册不同的开发账号。需要用不同的银行卡,因为苹果公司会检查银行卡账号,银行卡账号相同的,基本可以判断是同一个用户。自然有时会误杀。个人开发者,不要将自己的银行卡借给他人(比如亲戚)注册开发者账号,不然可能会受到牵连。

苹果对企业证书管理比较严,催生了淘宝上的一个生意,贩卖企业证书。有些 App, 因为内容违规或其它原因不能上架,但又不想暴露自己的企业证书。就会在淘宝上购买其他企业证书来签名发布,跟苹果公司打游击战。淘宝某个企业证书暴露被封杀了,又换一个企业证书重新签名发布。

------------------------

iOS 证书签名,实际是某种数字签名。数字签名基于非对称加密,目的保证文件没有被篡改。比如 A 要发文件给 B, A 生成一对私钥和公钥。A 保留私钥,而将公钥发给 B(或者将公钥预先给 B)。要发文件了,A 就先计算文件的摘要,之后将摘要用私钥加密。之后将文件和加密摘要一起发给 B,B 就用公钥将摘要解密,同时计算出文件的摘要信息。之后 B 对比解密后的摘要,和计算出来的摘要,假如两者相同,就表示文件没有被篡改。

下面我粗略描述一下认证流程,细节可能有误。

苹果有一对私钥和公钥,私钥在他的服务器上,经过重重保护。而对应的公钥内嵌在每一台 iOS 设备上。

当开发者注册证书时,会在本机生成私钥和公钥,将公钥上传给苹果服务器。苹果服务器用自身的私钥,对开发者上传的公司做数字签名,生成证书(cer)。经过苹果的私钥签名,就保证这个开发者证书没有被篡改。这个证书并非公钥本身,但它包含了公钥。

同时开发者会将 App 的描述文件(.mobileprovision) 下载回本机。描述文件主要用于控制 App 的权限,比如这个是否推送、是否支持 iCould 等。

原则上,证书(cer) 和描述文件(.mobileprovision) 是可以合并的。但每个 App 的权限都有所不同,于是就将其拆分成证书和描述文件。

当 App 构建签名时,选择证书(cer) 和描述文件(mobileprovision),会寻找对应的私钥,找不到私钥就会签名失败。之后计算 App 所有资源的摘要,将摘要用私钥加密,生成一个

_CodeSignature

的目录。同时会将公钥和权限信息嵌入到最终生成包中,生成

embedded.mobileprovision

_CodeSignature

中的摘要经过私钥加密,

embedded.mobileprovision

包含对应的公钥用于加密(这个公钥经过苹果自身私钥的签名,保证不会被篡改)。私钥只在本机中保存,用于构建时加密,并不会包含在安装包中。

上述签名过程,Xcode 是全自动的,开发者没有必要了解其过程。整个流程,私钥都只保存在本机中,并没有经过网络,假如私钥经过网络传输,就可能被窃取而不够安全。而本机上的私钥,可以导出成 p12 文件,导入到另一台开发机中。

------------------------

用户安装这个 App 后,需要手动信任描述文件。信任后,苹果用自家的公钥来验证开发者的公钥没有被篡改。再用开发者的公钥将

_CodeSignature

保持的摘要信息解密,验证 App 的资源本身没有被篡改。经过验证,App 就可以运行。

App 内的描述文件,需要用户手动选择信任的。App 不可能在用户不知情的情况下偷偷运行。企业证书签名过的 App, 加密的摘要,和用于解密的描述文件都包含在安装包中。因此用户用 Safari 打开链接、安装、信任描述文件后,就可以运行。

而开发者上传到 AppStore 上的 App,会经过苹果的私钥重新加密签名。因此在 AppStore 上下载的 App,不用手动点信任,也可以直接运行。iOS 设备必然可信任苹果自身的证书。

iOS 也有第三方应用商店,比如 PP 助手、同步推等等。这些商店号称免费安装任何 App,实际是去抓别人的 App,用自己的企业证书重新签名。因而信任第三方商店证书之后,就可以免费运行众多收费 App。但这是有隐患的,因为你不知道这些商店重新签名时,添加了些什么东西。第三方商店为了盈利,也很有动力偷偷添加些佐料。

对于最终用户,没有必要理解上述验证。只需要记住,不要安装来历不明的 App。就算不小心安装了,也不要信任其描述文件。只要不信任,它就没有办法运行。只在 AppStore 下载 App,不要为了贪小便宜去第三方商店下载 App,也不要越狱 iOS 系统。现在的手机不单单是电话本身,还是你的钱包、名片,实在太重要了。