本篇文章虽然是介绍iOS开发中ipa包的签名原理。但因为签名涉及到密码学中的概念。在了解签名之前,我们需要明确一些概念。密码学中,根据加解密密钥的不同,通常把加密方式分为对称密码(对称加密)和公钥密码(非对称加密)。常见加密算法有:DES、3DES、DESX、AES、RSA、ECC。其中RSA、ECC是非对称加密算法。以下是一些必要的概念。
对称密码:又叫做对称加密。加密和解密使用的是同一个密钥。
公钥密码:又叫做非对称加密。有一个公钥和一个私钥,公钥和私钥组成一个密钥对。使用私钥加密的数据可以使用公钥解密,反之亦然。
混合加密:同时使用对称加密和非对称加密两类算法。
消息摘要:通过单向散列函数对消息进行一定的运算,计算出固定长度的结果,就是消息摘要。
数字签名:用私钥对消息摘要(又叫哈希值、散列值)进行加密得到的密文就是数字签名
数字证书:按照一定格式将明文信息和消息摘要进行打包得到的文件就是证书。
中间人攻击: 中间人通过在网络中拦截并持有端到端的真实公钥,然后把自己的公钥转发给消息接收者。而后通过公钥拦截解析消息甚至篡改的一种攻击方式。
又叫做对称加密,一种加密和解密使用同一个密钥的加密算法。 即在对称密码中,密钥既可以对数据进行加密,又可以对数据进行解密。
对称加密的优点是加解密速度快。缺点是对称加密的算法的密钥在传输过程中和保存的安全性问题。因为加解密使用同一个密钥,一旦密钥被泄露,数据的隐私性和完整性将受到很大的威胁。
对称加密的常见算法有DES、3DES、DESX、AES。
公钥密码即我们常说的非对称加密,也称为公私钥加密。此类算法有一个公钥和一个私钥。公钥和私钥一一对应,共同组成一个密钥对,每个密钥对中的公钥和私钥是不同的。密钥对由网络中的通讯设备生成,通常是客户端或服务器。此处我们以PC客户端为例,PC客户端生成密钥对后,自己持有私钥,然后将公钥通过网络分发给其他PC客户端。公钥加密的数据需要私钥解密,反之亦然。公钥是公开的,可公开分发给其他PC客户端,但私钥只有密钥对生成者持有且不能泄露,一旦私钥泄露将会危及数据的安全。
非对称加密的缺点是加解密速度要远远慢于对称加密,在某些极端情况下,甚至能比非对称加密慢上1000倍。
公钥可公开,私钥需保密。
常见的非对称加密算法有RSA、Diffie-Hellman、ECC(移动设备使用)
上述我们简单了解了对称加密和非对称加密的概念和特点。对称加密中加解密使用的是同一个密钥,密钥在网络的配送过程一旦被非法窃取,数据的私密性和完整性无法得到保证。所以采用对称加密需要解决密钥的配送问题。常见的解决方案有:
● 通过事先共享密钥来解决
● 通过密钥分配中心来解决
● 通过 Diffie-Hellman 密钥交换来解决
● 通过公钥密码来解决
通过对称加密的特点,我们了解了对称加密的加解密速度快,但是存在密钥配送问题。非对称加密不存在密钥配送问题,但是加解密速度慢。结合这两类加密方式的特点,前辈们发明了对称加密+非对称加密的混合加密方式,即对称加密+非对称加密双重混合加密,称为混合密码系统。混合密码系统是将对称加密和非对称加密的优势相结合的方法。让我们来看看是如何实现的:
混合密码系统加密
会话密钥:本质上就是随机生成的对称密钥。消息发送方和接收方每次会话都会通过伪随机数生成器生成一个对称密钥,每次生成的对称密钥可能都不相同,这种想对称密钥称为会话密钥。
加密步骤:
消息接收方:生成非对称密钥对,把公钥发送给消息发送方
消息发送方:生成随机的会话密钥,本质就是对称密钥
消息发送方:使用对称密钥对消息进行加密
消息发送方:使用公钥对会话密钥进行加密从而生成会话密钥的密文
消息发送方:把用会话密钥的密文和用会话密钥加密过的消息一并发给消息接收方
解密步骤:
消息接收方:使用自己的私钥对加密过的会话密钥进行解密获得明文的会话密钥 然后用明文的会话密钥对消息进行解密获得明文消息
使用混合密码系统解决了密钥配送问题:因为对称密钥在网络上配送的是密文,密文是对称密钥使用公钥加密后的结果,只能通过私钥进行解密,即便密文的对称密钥被第三方非法窃取,但因为第三方没有对应的私钥,无法对密文的对称密钥进行解密,也就能够保证对称密钥的安全性。
为什么不是使用公钥对消息进行加密,再使用会话密钥对公钥进行加密呢?
1.公钥本身就是公开的,不需要对公钥进行加密,对公钥加密无意义,上图中公钥用于加密数据,即便窃听者获取了明文的公钥也只能对数据加密,而没有私钥无法对数据解密 。
2.使用公钥对数据加密没有解决非对称加密方式频繁加密大量数据的低效问题。
因为对称加密算法的加解密速度比非对称加密算法的快,在日常的网络数据传输中,经常会频繁的传输大量的网络数据,使用非对称加密这种安全但不高效的加密方式进行加密显然是不可取的,所以应该采用对称密钥加密消息。但对称密钥需要在网络上进行传输,且极易容易被破解。所以需要保证对称密钥在网络传输中的安全性,即需要保证对称密钥在网络配送过程中的安全性。所以最终的方案是使用对称密钥对消息进行加解密,再使用非对称加密的公钥对对称密钥进行加密,最后在网络上配送的是被公钥加密过的对称密钥和对称密钥加密过的消息(因为对称密钥数据长度比较短,使用非对称加密方式加密并不会很低效)。这种方式被称为混合加密(混合密码) 即用非对称加密(通常是RSA)解决密钥配送问题,用对称加密(AES)解决加密效率问题。
HTTPS中的SSL就是使用的混合密码系统。
除对称加密和非对称加密之外,还有一种特殊的加密方式——消息摘要。消息摘要又叫做Hash算法、单向散列算法。准确的说,消息摘要不算是一种加密方式,因为消息摘要是不可逆的且消息摘要的长度和消息的长度无关,所以就没有和对称加密、非对称加密混为一谈。
消息摘要函数又被称为哈希函数(hash function)。单向散列函数(One-way hash function)
哈希函数可以根据消息内容计算出对应的哈希值。哈希值的长度和消息的长度无关,无论消息是1bit、10M、100G,哈希函数都会计算出固定长度的哈希值。输出的哈希值也被称为消息摘要(message digest)、指纹(fingerprint)、散列值。
消息摘要长度固定。无论明文消息长度是多少,使用同一个消息摘要函数计算出的消息摘要的长度是固定的。
消息摘要函数计算速度快。相比较其他复杂的计算方式,消息摘要函数计算速度快。
消息明文和摘要一一对应。同样的消息经过同一个摘要函数计算的散列值永远相同,不同的消息计算的散列值也不同。
消息摘要具备不可逆性。消息摘要函数又叫做单向散列函数,顾名思义,函数具备单向性和不可逆性。正常情况下,无法通过算法计算消息摘要对应的明文消息。
单向散列算法是一个概念和标准,而不是一个具体的算法。常见的单向散列算法有:MD2、MD4、MD5、HAVAL、SHA、SHA-1、HMAC、HMAC-MD5、HMAC-SHA1、SHA-256。目前最常用的且相对安全的是SHA-256,SHA-256的散列值长度就是256bit。此外还有SHA-384、SHA-512。SHA-3是正在研究的全新标准。
macOS上自带md5散列函数:
因为消息摘要和明文的一一对应关系以及不可逆性,所以消息摘要通常用来验证消息的完整性和真实性,以及用于不可还原的密码存储。
1.防止数据篡改
2.密码口令加密
有了上述的对称密码、公钥密码、单向散列函数是不是就可以满足我们的数据安全需求了呢?答案是否定的。
对称密码和公钥密码仅解决了数据的加密问题,但依然无法彻底避免数据篡改和身份伪装。比如 常见的中间人攻击,就是在网络中拦截公钥并转发自己的公钥来实现消息拦截和篡改的。我们常见的网络代理应用——Charles就是采用中间人攻击的方式实现网络代理。
因为对称密码、公钥密码、单向散列函数都无法同时满足【防篡改、防伪装、防否认】。所以需要一种新的技术来识别数据篡改、伪装、否认。这种技术就是数字签名。
作用
数字签名通过一系列手段可以识别数据是否被篡改、识别消息发送方的真实身份是否合法,防止消息发送方否认。所以,数字签名的作用就是防止篡改、伪装、否认。
数字签名加密
数字签名加密即指对消息执行数字签名的处理过程,如下:
数字签名解密
数字签名解密即指对消息摘要执行解密和验证的处理过程,如下:
数字签名加密
数字签名解密
数字签名特点:
数字签名的完整过程
为什么要对数据的哈希值进行加密而不是对数据本身进行加密?
通常网络数据较长,直接用私钥对数据进行加密后的密文可能会很长。
数据的哈希值通常是固定长度的,且长度不会很长,所以对哈希值进行加密后的密文不会很长,方便网络传输。
数字签名的目的
数字签名发送的是【明文消息+数字签名】。即数据在网络上是明文传输的,所以无法保证数据的机密性。并且这也不是数字签名的目的。
数字签名的目的是识别接收的数据的真实性和完整性(即有没有被篡改数据(对比消息摘要)、有没有被伪装身份(公钥解密签名)、保证发送者无法否认(和身份伪装是一回事,因为是用发送者的私钥加密的消息摘要,所以发送者不能否认))。
中间人攻击
因为公钥是需要在网络上传输的,所以公钥有可能被中间人拦截篡改,这种篡改公钥的攻击方式叫中间人攻击。Charles代理的方式就是中间人攻击的应用。
中间人攻击是通过拦截并持有真正的公钥,转发自己的公钥来实现消息的篡改和转发。一旦公钥被拦截篡改,消息接收者收到的将是中间人的公钥,那么数字签名将形同虚设。
综上,问题就演变成:
要正确使用签名,前提是需要保证:用于验证签名的公钥必须属于真正的发送者。
所以如何保 证公钥属于真正的消息发送者?
为了保证验证签名的公钥属于真正的消息发送者,即避免遭受中间人攻击拦截&伪造公钥,即保证数字签名的公钥的真实性合法性,需要CA证书
上面通过介绍数字签名,了解到签名的原理是消息发送端用私钥加密消息摘要,消息接收端用公钥解密消息摘要。又了解到中间人攻击可以拦截公钥并转发自己的公钥,所以要正确使用签名,前提是需要保证:用于验证签名的公钥必须属于真正的发送者。如何保证数字签名的公钥是真实的呢?这里就用到了公钥证书(Public-key Certificate,PKC)。
什么是公钥证书?
要开车先得考驾照,驾照上面记有本人的照片、姓名、出生日期等个人信息,以及有效期、准驾车辆的类型等信息。并由公安局在上面盖章。我们只要看到驾照,就可以知道公安局认定此人具有驾驶车辆的资格。
公钥证书其实和驾照很相似,里面记有姓名、组织、邮箱地址等个人信息,以及属于此人的公钥,并由认证机构(Certification Authority、Certifying Authority,CA)施加数字签名。只要看到公钥证书,我们就可以知道认证机构认定该公钥的确属于此人。公钥证书也简称证书。
以上是笔者摘自《图解密码技术》一书中对公钥证书的定义。
作用
通过公钥证书的定义,可见公钥证书本质上就是对公钥的签名认证,已达到认证公钥确实属于某个机构、组织或个人的目的。
所以公钥证书的作用是验证公钥的真实性。这样就可以解决公钥配送过程中被中间人攻击的问题。
证书结构
何为CA(Certificate Authority)
CA就是我们上面说的认证机构
注册证书
1.消息接收者生成密钥对
2.消息接收者将密钥对的公钥发送给CA机构
3.CA机构用CA自己的私钥对消息接收者的公钥施加数字签名
4.CA机构通过上一步生成的数字签名和消息接收者的公钥生成公钥证书
使用证书
1.消息发送者从CA机构获取到指定的公钥证书
2.消息发送者通过预置的CA机构的公钥验证公钥证书的合法性
3.消息发送者使用证书中的公钥对传输的会话密钥(对称密钥)进行加密(采用混合密码系统)
HTTPS中的证书就是指CA证书
在了解iOS签名机制之前,我们必须先对齐一些概念,以及每个概念背后的意义。这些概念主要包括:
Mac公钥证书(.certSigningRequest)*
.certSigningRequest文件是以certSigningRequest作为后缀名的文件,也就是我们常说的CSR文件。CSR文件是从macOS的钥匙串的证书助理中通过证书颁发机构请求的公钥证书*。Mac作为证书颁发机构,生成的密钥对默认采用RSA算法,密钥大小默认2048位。
Apple证书(.cer)
描述文件(.mobileprovision)
所以,描述文件最终描述(限制)了:证书、iOS devices、appId、app的权限。
描述文件是开发期使用的文件,App Store下载的App不存在描述文件。
权限列表(Entitlements)
权限列表包含了 App 拥有的所有权限,比如消息push权限、后台运行权限。
p12
p12本质是Mac本地私钥的另一种形式,可以在钥匙串访问(Keychain Access)中导出p12文件给其他Mac设备,其他Mac设备把p12安装到自己的钥匙串中后就可以进行身份伪装。
在Xcode6之前,不管是真机调试,还是发布APP,开发者都需要按年购买一个付费的Apple ID,在开发一个新的App时,需要去Apple后台执行一系列复杂的配置步骤,目的就是要生成一个iOS开发证书和一个对App的描述文件,操作步骤如下:
模拟器调试则不需要执行以上配置,也不需要在Xcode添加Apple ID。对于真机调试,现在的Xcode会自动帮开发者执行以上操作。所以,非必要情况下,大多数开发场景是不涉及到以上繁琐的配置的。
Apple根据iOS App安装渠道来源的不同,对App的签名方式有所区别。通常App的安装渠道可以分为:
App Store签名是最简单的签名方式。只需要保证用户安装的App来源是App Store且被Apple认可的。
要实现这个需求很简单,最直接的方式,苹果官方生成一对公私钥,在 iOS 系统里内置一个Apple公钥,私钥由苹果后台保存,我们传 ipa 到 AppStore 时,苹果后台用私钥对 App 数据进行签名,iOS 系统下载这个 App 后,用预置的Apple公钥验证这个签名,若签名正确,说明这个 App 肯定是由苹果后台认证的,并且下载和安装过程中都没有修改过App,也就达到了苹果的需求:保证安装的每一个 APP 都是经过苹果官方允许的。事实上,Apple也是这么做的。Apple会对我们提审App Store的App进行重签名,所谓重签名就是对开发者提审的ipa包中的App进行重签,重签发生在Apple的后台,使用的Apple自己的私钥。具体重签流程后面介绍,这里仅作为了解。
App Store签名
上面App Store对应用市场分发的App进行签名,很好的保证了App的安全性。但我们知道,除了App Store分发的应用外,还有其他三种应用分发方式:In-House、AD-Hoc、Xcode。这三种方式生成的App文件不会上传到App Store,但Apple还是要兼顾这些法外之地的App的合规性和安全性,对这些App的安装和使用享有绝对的控制权。所以Apple还需要添加其他手段来验证这些非App Store分发的App。这里就引出了上面所说的描述文件——mobileprovision profile。
描述文件其实是Apple对App二次签名的产物。苹果想要对线下安装的App享有控制权,包括:
如上,第一条,苹果想要控制经过许可才可以线下安装App。通过我们对签名和证书的认识,这个实现很简单。其步骤大致如下:
简化的流程图大致如下:
iOS签名简化版
上述流程只解决了上面第一个需求,也就是经过苹果允许才可以安装使用App,还未解决第2、3个问题(2.指定的设备才能安装使用线下分发的App 3.指定设备只能安装指定的App,设备不能安装非开发期的App)。怎么解决呢?苹果再加了两个限制,一是限制在Apple后台注册过的设备才可以安装App,二是限制签名只能针对某一个具体的 App(也就是Bundle ID)。
怎么加的?在上述第2步,苹果用私钥签名我们本地公钥时,实际上除了签名公钥,还可以加上无限多数据(比如Devices、Bundle Id),这些数据都可以保证是经过苹果官方认证的,不会有被篡改的可能。
可以想到把 允许安装的设备 ID 列表 和 App对应的 AppID 等数据,都在第三步这里跟Mac公钥一起组成证书,再用苹果私钥对这个证书签名。在最后第 5 步验证时就可以拿到设备 ID 列表,判断当前设备是否符合要求。根据数字签名的原理,只要数字签名通过验证,第 5 步这里的设备 IDs / AppID / 公钥 L 就都是经过苹果认证的,无法被修改,苹果就可以限制可安装的设备和 App,避免滥用。
实际上一个“公钥证书”本来就有规定的格式规范,公钥证书,顾名思义就是对公钥的签名,上面我们把各种额外信息塞入证书里是不合适的,于是苹果另外加了一个文件,叫 Provisioning Profile,一个 Provisioning Profile 里就包含了证书以及上述提到的所有额外信息,以及所有信息的签名。这也是苹果二次签名(1. 对Mac公钥签名 2. 对Apple证书+Devices+Bundle ID+Entitlements签名)的原因。
至此,我们知道了,Apple想限制安装App的设备和限制安装的App,于是增加了描述文件,描述文件其实是对Apple证书、Devices、 App Id、Entitlements等条款的签名。
如下图,是笔者画的iOS开发期的详细的签名和验签的流程。其中涉及到Mac公钥(CSR文件)、Mac私钥、Apple公钥(提前预置到iOS设备中)、Apple私钥(用于对Mac公钥签名)、Apple证书、mobileprovison文件。下面详细介绍签名和验签的步骤。
这里再次不厌其烦的赘述下最终的流程:
embedded.mobileprovision
,把 APP 安装到手机上。embedded.mobileprovision
的数字签名是否正确,里面的公钥证书签名也会再验一遍。embedded.mobileprovision
里的数据都是苹果授权以后,就可以取出里面的数据,做各种验证,包括用公钥 L 验证App签名,验证设备 ID 是否在 ID 列表上,AppID 是否和Apple后台配置的Bundle ID对应得上,权限开关是否跟 App 里的 Entitlements 对应等。如果上述数据都能够对应的上,说明这个App的数据没有被篡改,允许安装。iOS签名机制
为什么从App Store下载安装的App没有mobileprovison文件?
App发布的时候做了一系列的合法性验证(三次签名),当我们把App上传到App Store后,苹果会用他自己的私钥对app进行重签名。 这样从app Store下载的app验证流程就变简单了:iOS 设备只需要用Apple预置的公钥验证下载的App的数字签名。签名验证通过说明App没有被篡改过,这样就保证了App渠道的合法性和App的安全性。
为什么我们发布到App Store的App必须要被Apple重签名?
因为发布到App Store的App需要安装到很多用户手机上,也要保证用户安装的App没有证书过期的问题。这样对证书有效期、可安装的Devices列表都有不一样的要求。所以,App Store 的签名验证方式和Xcode线下开发以及企业分发的App的验证方式不一样。我们上传到App Store的App会被Apple重签名,其重签名的思路大致是:①先对ipa中的描述文件embedded.mobileprovision进行验证,此步骤可以获得Apple证书,②然后再验证Apple证书,此步骤可以获得Mac公钥③再用上面一步获得的Mac公钥验证App是否被篡改过。如果以上三步都通过,则Apple会使用自己的Apple私钥对App文件进行重签名。这样用户从App Store下载的App就不会有embedded.mobileprovision
文件,也就是它安装和启动的流程是不依赖这个文件,验证流程也就跟Xcode、In-House、AD-Hoc的验证流程不一样了。
所以 App 上传到 AppStore 后,就跟你的Apple开发证书、Provisioning Profile 都没有关系了,无论他们是否过期或被废除,都不会影响 App Store 上的安装包。
那为什么发布 AppStore 的包还是要跟开发版一样搞各种证书和 Provisioning Profile?
iOS App 签名的原理中说“猜测因为苹果想做统一管理,Provisioning Profile 里包含一些权限控制,AppID 的检验等,苹果不想在上传 AppStore 包时重新用另一种协议做一遍这些验证,就不如统一把这部分放在 Provisioning Profile 里,上传 AppStore 时只要用同样的流程验证这个 Provisioning Profile 是否合法就可以了。”
上面的猜测比较合理。笔者这里的补充是Apple需要对开发者上传大App Store的包进行验证。即保证开发者上传过程中App的文件信息没有被中间人攻击,所以这里Apple还是要求开发者将App带着各种证书和签名上传到Apple后台,Apple后台再以拆快递的方式对ipa包进行各种合法性校验验证通过后再重签名。
非越狱手机才会验证App的数字签名。越狱手机可以随意安装各种App且不会验证签名,相当于没有了安全验证机制。所以如果越狱开发者把篡改后的App安装到越狱手机,则不需要对App重签名。 所以,改完的被篡改过mach-O的app文件不需要重签名就可以运行在越狱手机上。
正因为非越狱手机会验证App的数字签名,所以如果想把被修改过的app安装到非越狱手机上,需要对app进行重签名,目的就是让被篡改过的App可以通过设备的对数字签名的层层校验。
通常我们对App重签名的方式有:
《图解密码技术》