转自 http://xelz.info/blog/2019/01/11/ios-code-signature-1/,版权归原作者所有
导航
- 一口气读完,大约需要40-60分钟
- 分步阅读
- 细说iOS代码签名(一):签名的作用及原理
- 细说iOS代码签名(二):开发者证书、Entitlements、Provisioning Profile
- 细说iOS代码签名(三):签名的过程及代码签名的数据结构
- 细说iOS代码签名(四):签名校验、越狱、重签名
0x01 签名的作用
数字签名其实跟我们手写的签名类似,代表一个特定的主体(签名者)对特定内容(被签名数据)的署名和认可,签名是对信息发送行为真实性的有效保障。数字签名在很多领域都有应用,iOS的代码签名正是其中最典型的一种,我们可以先尝试分析一下iOS上代码签名的目的和好处。
安全性
代码签名的首要任务是保证设备及系统的安全性,只有被苹果设备认可的证书签名的代码才能够被执行,否则在安装或者运行时会因为无法通过内核的签名校验而失败。iOS的系统中内置了来自苹果的CA证书,系统自身的代码都是被苹果”签名“过的, 而用户从AppStore下载的App也都已被苹果官方进行签名。签名机制可以有效地防止来自外部的攻击。
这里存在两种场景:
- 第一种是对系统本身的攻击,比如越狱,假如黑客发现了内核任意读写的漏洞,借此注入提权代码,但是这些代码会因为没有合法的签名而被系统拒绝运行,也就自然无法对系统造成实质性的破坏。
- 第二种是对设备或者用户的攻击,众所周知,提交到AppStore的应用代码都会经过苹果的审查,包含恶意代码的App是无法上架的。此时,黑客可能会尝试先提交一个正常的App,通过各种技术手段躲避Apple的审查,上架后从网络上下载恶意代码并加载执行,但这种方式也会因为签名不合法而失败。
沙盒
除了能够避免非授权的恶意代码运行,代码签名还可以有效地限制app的行为,这部分功能主要是由Sandbox机制来保证,但Sandbox的配置是绑定在签名中的,就是通常所说的Entitlements文件。试想,如果Entitlements文件可以被任意修改,那么Sandbox也就失去了意义,所以Entitlements文件也是强制签名保护的对象。对于越狱来说,如果无法绕过签名和Sandbox,再强大的提权漏洞也无计可施。
垄断
代码签名还给苹果带来了一个巨大的好处:App分发的绝对控制权。在iOS平台上(面向未越狱的用户)公开发行App的合法途径有且只有一种,就是上传到苹果官方的AppStore供用户下载。苹果会对App进行严格的审查并签名,App的功能及支付渠道也因此可以受苹果的严格管制,这为苹果带来的经济效益不言而喻。
0x02 什么是签名
签名的本质是用于验证数据的合法性,确保被签名的数据来自特定的来源,并且未经篡改。它基于非对称加密,和哈希算法,研究签名之前需要对这两种算法有一定的了解。
公钥加密算法
也叫非对称加密,它在加密和解密时使用的是不同的密钥,具有这样的特征:
- 有一对密钥
a
和b
,满足a ≠ b
- 用密钥
a
加密的数据只能用b
进行解密,a
自身无法解密,反之亦然 - 只知道其中一个密钥,无法推导出另一个
- 把其中一个可以公开的叫做公钥,另一个不能公开的叫做私钥。
最常见的公钥加密算法是RSA公钥加密算法,也是签名中普遍使用的算法。其数学原理如下:
- 选定两个超大的素数
p
,q
,并计算他们的乘积n = p * q
- 计算欧拉函数
φ(n) = φ(p) * φ(q) = (p-1) * (q-1)
- 随机选定一个数
e
,满足1 < e < φ(n)
,且与φ(n)
互质 - 根据扩展欧几里得算法计算
e
对于φ(n)
的乘法逆元d
,e * d = 1 mod φ(n)
-
{n, e}
和{n, d}
分别组成这个算法的一对密钥 - 对于给定明文
p
, 若使用{n, e}
作为加密密钥,其密文计算方法为c = p ^ e mod n
- 这是一个
单向函数
,已知{c, n, e}
无法计算出p
- 这是一个
- 相应地需要使用
{n, d}
进行解密,p' = c * d mod n
- 这是上一步加密函数的
逆函数
- 这是上一步加密函数的
- 两组密钥中
n
是相同的,那么如果已知了e
和d
其中的一个,想要计算另一个,必须知道φ(n)
,也就是必须先将n
分解质因数
,得到p
和q
,但由于n
的值非常大,这样的计算量基本上是不可能
的,也就保障了算法的安全性
理论上 {n, e}
和 {n, d}
可以互换,任何一个都可以是公钥或者私钥,加密和解密的函数也可以互换。但实践中,一般固定设置e=65537(0x10001)
,相当于公开的一个约定,这样一来{n, e}
就只能作为公钥使用。
哈希算法
也叫散列或者摘要算法,对一段任意长度的数据,通过一定的映射和计算,得到一个固定长度的值,这个值就被称为这段数据的哈希值(hash)。给定一个哈希算法,它一定具有以下特征:
- 哈希值不同的两段数据绝对不同
- 相同的数据计算出的哈希值绝对相同
- 由于哈希值是固定长度, 也就意味着哈希值的数量是有限的。而任意数据都可以计算出一个哈希值,计算哈希的过程,相当于无限集到有限集的映射。因此哈希值相同,对应的原始数据不一定相同,如果不同,则称这两段数据存在
哈希碰撞
,实际应用中认为这是小概率事件(数学意义上的"不可能事件"),优秀的哈希算法都是碰撞率极低
的。 - 哈希算法是单向算法,无法通过哈希值,
计算
出原始数据,这一点非常重要!
常见的哈希算法有: md5, sha1, sha256等,其中sha1长度为160bits,而sha256长度为256bits,二者相比,sha256的取值范围更大,因此碰撞和破解的概率更低,也就相对更安全。
签名算法
有了上面这两种算法作为基础,就可以组建一个签名和验证签名的体系了,如下图所示
假如A
要给B
发送一段数据d
,先对其签名:
- 计算
d
的哈希值h
,并使用自己的私钥a
对h
进行加密,得到的密文c
就是签名
得到签名后,将数据d
和签名c
通过某种方式发送给B
,此时B
收到了数据d'
以及签名c'
,需要验证这段数据是否被篡改,以及是否是A
发送的
- 计算
d'
的哈希值h'
,使用A
的公钥b
将签名c'
解密,得到h''
。通过对比h'
和h''
是否一致,就可以知道数据或签名是否被篡改。并且,如果哈希值是匹配的,能够说明这段数据一定是由A
签名并发出的
常见的签名算法:
- sha1WithRSAEncryption:先对数据计算sha1摘要,再对摘要进行RSA加密
- sha256WithRSAEncryption:先对数据计算sha256摘要,再对摘要进行RSA加密
- md5WithRSAEncryption:先对数据计算MD5摘要,再对摘要进行RSA加密
证书
上面这个例子中,任何需要接受A
的消息的人都需要事先保存A
的公钥。这样的方案存在一个很大的问题:公钥如何分发?如果B
要接受来自很多不同来源的数据,不可能事先将所有来源的公钥都提前保存下来,并且这样无法适应来源变动(增加、删除、变更)等带来的变化。因此,一般会把公钥当做签名的一部分,随着数据一起分发,接收方不需要事先保存任何数据来源的公钥。
但是这样会引入一个新的问题:如何知道数据中所携带的公钥就是否是发送者自己的公钥?
这涉及到密钥的管理和分发,细节展开的话是一个非常大的课题。简单来说,可以把公钥和所有者的信息保存在一个文件里,并让一个可信的第三者使用其私钥对这个文件进行签名,得到一个签了名的公钥文件,这个文件就叫做证书
。证书会作为签名的一部分,随着数据一起分发。
这里出现了一个有意思的事情,数据签名中的证书本身也是一段数据(公钥+所有者信息)以及其签名组成的,但证书中的签名是简单签名,一般只有哈希值和签发者名称,不会再将签发者的证书包含在签名中,否则就陷入无限递归的死循环了。
此时我们还需要使用第三者的公钥验证这个证书的合法性。虽然需要多验证一步,但是这样一来,本地不再需要保存每个数据来源的公钥,只需要保存这个第三者的证书(公钥)即可,每个数据来源的证书都由这个可信的第三者进行签发,这个可信的第三者就被称为证书颁发机构(Certification Authority),简称CA
。
实际上,CA的证书可能也是由其他更高一级的CA进行签发的,这种情况会产生3级甚至3级以上的证书链,系统中只需要保存最高级CA的证书,中间CA的证书和信息提供者的证书依次进行递归校验即可。
可以通过这个命令导出Xcode应用中可执行程序的签名证书,mac OS上的代码签名格式与iOS平台是相同的
$ codesign -d --extract-certificates=cert /Applications/Xcode.app/Contents/MacOS/Xcode
当前文件夹下会产生三个证书文件cert0
cert1
cert2
。其中cert0是由cert1签发的,可以使用cert1验证其合法性,同理cert2可以验证cert1的合法性。而对于cert2,只需要对比系统的keychain中是否有相同的证书文件即可。通过下面的命令可以分别查看他们的所有者名称:
$ for i in 0 1 2; do openssl x509 -inform DER -text -noout -in cert$i | grep Subject:; done
Subject: CN=Apple Mac OS Application Signing, O=Apple Inc., C=US
Subject: C=US, O=Apple Inc., OU=Apple Worldwide Developer Relations, CN=Apple Worldwide Developer Relations Certification Authority
Subject: C=US, O=Apple Inc., OU=Apple Certification Authority, CN=Apple Root CA
本篇完。
下一篇: 细说iOS代码签名(二):开发者证书、Entitlements、Provisioning Profile