iOS的签名机制很复杂,各种证书,Provision Profile,entitlements,CertificateSigningRequest,p12,AppID,这篇文章从概念出发,一步一步推出为什么会有这么对概念,希望能有助于理解iOS的App签名的原理。
目的
在iOS出来之前,在主流操作系统(Mac,Windows,Linux)上开发和运行软件是不需要签名的,软件随便从哪里下载都能运行,导致平台对第三方软件很难控制,盗版盛行。苹果希望解决这样的问题,希望iOS平台对第三方App有绝对控制权,一定要保证每一个安装到iOS上的App都是经过苹果官方允许的,怎么保证呢?就是通过签名机制。
最简单的实现
要实现这个需求很简单,最直接的方式,苹果官方生成一对公私钥,私钥由苹果后台保管,公钥内置到iOS设备里,在我们将App上传到App Store上时,苹果后台使用私钥对App进行签名,iOS设备下载这个应用后,用公钥验证这个签名,若签名正确,则说明这个App是经过苹果后台认证的,并且没有被修改过,这样也就达到了苹果的目的:保证iOS设备安装的每一个APP都是经过苹果官方允许的。
image
如果我们的iOS设备安装App只通过App Store这一种方式的话,那么问题到这里就已经解决了,但是实际上除了从App Store上下载应用,还可以以一下三种方式安装一个APP:
- 1.作为开发者,开发App时直接进行真机调试。
- 2.In-House 企业内部分发,可以直接安装企业证书签名后的App。
- 3.AD-Hoc 相当于是企业分发的限制版,限制安装设备数量。
苹果要对这三种方式安装的APP进行孔子,就无法像上面这样简单了。
新的需求
我们先来看第一个,开发时安装APP,它有两个需求:
- 1.安装包不需要传到苹果服务器,可以直接安装到手机上。
- 2.苹果必须对这个安装过程有控制权,包括:
a.经过苹果允许才可以这样安装
b.不能被滥用导致非开发App也能被安装。
为了满足这个需求,iOS签名的复杂度也就开始增加了。
苹果给出的方案是使用双层签名,有一点绕,流程大概是下图这样:
image
1.在我们开发使用的Mac上生成一对公钥和私钥,称为公钥,私钥L。L:Local。
2.苹果有固定的一对公钥和私钥,私钥在自己后台保存,公钥内置到了iOS设备里,称为公钥,私钥A。A:Apple。
3.把公钥L上传到苹果后台,用苹果后台的私钥A去签名公钥L。得到了一份数据包括公钥L及其签名,这份数据称为证书。
4.在开发时,编译完一个APP后,用第一步生成的私钥L去签名这个App,同时把第三步得到的证书一起打包进App里,安装到手机上。
5.在安装时,iOS系统取得证书,通过系统内置的公钥A,去验证证书的数字签名是否正确。
6.验证证书后确保了公钥L是苹果认证的,再用公钥L去验证App的签名,这样就间接验证了这个App安装行为是否经过苹果允许。
加点东西
上述流程只解决了上面的第一个需求,也就是需要经过苹果的允许才可以安装,还未解决第二个避免被滥用的问题。怎么解决呢?苹果加了两个限制,一个是限制在苹果后天注册过的设备才可以安装,二是限制签名只能针对某个具体的App。
在上述的第三步中,苹果用私钥A去签名我们本地公钥L时,实际上除了签名公钥L,还可以加上很多数据,这些数据都可以保证是经过苹果官方认证的,不会有被篡改的可能,那么我们就可以把AppID和设备ID添加进去:
image
把允许安装的设备ID和APP对应的AppID等数据,都在第三步这里和公钥L一起,被私钥A签名,一起组成证书。在第五步验证时就可以拿到设备ID列表,判断当前设备是否符合要求。
最终流程
到这里这个证书已经变得很复杂了,有很多额外的信息,实际上除了设备ID,AppID,还有其他信息也需要用苹果签名,像App里面的iCloud,后天运行等苹果都想控制,苹果把这些权限开关统称为entitlements,它也需要通过签名去授权。
但是一个证书本来就有规范的格式,我们把这些杂七杂八的额外信息赛入证书是不合适的,因此苹果另外搞了一个东西叫Provisioning Profile,一个Provisioning Profile里面就包含了证书以及上述提到的所有额外信息,以及所有信息的签名。
所以最终流程就变成了这样:
image1.在你的Mac上生成一对公钥和私钥,称为公钥L和私钥L。
2.苹果自己有一对固定的公钥和私钥,私钥在苹果后台,公钥内置在iOS设备中,分别称为私钥A和公钥A。
3.把公钥L传到苹果后天,用苹果后天的私钥A去签名公钥L,得到一份数据包括公钥L和签名,这份数据称为证书。
4.在苹果后台申请好AppID,配置好设备ID列表,App权限开关,再加上第三步的证书,组成的数据用苹果后天的私钥A签名,把数据和签名一起组成一个Provisioning Profile文件,下载到本地Mac。
5.在开发时,编译完一个App后,用本地的私钥L对这个App进行签名,同时把第四步生成的Provisionning Profile一起打包进App里,文件名为embeded.mobileprovision,把App安装到手机。
6.在安装时,就可以使用iOS设备里内置的公钥A来验证Provisioning Profile的数字签名是否正确。
7.如果数字签名没有问题,那么就能确保设备ID,AppID,entitlements,和App都是经过苹果认证的,可以安装到iOS设备上。
上面的步骤对应我们平时具体操作和概念是这样的:
1.第一步对应的是从keychain里“从这证书颁发机构请求证书”,这样就在本地生成了一对公私钥,保存的额CertificateSigningRequest就是公钥,公钥保存在本地电脑里。
2.第二步苹果处理,不用管。
3.第三步把CertificateSigningRequest上传到苹果后天,生成证书,并下载到本地。
4.第四步是在苹果网站操作的,配置AppID,设备ID,权限等,生成Provisioning Profile文件,并下载Provisioning Profile文件到本地。
5.xcode通过第三步下载下来的证书,去找对应的本地私钥,用本地私钥去签名App,并把Provisioning Profile文件一起打包进去,安装进iOS设备。
总结一些概念:
1.证书:内容是公钥或者私钥,由其它机构对其签名组成的数据包。
2.entitlements:包含了App权限开关列表,AppID,设备ID等。
3.CertificateSigningRequest:本地公钥。
4.p12:本地私钥。
5.Provisioning Profile:包含证书,entitlements等数据,并由苹果后台私钥签名的数据包。
我们平时的操作
按照上面的流程,那么对于开发人员来说,应该是我们每次新建一个项目也就是有一个新的AppID时,都应该去申请一对本地公私钥,上传公钥到苹果后台,然后下载证书,但是实际上我们并没有这么做,好像很少需要去keychain请求本地公私钥,这是为什么呢?
这里的原因就是iOS Team Provisioning Profile。
iOS Team Provisioning Profile是第一次使用xcode添加设备时,xcode自动生成的,它包含了xcode生成的一个Wildcard AppID(匹配所有应用程序,账户里面的所有device,所有Development Certificates),因此team中的所有成员都可以使用这个iOS Team Provisioning Profile在team的所有设备上调试所有的应用程序,并且当有新设别添加进来时,xcode会更新这个文件。
如此一来,只要我们有一对本地公私钥,并且通过这个本地的公钥上传给苹果获取了证书,那么以后我们运行任何App,在任何iOS设备上运行,都可以使用这个本地私钥和证书,而没有必要每次去创建新的公私钥和获取证书。
下面我从我的项目中找出一个iOS Team Provisioning Profile,我们可以一起来看一下它的结构:
第一个是AppID,这里的AppID是我当前应用的AppID。
第二个是证书,这就是选择了我本地的一个证书,是上面的流程中上传本地的公钥得到的证书。
第三个是team,这个是我在项目中选择的,这个team决定了我用哪个证书。
第五个是entitlements,就是一系列的权限开关。
通过这个iOS Team Provisioning Profile的结构我们就能明白,iOS Team Provisioning Profile中保存着很多分证书,很多AppID,很多设备ID,entitlements。当我们需要在一个指定的iOS设备上运行一个指定的App时,iOS Team Provisioning Profile就会得到这个AppID和这个设备ID以及它对应entitlements,组成这个特定的Provisioning Profile,打包进APP里面。这样就不需要我们每次去申请证书,生成Provisioning Profile文件了,非常方便。
注:这篇文章主要是把大牛的文章摘抄了一遍,由于大牛写的太好了,让我茅塞顿开,于是想重新写一次加深理解,也方便以后查阅。
大牛博客:
iOS App 签名的原理
关于 Certificate、Provisioning Profile、App ID 的介绍及其之间的关系
网友评论