iOS签名原理
在iOS真机调试和发布上线的时候,我们可能已经习惯了配置各种证书、描述文件,等这一繁琐的步骤。但是对于背后我们为什么要配置这些东西,以及其背后的原理之前一直没有做过分析研究,最近有空就简单的研究了一下!
一、背景
我们都知道苹果手机的正版APP只能去App Store下载,而其他系统的手机比如安卓手机的APP现在的途径有很多,这些软件是不需要签名的。而苹果为了控制每一个安装在苹果手机上的APP都是经过苹果官方认证的,于是就采用了签名机制。
1.对称加密
对称加密是密码学中一类加密算法的统称,这类算法在加密与解密时使用相同的密钥,或者使用两个可以简单的相互推算的密钥。常见的对称加密算法有DES、3DES、AES、RC5等。相比非对称加密算法,对称加密算法的优点是加解密的速度很快。
2.非对称加密
非对称加密是指加密密钥与解密密钥是成对出现的,其中一个对外公开,叫公钥,另一个末公开的叫私钥,几乎不能从一个密钥计算出另一个密钥。通过私钥加密的只能通过公钥解密,公钥加密的只能通过私钥解密。最著名的非对称加密算法是RSA算法。当然如此强大可靠的安全性是在牺牲加密速度的基础上得到的。
通常我们所说的签名就是数字签名,它是基于非对称加密算法实现的。对称加密是通过同一份密钥加密和解密数据,而非对称加密则有两份密钥,分别是公钥和私钥,用公钥加密的数据,要用私钥才能解密;用私钥加密的数据,要用公钥才能解密。这里的非对称加密就是我们所熟知的RSA,要了解RSA背后的数学原理可以参考RSA算法原理(一)(二)
二、从App Store安装APP
这个过程的签名方式相对简单一些。苹果官方生成一对公私钥,在苹果手机里面内置一个公钥,私钥由苹果后台保存,我们传App上AppStore时,苹果后台用私钥对App数据值的MD5值进行签名,iOS系统下载这个App后,用公钥验证这个签名,若签名正确,这个App肯定由苹果后台认证的,并且没有被修改过,也就达到了苹果的需求:保证安装的每一个App都是经过苹果认证允许的。
三、其他方式安装APP
在实际工作当中,我们还有一些其他的方式把APP安装到手机上:
开发App时可以直接把开发中的应用安装进手机调试;
In-House企业内部分发,可以直接安装企业证书签名后的App;
AD-Hoc相当于企业分发的限制版,限制安装设备数量,较少用。
苹果对这几种方式安装的控制过程就变得复杂了,即要保证APP的安装时经过苹果认证的,又要控制APP不能被随便安装到其他设备上,以及一些其他的权限,为了达到这样的目的,苹果采用的流程大致是这个样子
基本流程:
1、在Mac上生成一对公私钥,这里称公钥M,私钥M。
2、苹果自己有固定的一对公私钥,跟上面AppStore例子一样,私钥在苹果后台,公钥内置在每个iOS设备上,这里称为公钥A,私钥A。
3、把公钥M上传到苹果后台,用苹果后台里的私钥A去签名公钥M。得到一份数据包含了公钥M以及其签名(也就是公钥的HASH值),把这份数据称为证书。
4、在开发时,编译完一个App后,用本地的私钥M对这个App进行签名,同时把第三步得到的证书一起打包进App里,安装到手机。
5、在安装时,iOS系统取得证书,通过系统内置的公钥A,去验证证书的数字签名是否正确。
验证证书确保公钥M是苹果认证过的,再用公钥M去验证App的签名,这里就间接验证了这个App的安装行为是否经过苹果官方允许。(这里只验证安装行为,不验证App是否被改动,因为开发阶段App内容总是不断变化的,苹果不需要管)。
最终流程:
上述流程只解决了上面第一个需求,也就是需要经过苹果允许才可以安装,还未解决第二个避免被滥用的问题。怎么解决呢?苹果加了两个限制,一是限制在苹果后台注册过的设备才可以安装;二是限制签名只能针对某一个具体的App。
那么它到底是怎么添加这两个限制的呢?在上述第三步,苹果用私钥A签名我们的本地公钥M时,实际上除了签名本地公钥M外,还可以加上无限多数据,这些数据都可以保证是经过苹果官方认证的,不会有被篡改的可能。
可以把允许安装的设备ID列表和App对应的AppID等数据,都在第三步这里跟公钥M一起组成证书,再用苹果私钥A对这个证书签名。在最后第5步验证时就可以拿到设备ID列表,判断当前设备是否符合要求。根据数字签名的原理,只要数字签名通过验证,第5步这里的设备IDs/AppID/公钥M就都是经过苹果认证的,无法被修改,苹果就可以限制可安装的设备和APP,避免滥用。
到这里这个证书已经变得很复杂了,有很多额外信息,实际上除了设备ID/AppID,还有其他信息也需要在这里用苹果签名,像App里iCloud、push、后台运行 等权限苹果都想控制,苹果把这些权限开关统称为Entitlements,它也需要通过签名去授权。
实际上一个证书本来就有规定的格式规范,上面我们把各种额外的信息塞入证书里是不合适的,于是苹果另外搞了一个东西,叫Provisioning Profile,一个Provisioning Profile里就包含了证书以及上述提到的所有额外信息,以及所有信息的签名。
所以,就成这样了:
在 Mac 上生成一对公私钥,这里称为公钥M,私钥M。
苹果自己有固定的一对公私钥,跟上面 AppStore 例子一样,私钥在苹果后台,公钥在每个iOS设备上。这里称为公钥A,私钥A。A:Apple
把公钥M传到苹果后台,用苹果后台里的私钥A去签名公钥M。得到一份数据包含了公钥M以及其签名,把这份数据称为证书。
在苹果后台申请AppID,配置好设备ID列表和APP可使用的权限,再加上第3步的证书,组成的数据用私钥A签名,把数据和签名一起组成一个Provisioning Profile文件,下载到本地Mac开发机。
在开发时,编译完一个APP后,用本地的私钥M对这个APP进行签名,同时把第4步得到的Provisioning Profile文件打包进APP里,文件名为 embedded.mobileprovision,把APP安装到手机上。
在安装时,iOS系统取得证书,通过系统内置的公钥A,去验证 embedded.mobileprovision的数字签名是否正确,里面的证书签名也会再验一遍。
确保了embedded.mobileprovision里的数据都是苹果授权以后,就可以取出里面的数据,做各种验证,包括用公钥M验证APP签名,验证设备ID是否在ID列表上,AppID是否对应得上,权限开关是否跟APP里的Entitlements对应等。
开发者证书从签名到认证最终苹果采用的流程大致是这样,还有一些细节像证书有效期/证书类型等就不细说了。
上面的步骤对应到我们平常具体的操作和概念是这样的:
第1步对应的是keychain里的“从证书颁发机构请求证书”,这里就本地生成了一对公私钥,保存的CertificateSigningRequest就是公钥,私钥保存在本地电脑里。
第2步苹果自己处理,我们不用管。
第3步对应把CertificateSigningRequest传到苹果后台生成证书,并下载到本地。这时本地有两个证书,一个是第1步生成的,一个是这里下载回来的,keychain会把这两个证书关联起来,因为它们的公私钥是对应的,在Xcode选择下载回来的证书的时,实际上会找到keychain里面对应的私钥去签名。这里私钥只有生成它的这台Mac才有,如果别的Mac也要编译签名这个App,把私钥导出给其他Mac使用,在keychain里面导出私钥,就会存成.p12文件,其他Mac打开后就导入私钥。
第4步都是在苹果网站上操作,配置AppID、权限、设备等,最后下载 Provisioning Profile文件。
第5步Xcode会通过第3步下载回来的证书(存着本地公钥),在本地找到对应的私钥(第1步生成的),用本地私钥去签名App,并把Provisioning Profile文件命名为embedded.mobileprovision一起打包进去。这里对App的签名数据保存分为两部分,Mach-O可执行文件会把签名直接写入这个文件里,其他资源文件则会保存在_CodeSignature目录下。
第6、7步的打包和验证都是 Xcode 和 iOS 系统自动做的事。
四、总结
几个概念:
证书:内容是公钥或私钥,由其他机构对其签名组成的数据包。
Entitlements:包含了App权限开关列表。
CertificateSigningRequest:本地公钥。
.p12:本地私钥,可以导入到其他电脑。
Provisioning Profile:包含了 证书/Entitlements 等数据,并由苹果后台私钥签名的数据包。
其他发布方式
前面以开发包为例子说了签名和验证的流程,另外两种方式In-House企业签名和AD-Hoc流程也是差不多的,只是企业签名不限制安装的设备数,另外需要用户在iOS系统设置上手动点击信任这个企业才能通过验证。
而AppStore的签名验证方式有些不一样,前面我们说到最简单的签名方式,苹果在后台直接用私钥签名App就可以了,实际上苹果确实是这样做的,如果去下载一个AppStore的安装包,会发现它里面是没有embedded.mobileprovision文件的,也就是它安装和启动的流程是不依赖这个文件,验证流程也就跟上述几种类型不一样了。
因为上传到AppStore的包苹果会重新对内容加密,原来的本地私钥签名就没有用了,需要重新签名,从AppStore下载的包苹果也并不打算控制它的有效期,不需要内置一个embedded.mobileprovision去做校验,直接在苹果用后台的私钥重新签名,iOS安装时用本地公钥验证App签名就可以了。
那为什么发布AppStore的包还是要跟开发版一样搞各种证书和Provisioning Profile,因为苹果想做统一管理,Provisioning Profile里包含一些权限控制,AppID 的检验等,苹果不想在上传AppStore 包时重新用另一种协议做一遍这些验证,就不如统一把这部分放在 Provisioning Profile里,上传AppStore时只要用同样的流程验证这个 Provisioning Profile是否合法就可以了。
所以 App 上传到AppStore后,就跟你的 证书 / Provisioning Profile 都没有关系了,无论他们是否过期或被废除,都不会影响AppStore 上的安装包。
以上就是整个签名的大致分析。
iOS签名简洁过程
第一步:从KeyChain生成 CertificateSigningRequest.certSigningRequest。
得到 本地文件CertificateSigningRequest.certSigningRequest包含用户信息,公钥。
得到 Access|Keys中一对Public/Private Key ,公钥/私钥。
第二步申请证书:
通过CSR文件的公钥生成证书,包含开发者信息,公钥信息,使用苹果私钥加密。下载到本地使用苹果公钥解密,得到KeyChain中的证书文件,
第三步 申请授权描述文件:
使用苹果私钥加密,包含AppId,证书,功能授权列表,设备列表等信息。
第四步 App打包生成:
私钥签名
第五步 App安装验证:
设备使用CA证书(WWDRCA.cer)的公钥解密Provisioning Profile得到app 公钥和app内容摘要,验证Provisioning Profile的合法性。
使用app公钥解密app来判断App的合法性,使用app内容摘要判断app是否被篡改。
重签名
重签名的解释:
当在Xcode进行archive或者通过脚本打包ipa的时候,通常到最后一步,要对包有一个签名过程,对签名起到关键作用的是配置好证书和描述文件。也就是一套证书,描述文件最终签名好一个对应的ipa。
而所谓的重签名的概念就是,可以把一个已经存在的ipa重新配置一套证书和描述文件,再签名生成一个新的ipa包。
ios重新签名的核心原理是使用 codesign 命令,当然也许完成一些额外的操作。
大致流程
1、解压ipa
2、删除旧的签名
3、复制新的描述文件
4、用新的证书签名
5、压缩成ipa
在这个过程中,最重要是这个 entitlements.plist文件的问题。
entitlements.plist是一个比较重要的文件,涉及到app的权限及签名相关问题。
那么,如何得到这个文件呢?我们可以通过这条命令
获取到这个文件的内容,大致格式如下:
注意:这里我们我么需要把红色的地方替换成新的签名文件的bundleid和teamid.
当然,有的朋友可能会问,有没有更简单的操作呢?
实际上是有的。
著名的自动化工具fastlane,就封装了一个resign的工具,专门可以用来对app进行重签名的。具体可看:https://docs.fastlane.tools/actions/sigh/#resign
使用方法也很简单, 一行命令即可。
参考相关文章:
链接: Ios下app重签名的原理及使用教程 - Jason.z博客