Jony's Blog

iOS App 签名原理

2018-07-23
JonyFang

iOS 之前,主流操作系统上的开发和运行软件不需要签名,软件从任何地方下载都可以运行,导致平台对第三方软件难以控制。所以,苹果希望能对 iOS 平台的第三方应用有绝对的控制权,已保证每一个安装到 iOS 上的应用都是经过苹果官方审核允许的。而保证的方式,即是通过签名机制的方式。

1.数字签名

数字签名的作用是,A对某一份数据进行标记,表示这一份数据是签了名的,之后发送给其他人,其他人可以知道这份数据是经过 A 认证的,数据没有被篡改过。过程如下图:

  • 首先通过一种算法,算出原始数据的摘要。

算法需满足的条件:若原始数据发生变动,计算出来的摘要值也会变化;摘要要短,常用 MD5。

  • 生产一份非对称加密的公钥和私钥。私钥保留,公钥向外部提供。
  • 对一份数据,算出摘要后,用私钥加密这个摘要,得到加密后的数据,称为原始数据的签名。将这份签名和原始数据一起发送给用户
  • 用户收到数据和签名,用公钥解密签名,得到摘要。同时,用户通过同样的计算方法计算得到原始数据的摘要,对比两份摘要是否相同。若相同,表示这份数据没有被篡改过;如果不同,表示这份数据是被篡改过的

第一步中,计算摘要的原因是,非对称加密的原理限制课加密的内容不能太大(一般不大于 1024位/2048位)。若要对任意大的数据签名,需要改成对它的特征值签名,效果是一样的。

2.最简单的签名实现

实现这个需求的直接方式,苹果官方生成一对公私钥,iOS 里内置一份公钥,苹果后台存放一份对应的私钥。当开发者提交 App 到 App Store 时,苹果后台用私钥对应用数据进行签名;iOS 系统下载这个 App 后,用公钥验证这个签名,如果签名一致,说明这个应用是被苹果官方后台认证的,并且没有被修改过。通过这种方式,来保证每一个应用都是经过苹果官方允许的。

如果 iOS 设备安装应用只有从 App Store 下载这一种方式,上面的签名验证过程即简单解决了问题。

但实际上,除了 App Store 下载,还可以有三种安装 App 的方式:

  • 开发中的应用,直接安装到手机进行调试
  • In-House 企业内部,可以直接安装企业证书签名后的应用
  • AD-Hoc 相当于企业分发的限制版,限制了安装设备数量

苹果需要对这三种安装应用的方式,进行控制,所以上述的方式还不能满足。

3.新的需求

开发是安装应用,涉及到两个需求:

  • 安装包不需要传到苹果服务器,可以直接安装到手机。
  • 苹果必须对安装有控制权。(经过苹果允许才可以安装;非开发中的应用不能被安装)

苹果对应的解决方式,采用双层签名的方式,流程如下图:

  • 在 Mac 开发机器中生成一对公私钥(公钥L & 私钥L)
  • 苹果有自己固有的一套公私钥,苹果后台有一份私钥,每个 iOS 设备有对应的一份公钥。(私钥 A & 公钥 A)
  • 把公钥 L 传到苹果后台,用苹果后台的私钥 A 去签名 公钥 L,得到一份包含了公钥 L 和签名的数据。这份数据就是证书
  • 开发时,编译完一个应用后,用本地的私钥 L 对这个应用进行签名,同时把上一步得到的证书一起打包进应用中,安装到手机
  • 安装时,iOS 取到证书,通过系统内置的公钥 A,去验证证书的数字签名是否正确
  • 验证证书后,确保了公钥L 是苹果认证过的,再用公钥 L 去验证应用的签名。这样就间接验证了这个应用安装行为是经过苹果官方允许的。(这里只验证安装香味,不验证应用是否被改动,因为开发阶段应用内容是在不断变动的,苹果不需要知道)

4.如何避免本地安装滥用问题

经过 4 的方案,解决了需要经过苹果允许才能安装应用的需求,但未解决避免滥用的问题。

为解决这个问题,苹果加了两个限制。一是限制苹果后台注册过的设备才能安装;二是限制签名只针对某一具体应用

