上周我给组里做了一次“学习汇报”,其实也是组里每周都有的技术分享,每个人都有机会,这次轮到我了。那作为团队菜鸟,我该讲点什么呢?
我思前想后,突然想到自己之前老是遇到的一个棘手的问题:在真机上运行iOS工程时,工程还没跑起来,工程配置的签名(Targets > Signing & Capabilities)那里就先报错了,不管是自己的工程,第三方开源库,还是公司的项目。
虽然自己每次面向谷歌或者面向同事都可以找到答案,但为什么能解决以及为什么真机运行iOS项目需要签名,自己只能说是对它们一知半解、被它们弄得云里雾里。
不过现在我总算明白了它们背后的奥秘!如果你也有同样的困惑,往下看,保证这次让你彻底弄懂它——iOS签名背后的原理!
本文目标
阅读完本文会让你拥有轻松解决以下问题的能力:
- 如何在真机上跑自己的iOS项目,或者iOS开源库?
- 如何在真机上跑公司的iOS项目?
另外,本文的终极目标是:遇到任何iOS签名相关问题时,你都能够快速解决。
预备知识:数字签名 & 数字证书
在聊iOS签名之前,我们先需要了解两个预备知识,那就是在互联网世界里的签名✒️和证书。
可参考:数字签名和数字证书是什么?——阮一峰
数字签名
数字签名一般夹带在要传输的数据中,用来防止数据被篡改。
它的底层核心是哈希混淆算法和非对称加密技术(公/私钥)。
生成
签名Signature的生成由通信中的发送方Sender进行,首先对要传输的数据Data进行哈希Hash混淆得到数据摘要Digest,然后用私钥Private Key对摘要进行加密,这样就生成了数据的签名。
验证
接收方Receiver接收来自发送方的数据和签名,对它们分别做如下处理:
- 数据:使用与发送方相同的哈希算法对数据进行混淆,得到数据摘要A;
- 签名:利用发送方加密所用私钥对应的公钥Public Key,对签名进行解密,得到数据摘要B。
比对摘要A和摘要B,如果相等,则说明数据没有被篡改,否则数据存在问题。
签名的生成和验证过程合在一起如下图所示,自己再分析一遍可加深理解。
带着问题往下走:
Q1: 猜想一下iOS签名和验签的过程,App开发者和App使用者中,谁是发送方,谁是接收方,传输的数据又是什么呢?
A1: App开发者是发送方,App使用者是接收方,传输的数据就是App的安装包。
Q2: 接收方如何拿到解密所用的公钥呢?
请接着往下看。
数字证书
在了解数字证书之前,我们可能马上想到的是,发送方在发送数据和签名的同时,再附带上公钥不就万事俱备了吗~如下图所示:
但是这样会引入一个新的问题:
Q1: 接收方如何确认公钥没有被其他人恶意替换呢?也就是公钥的身份不明。
这时候数字证书就该登场了。
组成内容
我们可以先看一下数字证书的组成内容,它由公钥、公钥的身份信息Identity Info以及它们的签名B组成。
注意:加密公钥数据所用的私钥又是另外一个公私钥组合了,它是由权威的认证机构(Certificate Authority,CA)颁发的。
公钥包装在数字证书中传递
这下,原来发送方发送的内容由数据+签名+公钥,变成了数据+签名+证书。
证书里包含了接收方解密签名A所需的公钥,除此之外,证书里还有公钥的身份信息和签名B:
- 身份信息的存在则可以消除公钥身份不明的隐患;
- 而签名B则对公钥及其身份信息未被篡改做了保证。
但也因为有了签名B,我们在取公钥时,还需要先对证书里的签名B进行验证:
注意:解密证书里签名B所用的公钥也是由CA颁发的,它存在于CA证书里。
现在,我猜你已经能够记住证书的组成内容了:公钥+公钥的身份信息+它们的签名。
带着问题往下走:
Q1: 接收方从哪里获得这个CA证书呢?接收方又要如何验证这个CA证书的签名呢?似乎进入了无限循环♻️。
请接着往下看。
证书信任链
CA证书一般是在安装系统/软件时内置的,这样我们总该信任它了吧~
接下来,我们可以再了解一下证书的信任链。
根据证书在信任链中所处的位置,可以将证书分为三种:
- 根证书Root Certificate(参考Apple 操作系统中可用的受信任根证书——Apple官方)
- 中间证书Intermediate Certificate
- 叶子证书Leaf Certificate
举例:我的开发者证书A(Apple Development)由中间证书B(Apple Worldwide Developer Relations Certification Authority,安装Xcode时内置)的CA签发,中间证书B由根证书C(Apple Root CA,系统内置)的CA签发,而根证书C是由自己的CA签发的,因为C已经在信任链的顶端啦,它自己说了算。
回到上一个问题:如何保证这个CA证书可信呢?只要接收方有签发方的证书,那么用证书里的公钥验证一下就可以了。比如一台iPhone安装我开发的App时,收到了我的开发者证书A,那么手机就会去找签发方的证书B(Apple Worldwide Developer Relations Certification Authority,iOS系统内置)来验证A是否可信。
现在,你也可以看看自己的Mac > 钥匙串Keychain软件 > 证书Certificates,加深理解。
继续带着问题往下走:
Q1: 加密公钥及其身份信息所用的CA私钥在我们的电脑本地吗?如果不是,该如何生成我们的证书呢?
iOS证书申请原理 & 申请方法
当然不是,这些私钥可是CA签发证书的秘密宝贝。比如,我们要想申请iOS开发者证书,则需要找Apple官方帮忙,Apple官方会使用上面提到的中间证书CA的私钥来签发证书。
申请原理
想要申请自己的iOS开发者证书,分为以下几步:
- 用自己的电脑生成公私钥对,并填写公私钥对的身份信息;
- 将公钥及其身份信息发送给Apple CA;
- CA使用使用哈希算法和CA私钥对数据(公钥及其身份信息)进行签名,数据和签名则组成了我们想要的证书;
- 我们再在CA上把证书下载到电脑上,安装证书后电脑会自动关联对应的私钥。
申请方式:2种
具体的申请方式有2种:1)上传CSR (CSR, Certificate Signing Request) 文件方式,2)Xcode自动申请方式,这里推荐第二种。
1)上传CSR文件方式
该方法适合想了解iOS证书申请原理的你。注意:该方式需要加入苹果开发者计划,$99/年。
a) 打开Keychain > Certificate Assistant > Request a Certificate From a Certificate Authority...:
b) 输入身份信息(邮箱、证书名字),可以选择保存到本地,本地就得到了一个CSR (.certSigningRequest) 文件,里面包含了公钥及其身份信息。
你可能会问,那私钥放在哪里呢?如果你细心的话,你会发现Keychain > login > Keys里面已经多了一对你命名的公私钥对。
c) 登陆Apple Developer网站,进入Certificates, Identifiers & Profiles板块,上传刚刚生成的CSR文件,即可生成证书(.cer文件),此时将证书下载到本地,双击即可导入Keychain,在Keychain > login > May Certificates中可以看到该证书。
如果你细心的话,你会发现Keychain里该证书已经和一把私钥绑定起来了,如果想把证书共享给其它开发者使用,则需要右键导出我们熟悉的.p12文件,它包含了证书和对应的私钥~
2)Xcode自动申请方式(推荐)
这种方式则不需要繁琐的上传CSR文件、下载.cer证书过程,也不会强求你加入Apple开发者计划。
a) 在Xcode里登陆Apple账号:Xcode > Preference > Account > Apple IDs > 「+」。
b) 登陆片刻后,你就会发现KeyChain里自动多出一份相应的证书了。
注意:如果你加入了开发者计划,Apple开发者网站上也会自动添加该证书。
iOS签名 & 打包原理
终于来到iOS签名部分了,前面的内容你理解的怎么样呢?如果你还有点迷糊,那么记住签名的作用是防止传输的数据被篡改这一点,你就基本掌握本文的真谛了!
其实,在真正iOS签名的时候,还不是只附加证书(含公钥)这一个东西,我们还需要对证书做一层包装,那就是我们熟悉的Provisioning Profile文件,又叫PP文件、描述文件、供应配置文件。
PP文件
先来了解一下这个重要的PP文件,我们可以把它理解为证书的升级版。
如何生成
- 开发者平台上申请;
- Xcode自动生成:Xcode > Targets > Signing & Capabilities > 勾选Automatically manage signing。
文件结构
- App ID:
- 在Apple开发者平台上注册;或者根据Xcode > Targets > Signing & Capabilities填写的Bundle ID自动生成。
- 我们在Xcode > Targets > Signing & Capabilities中填写的Bunlde Identifier必须与App ID是一致或匹配的。
- Entitlements:
- 允许使用的权限列表,实际在App中使用的权限必须是这个列表的子集,即我们在Xcode > Targets > Signing & Capabilities中添加的Capabilities,不能超出其范围。
- 工程目录下也会有一份.entitlements文件(授权文件),它是根据Xcode > Targets > Signing & Capabilities里添加的Capabilities自动生成的。如果 App 中使用到了某项沙盒限制的功能,但是该.entitlements文件没有声明对应的权限,当App运行到相关代码时,App会直接 Crash。
- Certificates:由iOS开发者证书组成,可以不只一个,它们就是上面申请到的iOS证书。
- Devices:由iOS设备的UDID (Unique Device Identifier) 组成的列表,限定了可以开发调试该App的iOS设备。
- Signature:在生成PP文件时,由Apple CA对其进行签名,防止被人篡改。
示例
PP文件默认保存在:~/Library/MobileDevice/Provisioning\ Profiles
。
下面是Xcode自动生成的一个PP文件预览:
签名 & 打包
iOS签名和打包过程其实是由Xcode来把控的,看下图:
- 首先,Xcode会检查App里的bundle ID是否匹配PP文件中的App ID,以及App里的Entitlements (.entitlements) 文件声明的权限是否在PP文件中Entitlements允许权限的范围内,二者任一条件为否,则检查不通过;
- 其次,Xcode会在电脑的Keychain里找,是否有匹配PP文件中Certificates的证书,如果匹配到了,才继续下一步;
- 然后,Xcode会检查匹配到的证书是否绑定了对应的私钥,如果没有,就无法进行下面的关键步骤——签名;
- 现在,开始利用哈希算法和私钥对App进行签名了;
- 最终,对App、PP文件和签名进行打包,即生成.ipa包。
⚠️:上面忽略了签名C和签名A的验证过程,在第1步使用PP文件之前和在第2步匹配到证书之后,都应先使用CA公钥对其附带的签名C和A进行验证,防止它们被篡改。如有错误,欢迎指正。
延伸问题:
Q1: Xcode会对App的哪些内容进行签名呢?
A1: 全部内容。但签名过程较为复杂,这里就不展开了,简而言之,为了权衡签名的安全性和效率,App的签名被分为4次哈希和1次加密,每次哈希都是环环相扣的,保证了App内容没有被篡改。
具体可参考细说iOS代码签名(三):签名的过程及代码签名的数据结构。
iOS验签原理
说完了iOS签名,那真机在安装App时如何验证它的签名呢?
首先,我们需要知道的是,App安装包分为测试包和正式包,两者的验签过程有所不同。
顺便补充一下测试包和正式包的知识:
1)测试包:分为内测包、准备上传App Store的发布包、Ad Hoc发布包、In-house企业内部发布包4种类型。
2)正式包:上传到App Store上的安装包。
⚠️:Xcode打的包都属于测试包。
参考iOS不同类型测试包介绍——搜狗测试公众号。
测试包
安装测试包时,真机对其进行了完整的验签过程,如下图所示:
- 首先,真机设备会使用系统内置的CA公钥验证PP文件及其签名C的合法性;
- 其次,真机再使用系统内置的CA公钥验证PP文件中证书及其签名A的合法性;(App里保存了生成签名B所用的证书信息,这样真机才知道去取出哪个证书里的公钥)
- 然后,真机再取出证书中的公钥验证App及其签名B的合法性;
- 最后,真机会检查自己的UDID是否在PP文件的Devices列表中,如果存在,才开始安装App。
⚠️:图里简化了验签的过程,相信你已经很熟悉这个过程了,哈希混淆、公钥解密、判等...
其实,签名的验证并非一次性完成,在安装、启动和运行时有着不同的校验规则,上面简化了该过程,具体可参考细说iOS代码签名(四):签名校验、越狱、重签名。
正式包
而安装正式包时,真机对其的验签过程简化了很多,因为对开发者证书的验签过程交给了App Store。
1)将发布包上传到App Store。
- 当你将发布包(测试包的一种)上传到App Store时,Apple官方同样会对发布包进行验签,其过程类似上述测试包的验签过程;
- 验签通过后,App Store会重新对App进行签名,这里用的就不是开发者证书对应的私钥了,而是CA 私钥;
- 最后,App Store只会对App和新的签名进行打包,生成.ipa包。(⚠️:不包含PP文件了)
2)真机设备安装正式包。
当设备从 App Store上下载 App 后,只需要用系统内置的CA公钥对App进行验签,验证通过即可安装。
练习一下
网上也有一些画得很好的图,你们可以按照序号回顾一遍过程,用来巩固本文的内容。
来自iOS证书那些事儿——掘金。
来自iOS App 签名的原理——Blog。
回到:本文目标
最后,我们再回到文章开头目标里提出的问题,解决那些问题之前永远铭记两个东西:包含私钥的证书(.p12 = .cer + private key)、PP文件(.mobileprovision)。
再看看我们的问题,只要找到上面两个东西就可以了:
- 如何在真机上跑自己的iOS项目,或者iOS开源库?
- 包含私钥的证书:参考iOS证书申请方式那一节;
- PP文件:在开发者平台上申请;或者由Xcode自动生成,Targets > Signing & Capabilities > 勾选Automatically manage signing。
- 如何在真机上跑公司的iOS项目?
- 包含私钥的证书:向团队索要,注意文件后缀是.p12;
- PP文件:向团队索要,记得让负责人在开发者平台上添加自己真机的UDID到PP文件中。
PS:
- 运行测试包安装的App时,一般还需要在真机上信任证书:Settings > General > VPN & Device Management。
- Xcode自动生成PP文件:
参考资料
整体把握:iOS 证书幕后原理——Blog
深度理解:细说iOS代码签名——Blog
其它你可能感兴趣的:
- iOS证书和描述文件申请——DClound
- iOS 应用的发布方式有哪些?——知乎
- iOS重签名探索——