学计算机的那个

不是我觉到、悟到,你给不了我,给了也拿不住;只有我觉到、悟到,才有可能做到,能做到的才是我的.

0%

iOS App签名原理

知道为什么iOS系统可以使用3年,还能保证系统的流畅度么?除了苹果定制的CPU领先业界两年的水平,软件层面,苹果通过签名机制控制了安装在iOS上的APP都是经过苹果官方允许的,这样可以规范控制App在iOS的行为。

数字签名

数字签名的作用是我对某一份数据打个标记,表示我认可了这份数据(签了个名),然后我发送给其他人,其他人可以知道这份数据经过我认证的,数据没有被篡改过。

  1. 首先使用摘要算法,算出原始数据的摘要
  2. 生成一份非对称加密的公钥和私钥,私钥我自己拿着,公钥发布出去
  3. 私钥加密摘要,得到一份加密后的数据,称为原始数据的签名,把它跟原始数据一起发送给用户
  4. 用户收到数据和签名后,用公钥解密签名得到摘要,同时用户使用同样的算法计算原始数据的摘要,对比这里计算出来的摘要是否相等,从而确保这份数据的一致性。

苹果的APP签名

苹果官方生成一对公私钥,在iOS里内置一个公钥,私钥由苹果后台保存,上传App上AppStore时,苹果后台用私钥对APP数据进行签名,iOS系统下载这个APP后,用公钥验证这个签名,弱正确,这个APP肯定是由苹果后台认证的,并没有被修改过,从而保证安装的每一个APP都是经过苹果官方允许的。

但是除了从AppStore下载,我们还可以有三种方式安装一个App:

  1. 开发App时可以直接把开发中的应用安装进手机进行调试
  2. In-House 企业内部分发,可以直接安装企业证书签名后的APP。
  3. AD-Hoc相当于企业分发的限制版,限制安装设备数量,较少用。

双重签名

开发时安装APP,有两个需求:

  1. 安装包不需要传到苹果服务器,可以直接安装到手机上
  2. 苹果必须对这里的安装有控制权,包括经过苹果允许才可以安装,不能被滥用导致非开发的app也能被安装。
  1. 在Mac开发机器生成一对公私钥,公钥L(用来获取证书),私钥L
  2. 苹果自己的一对固定公私钥,公钥A(在每个iOS设备上),私钥A(在苹果后台)
  3. 把公钥L传到苹果后台,用苹果后台里的私钥A去签名公钥L,得到一份数据包含了公钥L以及其签名,这份数据称为证书
  4. 开发时,编译完一个APP后,用本地的私钥对这个APP进行签名,同时把第三步得到的证书一起打包进APP里,安装到手机上。
  5. 在安装时,iOS系统取得证书,通过内置的公钥A,去验证证书的数字签名是否正确。
  6. 验证证书后确保公钥L是经过苹果认证的,再用公钥L去验证APP的签名,这里就间接验证了这个APP的安装行为是否经过苹果官方允许。(这里只验证安装行为,不验证APP是否被改动,因为开发阶段APP内容总是不断变化)

避免被滥用

1.限制在苹果后台注册过的设备才可以安装
2.限制签名只能针对某一个具体的APP
3.APP里iCloud,push,后台运行权限如何控制,苹果把这些开关统一称为Entitlements,也需要通过签名去授权。

分析:
双重签名中,可以看到步骤3中,证书中只有公钥L,这里可以通过添加允许安装的设备 ID 列表 和 App对应的 AppID 等数据,一起组成证书,再用苹果私钥A对这个证书进行签名,在第5步,验证时可以拿到设备ID列表,判断当前设备是否符合要求,根据数字签名的原理,只要数字签名通过验证,第 5 步这里的设备 IDs / AppID / 公钥 L 就都是经过苹果认证的,无法被修改,苹果就可以限制可安装的设备和 APP,避免滥用。

实际上一个“证书”本来就有规定的格式规范,于是苹果发明了Provisioning Profile,里面包含证书,以及上述提到的所有额外信息,以及所有信息的签名。

完整流程如下:

  1. 在Mac开发机器生成一对公私钥,公钥L(用来获取证书),私钥L
  2. 苹果自己的一对固定公私钥,公钥A(在每个iOS设备上),私钥A(在苹果后台)
  3. 把公钥L传到苹果后台,用苹果后台里的私钥A去签名公钥L,得到一份数据包含了公钥L以及其签名,这份数据称为证书
  4. 在苹果后台申请AppID,配置好设备ID列表,和APP可使用的权限,再加上第3步的证书,组成的数据用私钥A签名,把数据和签名一起组成一个Provisioning Profile文件,下载到本地Mac开发机。
  5. 开发时,编译完一个APP后,用本地的私钥L对这个APP签名,同时把第4步得到的Provisoning Profile文件打包进APP里,文件名为embeded.mobileprovison
    6.在安装时,iOS取得证书,通过系统内置的公钥A,去验证embedded.mobileprovison的数字签名是否正确,里面的证书签名也会再验一遍。
    7.确保了 embedded.mobileprovision 里的数据都是苹果授权以后,就可以取出里面的数据,做各种验证,包括用公钥 L 验证APP签名,验证设备 ID 是否在 ID 列表上,AppID 是否对应得上,权限开关是否跟 APP 里的 Entitlements 对应等。

概念和常见操作

  1. 第 1 步对应的是 keychain 里的 “从证书颁发机构请求证书”,这里就本地生成了一对公私钥,保存的 CertificateSigningRequest 就是公钥,私钥保存在本地电脑里。
  2. 第 3 步对应把 CertificateSigningRequest 传到苹果后台生成证书,并下载到本地。这时本地有两个证书,一个是第 1 步生成的,一个是这里下载回来的,keychain 会把这两个证书关联起来,因为他们公私钥是对应的,在XCode选择下载回来的证书时,实际上会找到 keychain 里对应的私钥去签名。这里私钥只有生成它的这台 Mac 有,如果别的 Mac 也要编译签名这个 App 怎么办?答案是把私钥导出给其他 Mac 用,在 keychain 里导出私钥,就会存成 .p12 文件,其他 Mac 打开后就导入了这个私钥
  3. 第 5 步 XCode 会通过第 3 步下载回来的证书(存着公钥),在本地找到对应的私钥(第一步生成的),用本地私钥去签名 App,并把 Provisioning Profile 文件命名为 embedded.mobileprovision 一起打包进去。这里对 App 的签名数据保存分两部分,Mach-O 可执行文件会把签名直接写入这个文件里,其他资源文件则会保存在 _CodeSignature 目录下。

一些概念:
证书:内容是公钥或私钥,由其他机构对其签名组成的数据包。
Entitlements:包含了 App 权限开关列表。
CertificateSigningRequest:本地公钥。
p12:本地私钥,可以导入到其他电脑。
Provisioning Profile:包含了 证书 / Entitlements 等数据,并由苹果后台私钥签名的数据包。

参考

  1. iOS App 签名的原理