4 的签名验证过程中,苹果用私钥 A 前本地公钥L 时,实际上除了签名公钥 L,还可以加上无限多的数据。这些数据都可以保证是经过苹果官方认证的,不会有被篡改的可能。流程如下图:

把允许安装的设备 ID 列表与 App 对应的 App ID 等数据,在步骤三跟公钥 L一起组成证书,再通过 私钥A 对证书进行签名。步骤5中,通过拿到的设备 ID 列表,判断当前设备是否符合要求。证书中的设备 IDs、AppID、公钥L 都经过苹果认证,无法被修改,这样就可以限制可安装的设备和 App,避免滥用。

5.最终流程

实际上,除了设备 ID、AppID,还有其他信息需要经过苹果的签名。如 App 中的 iCloud、push、后台运行等权限都会受到苹果的控制,这些权限开关统一被称为 Entitlements,也需要通过签名去授权。

实际上证书有规定的格式规范,不适合把所有的额外信息塞入证书中,所以苹果提供了 Provisioning Profile。一个 Provisioning Profile 里包含了证书、上述所有额外的信息、所有信息的签名。

整个流程的流程图:

  • 在 mac 开发机器生产一对公私钥(公钥L、私钥L L: Local)
  • 苹果自己有固定的一对公私钥,私钥在苹果后台,公钥在每个 iOS 设备中(公钥A、私钥A A:Apple)
  • 将公钥 L 传给苹果后台,通过后台的私钥A签名公钥L。得到一份证书,证书包含了公钥L及其签名
  • 在苹果后台申请 AppID,配置设备 ID 列表和App 可使用的权限,与上一步生成的证书组成一份数据包,通过私钥A签名,数据包与签名组成一份 Provisioning Profile 文件,下载到 mac 开发机
  • 开发过程中,App 编译完成之后,用私钥L对 App 进行签名。同时,将得到的 Provisioning Profile 文件一起打包进 App 中(embedded.mobileprovision),再将 App 安装到手机
  • 安装过程中,iOS 系统取到证书,通过系统内置的公钥A,验证安装包内 embedded.mobileprovision 的数字签名是否都是苹果授权
  • 确定了 embedded.mobileprovision 中数据是苹果授权后,取出里面的数据,进行各项验证(用公钥L验证 App 签名、验证设备 ID 是否在设备 ID 列表中、AppID 是否一致、权限开关是否与App内的 Entitlements对应 等)

6.概念和操作

6.1 日常操作的流程

  • 第一步:keychain 中“从证书颁发机构请求证书”,这个过程中,就是本地生成了一对公私钥,CertificateSigningRequest 即为公钥,私钥保存在本地电脑中
  • 第二步:将 CertificateSigningRequest 上传到苹果后台生成证书,并下载到本地。本地现在有两个证书(一个是上一步生成的,一个是这里下载的)。因为这两个证书的公私钥是对应的,keychain 会把两个证书关联起来。在 Xcode 选择下载回来的证书时,实际上会找到 keychain 里对应的私钥去进行签名。这里私钥只有生成它的 mac 有,如果其他的 mac 也要编译签名这个 App,需要将这台 mac 的私钥导出给其他 mac 使用。在 keychain 里导出私钥,存为 .p12 文件,其他 mac 打开即可导入这个私钥
  • 第三步:在开发者网站中配置 App ID、权限、设备列单 等信息,最后下载 Provisioning Profile 文件
  • 第四步:Xcode 通过第二步中下载回来的证书(附带公钥),在本地找到对应的私钥(第一步生成的),用本地私钥去签名 App,并将 Provisioning Profile 文件命名为 embedded.mobileprovision 一起打包入 App 包中。这里,App 的签名数据保存为两部分,Mach-O 可执行文件会将签名直接写入这个文件里;其他资源文件保存在 _CodeSignature 目录下
  • 之后的步骤即为 Xcode 和 iOS 系统进行打包和验证

6.2 一些概念

  • 证书:内容是公钥或私钥,由其他机构对其签名组成的数据包
  • Entitlements:包含 App 权限开关列表
  • CertificateSigningRequest:本地公钥
  • p12:本地私钥,可导出给其他 mac 使用
  • Provisioning Profile:包含证书、Entitlements 等数据,由苹果后台私钥签名的数据包

Similar Posts

Comments