图解iOS签名背后的原理

4,301 阅读14分钟

上周我给组里做了一次“学习汇报”,其实也是组里每周都有的技术分享,每个人都有机会,这次轮到我了。那作为团队菜鸟,我该讲点什么呢?

我思前想后,突然想到自己之前老是遇到的一个棘手的问题:在真机上运行iOS工程时,工程还没跑起来,工程配置的签名(Targets > Signing & Capabilities)那里就先报错了,不管是自己的工程,第三方开源库,还是公司的项目。

虽然自己每次面向谷歌或者面向同事都可以找到答案,但为什么能解决以及为什么真机运行iOS项目需要签名,自己只能说是对它们一知半解、被它们弄得云里雾里。

不过现在我总算明白了它们背后的奥秘!如果你也有同样的困惑,往下看,保证这次让你彻底弄懂它——iOS签名背后的原理

本文目标

阅读完本文会让你拥有轻松解决以下问题的能力:

  1. 如何在真机上跑自己的iOS项目,或者iOS开源库?
  2. 如何在真机上跑公司的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:

  1. 身份信息的存在则可以消除公钥身份不明的隐患;
  2. 签名B则对公钥及其身份信息未被篡改做了保证

但也因为有了签名B,我们在取公钥时,还需要先对证书里的签名B进行验证:

注意:解密证书里签名B所用的公钥也是由CA颁发的,它存在于CA证书里。

现在,我猜你已经能够记住证书的组成内容了:公钥+公钥的身份信息+它们的签名。

带着问题往下走

Q1: 接收方从哪里获得这个CA证书呢?接收方又要如何验证这个CA证书的签名呢?似乎进入了无限循环♻️。

请接着往下看。

证书信任链

CA证书一般是在安装系统/软件时内置的,这样我们总该信任它了吧~

接下来,我们可以再了解一下证书的信任链。

根据证书在信任链中所处的位置,可以将证书分为三种

举例:我的开发者证书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开发者证书,分为以下几步:

  1. 用自己的电脑生成公私钥对,并填写公私钥对的身份信息;
  2. 将公钥及其身份信息发送给Apple CA;
  3. CA使用使用哈希算法和CA私钥对数据(公钥及其身份信息)进行签名,数据和签名则组成了我们想要的证书;
  4. 我们再在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文件,我们可以把它理解为证书的升级版。

如何生成

  1. 开发者平台上申请;
  2. 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来把控的,看下图:

  1. 首先,Xcode会检查App里的bundle ID是否匹配PP文件中的App ID,以及App里的Entitlements (.entitlements) 文件声明的权限是否在PP文件中Entitlements允许权限的范围内,二者任一条件为否,则检查不通过;
  2. 其次,Xcode会在电脑的Keychain里找,是否有匹配PP文件中Certificates的证书,如果匹配到了,才继续下一步;
  3. 然后,Xcode会检查匹配到的证书是否绑定了对应的私钥,如果没有,就无法进行下面的关键步骤——签名;
  4. 现在,开始利用哈希算法和私钥对App进行签名了;
  5. 最终,对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不同类型测试包介绍——搜狗测试公众号。

测试包

安装测试包时,真机对其进行了完整的验签过程,如下图所示:

  1. 首先,真机设备会使用系统内置的CA公钥验证PP文件及其签名C的合法性;
  2. 其次,真机再使用系统内置的CA公钥验证PP文件中证书及其签名A的合法性;(App里保存了生成签名B所用的证书信息,这样真机才知道去取出哪个证书里的公钥)
  3. 然后,真机再取出证书中的公钥验证App及其签名B的合法性;
  4. 最后,真机会检查自己的UDID是否在PP文件的Devices列表中,如果存在,才开始安装App。

⚠️:图里简化了验签的过程,相信你已经很熟悉这个过程了,哈希混淆、公钥解密、判等...

其实,签名的验证并非一次性完成,在安装、启动和运行时有着不同的校验规则,上面简化了该过程,具体可参考细说iOS代码签名(四):签名校验、越狱、重签名

正式包

而安装正式包时,真机对其的验签过程简化了很多,因为对开发者证书的验签过程交给了App Store

1)将发布包上传到App Store。

  1. 当你将发布包(测试包的一种)上传到App Store时,Apple官方同样会对发布包进行验签,其过程类似上述测试包的验签过程;
  2. 验签通过后,App Store会重新对App进行签名,这里用的就不是开发者证书对应的私钥了,而是CA 私钥;
  3. 最后,App Store只会对App和新的签名进行打包,生成.ipa包。(⚠️:不包含PP文件了)

2)真机设备安装正式包。

当设备从 App Store上下载 App 后,只需要用系统内置的CA公钥对App进行验签,验证通过即可安装。

练习一下

网上也有一些画得很好的图,你们可以按照序号回顾一遍过程,用来巩固本文的内容。

来自iOS证书那些事儿——掘金。

来自iOS App 签名的原理——Blog。

回到:本文目标

最后,我们再回到文章开头目标里提出的问题,解决那些问题之前永远铭记两个东西:包含私钥的证书(.p12 = .cer + private key)、PP文件(.mobileprovision)。

再看看我们的问题,只要找到上面两个东西就可以了:

  1. 如何在真机上跑自己的iOS项目,或者iOS开源库?
    1. 包含私钥的证书:参考iOS证书申请方式那一节;
    2. PP文件:在开发者平台上申请;或者由Xcode自动生成,Targets > Signing & Capabilities > 勾选Automatically manage signing。
  2. 如何在真机上跑公司的iOS项目?
    1. 包含私钥的证书:向团队索要,注意文件后缀是.p12;
    2. PP文件:向团队索要,记得让负责人在开发者平台上添加自己真机的UDID到PP文件中。

PS:

  • 运行测试包安装的App时,一般还需要在真机上信任证书:Settings > General > VPN & Device Management。
  • Xcode自动生成PP文件:

参考资料

整体把握:iOS 证书幕后原理——Blog

深度理解:细说iOS代码签名——Blog

其它你可能感兴趣的: