iOS App 签名原理

3,266 阅读19分钟

笔者接触打包已经一段时间了,但一直对签名都是似懂非懂,最近从加密数论知识起回看这部分知识,感觉还是有很多不懂的地方。

先简单说明一哈数学原理,然后说RSA 算法密钥生成的步骤,最后回到 iOS 签名打包,以及分享一哈利用重签名做过的坏事(以学习、省时间为目的)。

非对称加密

对称加密是通过同一份密钥加密和解密数据;而非对称加密则有两份密钥,分别是公钥和私钥,用公钥加密的数据,要用私钥才能解密,用私钥加密的数据,要用公钥才能解密。

(1)乙方生成两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。 (2)甲方获取乙方的公钥,然后用它对信息加密。 (3)乙方得到加密后的信息,用私钥解密。


数学原理

数学原理太长(难)不看篇:有一条等式,三个数字可以关联起来,从而达到互相验证对方的数字是否配对。如果已知数字验证等式很容易,但是如果未知这些数字,要推测出来对方的数字目前的技术还十分艰难。

互质的简单结论

关于互质关系,我们不难得出以下结论:

  1. 任意两个质数互质。

  2. 一个数是质数,另一个数只要不是它的倍数,两数互质。

  3. 一个质数与比它小的数,两数互质。

  4. 1 和任意自然数互质。

  5. p 是大于1的整数,p 和 p - 1互质。

  6. p 是大于1的奇数,p 和 p - 2互质。

欧拉函数

思考下面这个问题。

任意给定正整数n,请问在小于等于n的正整数之中,有多少个与n构成互质关系?

计算这个值的方法就叫做欧拉函数 ,以φ(n) 表示。

  • 如果n = 1,则φ(1) = 1 。

  • 如果n是质数,则 φ(n)=n-1 。由以上结论(3)得出。

  • 如果 n = p^k (p为质数,k为大于等于1的整数),那么

image.png

因为 n 的因数只有 1、p、n,所以只要一个数因数分解不包含质数 p,才可能与 n 互质。

而比p小,包含质数 p 的数有 1 *p、2 * p、3 * p、...、p^(k-1)×p,共有 p^(k-1) 个。减去即可。

比如 φ(8) = φ(2^3) =2^3 - 2^2 = 8 -4 = 4。

  • 如果 n 可以分解成两个互质 的整数之积。

  • n = p1 * p2

网上有种证明是“中国剩余定理”,看不懂🙈。。。说说自己的理解。

比如 35 = 5 * 7。由于35 的因数只有1、5、7、35。对于比35小的数来说,有7个5的倍数,5个7的倍数,则有12个,由于35是重复计算的,则有11个。所以结果是 35 - 24 = 11。

**φ(n) = p1 * p2 - p1 - p2 + 1 = (p1 - 1)(p2 -1) = φ(p1)φ(p2) **

  • 因为任意大于1的正整数,都可以由一系列质数相乘所得。

image.png

根据第四条,注意是要互质的数 ,可得

image.png

再根据第三条,可得

image.png

image.png

这就是欧拉函数 。

欧拉定理

欧拉定理是RSA算法的核心。理解了这个定理,就可能可以理解RSA。

直接给结论。感兴趣的可以自己去看证明(我也想看懂,但懵懵懂懂)。

image.png

即 a的φ(n)次方被n除的余数为1。

这个定理结合取余分配率可以用来简化幂的模运算。

(ab)%c=(a%cb%c)%c

image.png

费马小定理。

假设正整数 a 与质数 p 互质,n = p的情况下,由结论(3)可得:

image.png

image.png

模反元素

如果 a 和 n 互质,那么一定可以找到整数 b,使得 ab - 1 被 n 整除。这时,b 就叫做 a 的“模反元素”。不难发现,如果b是模反元素,那么b + a、b - a也是模反元素,模反元素不止一个 。

image.png

欧拉定理可以用来证明模反元素必然存在。

image.png

密钥生成的步骤

假设龙神要与那个ta 进行加密通信,他要怎么做才能瞒过八卦的群众呢?

第一步,随机选择两个不相等的质数 。

龙神的幸运数字是61 那个ta 的幸运数字是53。

第二步,相乘。

n = 61 * 53 = 3233

写成二进制是 110010100001,一共有12位,所以这个密钥是12位。(通常1024位,更安全的场合2048位)。

**第三步,计算n的欧拉函数φ(n)。 **

φ(n) = (p-1)(q-1),φ(3233) = 3120。

**第四步,随机选择一个整数e,条件是1< e < φ(n),且e与φ(n) 互质。 **

龙神选了17。

第五步,计算e对于φ(n) 的模反元素d。

ed ≡ 1 (mod φ(n))

等价于

ed - 1 = kφ(n)

ex + φ(n)y = 1

17x + 3120y = 1

龙神算出一组整数解为(2753,-15),即 d = 2753。

第六步,将 n 和 e 封装成公钥,n 和 d 封装成私钥。

所以公钥(3233,17),私钥(3233,2753)。

第七步, RSA 的可靠性。

回顾上面的步骤,一共出现了六个数字。

p = 61 q = 53 n = p * q = 3233 φ(n) = (p-1)(q-1) = 3120 e = 17 d = 2753

我们上面提过,公钥是公开的,所以 n 和 e 都是所有人能知道的。关键是d,如果泄漏了,就等于私钥泄漏。

那么,在已知 n 和 e 的情况下,怎么推导出 d?

(1) ed≡1 (mod φ(n))。只有知道e和φ(n),才能算出d。

(2) φ(n)=(p-1)(q-1)。只有知道p和q,才能算出φ(n)。

(3) n=pq。只有将n因数分解,才能算出p和q。

所以,只要因数分解n,就可以得到d。

但是,对于一个很大的质数,要因数分解是非常困难的事 。

所以,以目前的技术来看, RSA 是可靠的 。

加密和解密

公钥:e 和 n 私钥:d 和 n 明文:m 密文:c

公钥和私钥通过一条等式能互相验证。有兴趣的可以自己研究。

RSA算法原理(二)

image.png

加密

假设龙神想说的悄悄话m,他就要用公钥(3233,17)进行一次加密。这里必须注意,m是小于n的整数。

然后算出下面式的c:

m^e ≡ c (mod n)

假设龙神说的悄悄话是 65,算出c 为2790。

我们要用到欧拉定理

65^17 mod 3233 = 2790

所以龙神就把2790发给那个ta。

解密

那个ta 收到2790后,就用私钥(3233,2753)进行解密。

c^d ≡ m (mod n)

2790 ^ 2753 ≡ 65 (mod 3233)

所以原文就是65。

RSA 的优缺点

优点: 目前来说安全。

缺点:

  • 速度慢

  • 只适合加密小数据


简单介绍完一番我没完全理解的数学理论后,下面进入签名环节。

数字签名

作用

数字签名的作用是利用非对称加密方法,自己持有私钥,公布公钥。互相加密以及验证消息来源。

步骤

  1. 首先用一种算法,算出原始数据的摘要。需满足

    a. 若原始数据有任何变化,计算出来的摘要值都会变化。

    b. 摘要要够短。这里最常用的算法是MD5 。

  2. 生成一份非对称加密的公钥和私钥,私钥保留,公钥公布出去。

  3. 对一份数据,算出摘要 后,用私钥加密 这个摘要,得到一份加密后的数据,称为原始数据的签名。把它跟原始数据一起发送给用户。

  4. 用户收到数据和签名后,用公钥解密得到摘要。同时用同样的算法计算原始数据的摘要,对比这里计算出来的摘要和用公钥解密签名得到的摘要是否相等,若相等则表示这份数据中途没有被篡改过,因为如果篡改过,摘要会变化 。

image.png

最简单的签名

image.png

iOS 设备用公钥验证一遍就知道该 App 是不是经过苹果后台认证的。如果被篡改过,肯定是不行的。

然而,App 除了从 AppStore 下载,还有三种方式安装:

  1. 开发 App 时可以直接把开发中的应用安装进手机进行调试。

  2. In-House 企业内部分发,可以直接安装企业证书签名后的 APP。

  3. AD-Hoc 相当于企业分发的限制版,限制安装设备数量,较少用。

所以苹果的签名还有额外的事情要做。

苹果的 App 签名

我们来考虑上面第一种情况。

首先,它既要有能不经苹果私钥验证,马上安装的便利,又要经过苹果验证。

听起来有点绕。苹果采用的方案是双层签名。

image.png

  1. 在 Mac 生成一对公私钥,这里称为公钥L私钥L

  2. 苹果自己有固定的一对公私钥,私钥在苹果后台,公钥在每个 iOS 设备上。

  3. 把公钥 L 传到苹果后台,用苹果后台里的私钥 A 去签名公钥 L。得到一份数据包含了公钥 L 以及其签名,把这份数据称为证书 。

  4. 在开发时,编译完一个 APP 后,用本地的私钥 L 对这个 APP 进行签名,同时把第三步得到的证书一起打包进 APP 里,安装到手机上。

  5. 在安装时,iOS 系统取得证书,通过系统内置的公钥 A,去验证证书的数字签名是否正确。

  6. 验证证书后确保了公钥 L 是苹果认证过的,再用公钥 L 去验证 APP 的签名,这里就间接验证了这个 APP 安装行为是否经过苹果官方允许。(这里只验证安装行为,不验证APP 是否被改动,因为开发阶段 APP 内容总是不断变化的,苹果不需要管。)


除了要保证经过验证外,苹果还要防止滥用这种途径下载。

想象一下,把真机连到 Mac 上,不就能想装多少装多少 App 吗?那多部设备连到 Mac 上,App 不就能想装在哪装在哪吗?

苹果的方案是识别 设备和 App

想调试的设备必须要到开发者网站申请,并且设备数量是有限制的。

然后还针对每个Bundle Identifier(App ID)配不同的证书。

这两种数据都在上面第三步一起组成证书(暂且这么认为)。

至此,证书就能实现 经过苹果认证,并且能限制安装设备数量和 App ID 。

当然,证书不止有这些信息,还有推送等权限,苹果把这些权限开关统一称为Entitlements 。如果App 一开始的证书没申请推送权限,那么后面新增权限后,需要更新配置。

实际上,一个“证书”有规定的格式规范,不应把这些额外的信息往里塞。所以上面的暂且这么认为部分是不对的。

所以苹果把证书和额外信息包装起来 ,把它叫做 Provisioning Profile 。

所以能拓展整个流程图如下。

image.png

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

关于证书等概念

上面的步骤对应到我们平常具体的操作和概念是这样的:

  1. 第 1 步对应的是 keychain 里的 “从证书颁发机构请求证书”,这里就本地生成了一对公私钥,保存的 CertificateSigningRequest 就是公钥,私钥保存在本地电脑里。
  2. 第 2 步苹果处理,不用管。
  3. 第 3 步对应把 CertificateSigningRequest 传到苹果后台生成证书,并下载到本地。这时本地有两个证书,一个是第 1 步生成的,一个是这里下载回来的,keychain 会把这两个证书关联起来,因为他们公私钥是对应的,在XCode选择下载回来的证书时,实际上会找到 keychain 里对应的私钥去签名。这里私钥只有生成它的这台 Mac 有,如果别的 Mac 也要编译签名这个 App 怎么办?答案是把私钥导出给其他 Mac 用,在 keychain 里导出私钥,就会存成 .p12 文件,其他 Mac 打开后就导入了这个私钥。
  4. 第 4 步都是在苹果网站上操作,配置 AppID / 权限 / 设备等,最后下载 Provisioning Profile 文件。
  5. 第 5 步 XCode 会通过第 3 步下载回来的证书(存着公钥),在本地找到对应的私钥(第一步生成的),用本地私钥去签名 App,并把 Provisioning Profile 文件命名为 embedded.mobileprovision 一起打包进去。这里对 App 的签名数据保存分两部分,Mach-O 可执行文件会把签名直接写入这个文件里,其他资源文件则会保存在 _CodeSignature 目录下。 第 6 – 7 步的打包和验证都是 Xcode 和 iOS 系统自动做的事。
  • CSR:Certificate Singing Request,证书签名请求文件。

包含电脑的公钥信息。所以创建时不需要填任何和发布等有关的信息。

  • Certificates证书:发布者证书。Apple Develop的ID 对某部电脑的授权证书。

内容是公钥或私钥,由其他机构对其签名组成的数据包。

电脑拥有这个证书后,有权对该Apple Developer的ID下所有App进行真机测试、打包、发布。注意这里,并未指定App,换句话说,和App无关。

  • CSR 和 Certificates 的联系

上面提到了Certificates包含了电脑的信息,这个信息来自于CSR。所以在创建Certificates时,需要提交CSR。相当于 Mac 的公钥被苹果私钥 加密的过程。

  • p12: 本地私钥,可以导入到其他电脑。

上面提到,拥有证书才有权做那些事。如果另一部电脑想发布,也需要证书。如果又创建一个新证书也能解决,但一般一个开发者帐号创建一个发布证书就够了,而且苹果对这证书数量有限制。这时候导出p12文件,相当于拷贝了一份私钥。给另一部电脑安装后,另一部电脑就有权了。

  • Entitlements:包含了 App 权限开关等信息。

  • Provisioning Profile: 描述文件。包含了证书、App ID、设备、Entitlements 等数据,并由后台私钥签名的数据包。也称为PP文件,.mobileprovision后缀文件。

小结

CSR文件包含本地公钥,被苹果私钥加密后生成 Cer 证书。该证书与权限、App ID、设备等数据经苹果私钥加密后生成 PP文件。装到真机时,会对 PP文件整体进行验证,还会对 PP 文件中的 Cer 证书进行验证。


苹果的 App 验证

上面说了开发包的验证流程

实际上 App 安装以及每次启动都会验证。

各种证书的有效期

企业帐号发布证书有效期是3年,而开发证书有效期为1年,而描述文件开发发布都是只有1年有效期。

个人帐号开发证书发布证书有效期都是1年,描述文件也全是1年有效期。


下面再说说企业包 和 AD - Hoc以及 App Store的验证流程。

企业包和 AD - Hoc包验证流程

企业包和 AD -Hoc 的区别在于企业包不会限制安装设备数量,并且需要信任证书。

因为这两种包不经过 App Store,对App的签名是用证书签名的。所以证书经苹果发布后,苹果还想要限制的话,就必须检查证书是否过期。这个步骤被放到了启动 App 时。

因此,如果证书过期或者 revoke掉,开发者账号被封禁,都会导致 App 启动时闪退

  1. 证书过期或 revoke
  • app 会马上不能使用,并且由于 PP 文件包含它,也会失效。
  1. PP 文件过期 或 revoke
  • 也会闪退,但可能不会马上反应过来,可能由于网络原因等,不能马上验证失效。
  1. 账号被注销
  • 闪退

App Store 的验证流程

blog.cnbang.net/wp-content/…

其实就是最简单的验证流程。

我们上传 App 到 App Store时验证我们的证书、PP文件。从此,与我们的文件再无关系。

上传后,苹果在后台直接用私钥签名 App 就可以了。如果去下载一个 AppStore 的安装包,会发现它里面是没有 embedded.mobileprovision 文件的,也就是它安装和启动的流程是不依赖这个文件,验证流程也就跟上述几种类型不一样了。

已经在苹果商店下载安装的app不受影响(无论是过期还是Revoke,甚至是开发者账号被注销,因为这个时候,对于app的签名,是通过苹果私钥直接签名的,没有使用开发者名下的私钥签名)。

但App Store 会下架相关的 App。


笔者之前遇到一个场景就是,违规操作被注销是没办法的(注意保护开发者账号,可以开启双重验证,不要被偷去做马甲包然后被举报了)。那么对于企业包证书过期问题怎么处理呢?

因为有效期为1年,我们可以申请两个 PP 文件,相隔半年,差不多到时间发新版时就换个新 PP 文件。

更多可以看iOS 各种证书的作用、有效期、过期的后果和解决办法

重签名

正常的打包流程就不再说了。iOS完整的证书申请和打包过程

对 ipa 包进行修改后,由于摘要变了,所以验证会不通过。我们可以采用重签名达到验证的效果。常用于逆向别人的 App,微信多开等途径。

App 的大致结构

打包出来 ipa 后,我们解压可以看到大致结构。

image.png

  • 资源文件:例如图片、html等等

  • CodeSignature/CodeResources:这是一个 plist 文件,可用文本查看,其中的内容就是程序包(不包括Frameworks)所有文件的签名。意味着你的程序一旦签名,就不能更改其中任何文件。

  • 可执行文件:此文件跟资源文件一样需要签名。

  • mobileprovision:校验证书文件、Bundle ID。

  • Frameworks:程序引用的系统自带的Frameworks,每个Frameworks其实就是一个 app,也包含签名信息。

iOS 系统验证签名有效性的过程

  1. 解压ipa

  2. 取出 embedded.mobileprovision,校验是否被篡改过

  3. 校验所有文件的签名

  4. 验证设备是否符合embedded.mobileprovision 的信息

  5. 对比 Info.plist 的 Bundle Id 是否符合 embedded.mobileprovision  文件中的信息

重签名的原理

既然签名是由证书mobileprovision共同实现,那么重签名的过程其实就是将新的证书mobileprovision替换旧文件的过程,但由于系统在验证app是否合法的时候还会随机验证授权设备列表和Bundle ID、所以必须修改Bundle ID新的mobileprovision中的信息保持一致,否则将会有验证失败的风险。

重签名的尝试

笔者之前打的是企业包,针对不同客户要实现不同 App Icon 和 App name等。如果一个一个打,一个打7分钟。💩

利用重签名,可以快速替换 ipa 中的内容,实现装逼的效果。

步骤如下

  1. 打包出一份 ipa。

  2. 修改 ipa 内对应文件。此时摘要变化了,相当于签名失效,苹果校验时就知道该 app 被篡改 了。

  3. 重签名

  • 3.1 删除插件
  • 3.2 对FrameWorks进行签名
  • 3.3 给可执行文件执行权限
  • 3.4 拷贝描述文件
  • 3.5 修改info.plist中的Bundle ID
  • 3.6 生成plist权限文件
  • 3.7 签名整个APP
  • 3.8 生成ipa包

具体实现,更多可查看之前写过的一篇文章 iOS —— 两套自动打包脚本

参考