Bo2SS

Bo2SS

iOS | 图解iOS签名背后的原理

image

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

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

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

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

本文目标#

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

  1. 如何在真机上跑自己的 iOS 项目,或者 iOS 开源库?
  2. 如何在真机上跑公司的 iOS 项目?

另外,本文的终极目标是:遇到任何 iOS 签名相关问题时,你都能够快速解决。

预备知识:数字签名 & 数字证书#

在聊 iOS 签名之前,我们先需要了解两个预备知识,那就是在互联网世界里的签名✒️和证书📄。

可参考:数字签名和数字证书是什么?—— 阮一峰

数字签名#

数字签名一般夹带在要传输的数据中,用来防止数据被篡改。

它的底层核心是哈希混淆算法非对称加密技术(公 / 私钥)。

生成#

签名 Signature 的生成由通信中的发送方 Sender 进行,首先对要传输的数据 Data 进行哈希 Hash 混淆得到数据摘要 Digest,然后用私钥 Private Key 对摘要进行加密,这样就生成了数据的签名。

image

验证#

接收方 Receiver 接收来自发送方的数据和签名,对它们分别做如下处理:

  • 数据:使用与发送方相同的哈希算法对数据进行混淆,得到数据摘要 A;
  • 签名:利用发送方加密所用私钥对应的公钥 Public Key,对签名进行解密,得到数据摘要 B。

比对摘要 A 和摘要 B,如果相等,则说明数据没有被篡改,否则数据存在问题。

image

签名的生成和验证过程合在一起如下图所示,自己再分析一遍可加深理解。

image

带着问题往下走

Q1: 猜想一下 iOS 签名和验签的过程,App 开发者和 App 使用者中,谁是发送方,谁是接收方,传输的数据又是什么呢?

A1: App 开发者是发送方,App 使用者是接收方,传输的数据就是 App 的安装包。

Q2: 接收方如何拿到解密所用的公钥呢?

请接着往下看。

数字证书#

在了解数字证书之前,我们可能马上想到的是,发送方在发送数据和签名的同时,再附带上公钥不就万事俱备了吗~如下图所示:

image

但是这样会引入一个新的问题

Q1: 接收方如何确认公钥没有被其他人恶意替换呢?也就是公钥的身份不明。

这时候数字证书就该登场了。

组成内容#

我们可以先看一下数字证书的组成内容,它由公钥、公钥的身份信息 Identity Info 以及它们的签名 B 组成。

image

注意:加密公钥数据所用的私钥又是另外一个公私钥组合了,它是由权威的认证机构(Certificate Authority,CA)颁发的。

公钥包装在数字证书中传递#

这下,原来发送方发送的内容由数据 + 签名 +公钥,变成了数据 + 签名 +证书

image

证书里包含了接收方解密签名 A 所需的公钥,除此之外,证书里还有公钥的身份信息和签名 B:

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

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

image

注意:解密证书里签名 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 已经在信任链的顶端啦,它自己说了算。

image

回到上一个问题:如何保证这个 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 上把证书下载到电脑上,安装证书后电脑会自动关联对应的私钥。

image

申请方式: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...:

image

b) 输入身份信息(邮箱、证书名字),可以选择保存到本地,本地就得到了一个 CSR (.certSigningRequest) 文件,里面包含了公钥及其身份信息。

image

你可能会问,那私钥放在哪里呢?如果你细心的话,你会发现 Keychain > login > Keys 里面已经多了一对你命名的公私钥对。

c) 登陆Apple Developer网站,进入 Certificates, Identifiers & Profiles 板块,上传刚刚生成的 CSR 文件,即可生成证书(.cer 文件),此时将证书下载到本地,双击即可导入 Keychain,在 Keychain > login > May Certificates 中可以看到该证书。

image

如果你细心的话,你会发现 Keychain 里该证书已经和一把私钥绑定起来了,如果想把证书共享给其它开发者使用,则需要右键导出我们熟悉的.p12 文件,它包含了证书和对应的私钥~

2)Xcode 自动申请方式(推荐)#

这种方式则不需要繁琐的上传 CSR 文件、下载.cer 证书过程,也不会强求你加入 Apple 开发者计划。

a) 在 Xcode 里登陆 Apple 账号:Xcode > Preference > Account > Apple IDs > 「+」。

image

b) 登陆片刻后,你就会发现 KeyChain 里自动多出一份相应的证书了。

image

注意:如果你加入了开发者计划,Apple 开发者网站上也会自动添加该证书。

iOS 签名 & 打包原理#

终于来到 iOS 签名部分了,前面的内容你理解的怎么样呢?如果你还有点迷糊,那么记住签名的作用是防止传输的数据被篡改这一点,你就基本掌握本文的真谛了!

其实,在真正 iOS 签名的时候,还不是只附加证书(含公钥)这一个东西,我们还需要对证书做一层包装,那就是我们熟悉的 Provisioning Profile 文件,又叫 PP 文件、描述文件、供应配置文件。

PP 文件#

先来了解一下这个重要的 PP 文件,我们可以把它理解为证书的升级版。

如何生成#

  1. 开发者平台上申请;
  2. Xcode 自动生成:Xcode > Targets > Signing & Capabilities > 勾选 Automatically manage signing。

文件结构#

image
  • 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 文件预览:

image

签名 & 打包#

iOS 签名和打包过程其实是由 Xcode 来把控的,看下图:

image

  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 内容没有被篡改。

image

具体可参考细说 iOS 代码签名 (三):签名的过程及代码签名的数据结构

iOS 验签原理#

说完了 iOS 签名,那真机在安装 App 时如何验证它的签名呢?

首先,我们需要知道的是,App 安装包分为测试包正式包,两者的验签过程有所不同。

顺便补充一下测试包和正式包的知识:

1)测试包:分为内测包、准备上传 App Store 的发布包、Ad Hoc 发布包、In-house 企业内部发布包 4 种类型。

2)正式包:上传到 App Store 上的安装包。

⚠️:Xcode 打的包都属于测试包。

参考iOS 不同类型测试包介绍—— 搜狗测试公众号。

测试包#

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

image

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

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

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

正式包#

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

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

image

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

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

image

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

练习一下#

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

image

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

image

来自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 文件:

image

参考资料#

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

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

其它你可能感兴趣的:

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。