iOS证书介绍
|
名词解释
数字签名
- 代表一个特定的主体(签名者)对特定内容(被签名数据)的署名和认可
- 签名的本质是用于
验证数据的合法性,确保被签名的数据来自特定的来源,并且未经篡改。- 基于
非对称加密和哈希算法。 - 非对称加密
- 最常见的公钥加密算法是
RSA公钥加密算法 - 公钥 + 私钥
- 私钥加密 - 公钥解密(反正亦可,不过一般设定其中一个作为公开的公钥)
- 最常见的公钥加密算法是
- 哈希算法
- 散列、摘要算法
- 对一段任意长度的数据,通过一定的
映射和计算,得到一个固定长度的值,这个值就被称为这段数据的哈希值(hash)。 - 哈希值不同的两段数据绝对不同 & 相同的数据计算出的哈希值绝对相同
- 存在碰撞(不同数据段哈希值相同)
- 哈希算法是
单向算法,无法通过哈希值,计算出原始数据。 - 常见的哈希算法有:
md5,sha1,sha256等,其中sha1长度为160bits,而sha256长度为256bits
- 基于
- 综上,常见的签名算法:
- sha1WithRSAEncryption:先对数据计算sha1摘要,再对摘要进行RSA加密
- sha256WithRSAEncryption:先对数据计算sha256摘要,再对摘要进行RSA加密
- md5WithRSAEncryption:先对数据计算MD5摘要,再对摘要进行RSA加密
- 总之一般都是先
hash,再RSA。 - 后续将最终结果统称为
加密哈希。
证书
- 一般会把
公钥当做签名(数据签名)的一部分,随着数据一起分发(避免接收方需事先保存各种公钥) - 为确保
公钥的可信,把公钥和公钥所有者的信息保存在一个文件里,并让一个可信的第三者使用其私钥对这个文件进行签名(证书签名),得到一个签了名的公钥文件,这个文件就叫做证书。(+外加签发者信息)证书作为签名的一部分,随着数据一起分发。- 若只含
加密哈希(+签发者信息), 不含公钥(签发者证书) ,一般称为简单签名
- 这个
可信的第三者就被称为证书颁发机构(Certification Authority),简称CA。 CA的证书可能也是由其他更高一级的CA进行签发,形成多级的证书链,系统中只需保存最高级CA的证书,中间CA的证书和信息提供者的证书依次进行递归校验即可。- 而证书链上的
根证书颁发机构(根CA)的证书,是自签名的,被内置在设备中,设备无条件信任 根证书(公钥)则由本地持有(不随数据)。
- 而证书链上的
- 理一理
- 数据
- 数据 +
加密哈希(数据) - 数据 +
加密哈希(数据)+ 公钥 - 数据 +
加密哈希(数据)+ 公钥 + 证书(=公钥&公钥所有者信息 +加密哈希(使用签发者公钥可解)+ 签发者信息) - 其中
加密哈希(签发者公钥)+ 签发者信息 => 公钥信息的签名加密哈希(数据)+ 公钥 + 证书 => 完整的数据签名
- 如果涉及
多级证书- 数据 +
加密哈希(数据)+ 公钥 + 证书0(=公钥&公钥所有者信息 +加密哈希(使用CA1的公钥可解)+ CA1的信息 + CA1的证书 (CA2签名) )- CA1证书=CA1公钥&CA1信息 +
加密哈希(使用CA2的公钥可解)+ CA2的信息 + CA2的证书(CA3签名) - …
- CA(n-1)证书=CA(n-1)公钥&CA(n-1)信息 +
加密哈希(使用CA root的公钥可解)+ CA root 信息 (此时不需要 + CA root 证书)
- CA1证书=CA1公钥&CA1信息 +
- 系统会检查
CA root的信息,在本地keychain是否有对应的证书(且被信任),并校验即可- 一般情况下,
CA root 证书(自签名)不需要打到证书链 CA root 证书=CA root公钥&CA root信息 +加密哈希(使用CA root的公钥可解)+ CA root 信息自签名,内置于设备,无条件信任
- 一般情况下,
- 数据 +
- 通常说使用
证书进行签名- 实际指使用与证书所匹配的
私钥进行签名,证书作为签名数据的一部分被嵌入签名中。 - 若Keychain中
只有证书,没有对应的私钥文件,则无法进行签名,会得到Missing private key之类的报错。
- 实际指使用与证书所匹配的
- 一般会把
开发者证书
- 一般来说 iOS app 必须
被苹果签名之后才能安装到设备上 - 为方便开发过程中的频繁安装(避免每次都要交给苹果签名)
开发者自己持有一套私钥和证书- 由Apple对开发者的身份进行
背书,让设备间能够接信任开发者自行签名的app背书的方式即Provisioning Profile
- 普通开发者($99/year)
- 最大测试uuid支持数:100
- 按类别分位
个人开发者账号和公司开发者账号- 个人: 协作人数1人
- 公司: 协作人数多人,设置多个Apple ID,分多种管理级别的权限,申请时需要额外信息。
- 按功能分位
Development证书和Distribution证书 - Development证书
开发及测试阶段使用,一般命名 iPhone Developer: xxxxxxx- 用于在设备安装上
开发阶段的App后对App的完整性进行校验 - 如果是多人协作的开发者账号,任意成员都可以申请自己的Development证书。(一般没必要)
- Distribution证书
- 用于
提交AppStore的证书,一般命名 iPhone Distribution: xxxxxxxxx - 用于让
AppStore校验提交上来的App的完整性 - 只有管理员以上身份的开发者账号才可以申请,
不能用于开发及调试。
- 用于
- 企业级开发者($299 / year)
企业级开发者证书- 签名的App可以被直接安装在
任意的iOS设备上,只要用户主动信任该证书 - 方便企业给内部员工分发生产力工具
- 不能发布到 App Store
- 教育机构:免费、额外申请、不可发布 App Store、 …
- 一般来说 iOS app 必须
CSR 文件
- .certSigningRequest 后缀
- Certificate Signing Request
- 用于向苹果请求证书
- 包含
个人信息:账号信息公钥自签名:使用自己的私钥进行签名
.cer证书
- 苹果返回,双击导入 keychain, 与已有公钥、私钥自动配对
- 签发者:
Apple Worldwide Developer Relations Certification Authority中级证书颁发机构(中级CA),其证书由Apple Root CA根证书颁发机构(根CA)进行签发Apple Root CA的证书,自签名的,被内置在设备中
- 包含
公钥公钥所有者信息- 并非由我们自行指定,而是签发者Apple
根据我们的账号信息自动生成
- 并非由我们自行指定,而是签发者Apple
苹果的签名
.p12证书
- 个人信息交换文件
- Xcode 签名需要
- APNs 需要
- 包含
私钥证书(含公钥)
推送证书
- 用于和
APNs通信的证书 - 基本同开发者证书
- 服务端使用
推送证书(推送证书的.p12文件)与APNs通信每个App需要单独配置。推送证书的有限期为1年(生产的推送证书比开发的推送证书多一个月),过期之后需要重新配置。- 分
开发证书和生产证书开发证书只能用于往开发环境(sandbox)推送消息生产证书现在可以同时支持开发环境(sandbox)和 生产环境,(以前貌似只能往开发环境推送消息)- 服务端和
APNs通信时使用不同的参数区分开发环境(sandbox)和 生产环境
- 服务端和
- 用于和
Entitlements
- iOS沙盒的
配置文件,声明了app所需的权限,如果app中使用到了某项沙盒限制的功能,但没有声明对应的权限,可能运行到相关的代码时会直接Crash。 - 全新的iOS工程中是没有这个文件的,如果在
Capabilities中开启了一些需要权限的功能之后,Xcode会自动(Xcode 8及之后的版本)生成Entilements文件,并将对应的权限声明添加到Entitlements文件中。 Capabilities主要能力如下- 获取 wifi 信息
- 使用 apple pay
- keychain sharing
push notifications- siri 等等
- 与
info.plist的隐私权限配置不同- 相册读写、打开相机、访问日历、访问通讯录等等。
- 一般情况下,如果没有配,但代码调用了相应的API,提交二进制包就会通不过,苹果会发邮件/通知告诉你缺了配置,所以不太担心上线后 crash。(但是提包会失败。)
- 但有些情况下,是 web 里的某些行为触发的一些逻辑,
- 比如 iOS12 下 web 里可以提供上传视频录制的功能,此时会调用
麦克风权限,如果此时 APP 没有配置麦克风相关的隐私权限描述,就会 crash。 - 但有时候企业级证书并不 crash…
- 比如 iOS12 下 web 里可以提供上传视频录制的功能,此时会调用
- iOS沙盒的
Provisioning Profile- 官方说明:A provisioning profile is a collection of digital entities that uniquely ties developers and devices to an authorized iPhone Development Team and enables a device to be used for testing.
- 对
设备和开发者授权的作用,他将开发者账号、证书、entitlements文件以及设备进行了绑定。 - Xcode 8及后续版本默认情况下会自动帮我们管理
Provisioining Profile- 自动下载的
Provisioning Profile都被存放在~/Library/MobileDevice/Provisioning\ Profiles/路径下
- 自动下载的
- 由
Apple iPhone OS Provisioning Profile Signing进行签名 - 几项关键内容
DeveloperCertificates- 允许使用的
开发者证书,这是一个列表,一般包含生成这个Provisioning Profile文件时,当前开发者账号下所有有效的Development证书
- 允许使用的
Entitlements- 允许使用的
权限列表,实际在App中使用的权限必须是这个列表的子集,否则安装时会无法通过校验而失败。
- 允许使用的
ProvisionedDevices- 允许安装的
设备列表,如果目标设备的UUID不在这个列表中,会安装失败。普通开发者证书: 只允许最多注册100台用于测试的设备,避免任意分发测试包。企业级开发者证: 有任意安装的需求,因此在分发时,这一项会被ProvisionsAllDevices取代,代表授权任意设备。
- 允许安装的
- 这些信息中有任何变动的时候,
- 比如开发者证书有新增或者失效,在
Capabilities中启用了当前App从未使用过的新功能, - 或是将新的iPhone连接到Xcode用于测试,
- Xcode都会自动
重新申请Provisioning Profile。(前提是Xcode登录了对应的账号,否则还是手工操作吧)
- 比如开发者证书有新增或者失效,在
Provisioning Profile最终会被内置在App中,置于App根目录下的embedded.mobileprovision。- 但从
AppStore上下载下来的App,里面不会有embedded.mobileprovision这个文件,因为经过Apple重新签名以后,设备就不再需要它了。 非AppStore安装App时如果签名校验通过,这个文件会自动被拷贝到iOS设备的/Library/MobileDevice/Provisioning\ Profiles/路径下。- 由于该文件已被
Apple官方签名,系统可以无条件信任它,并用它来校验App的签名、权限,以及本机的UUID等是否满足来自官方的授权。 - 通过这种方式,
间接信任了使用开发者证书签名的App,让iOS设备可以运行非苹果官方签名的App。
代码签名
2008年苹果发布iOS2.0时引入了强制代码签名(Mandatory Code Signing)
- 严格控制设备上能够运行的代码
- 为iOS设备的安全性和苹果的AppStore生态奠定了坚实的基础。
- iOS的代码签名是典型的
数字签名
好处:
- 安全性
- 保证设备及系统的安全性
- 只有被苹果设备认可的证书签名的代码才能够被执行,否则在安装或者运行时会因为无法通过内核的签名校验而失败。
- iOS的系统中内置了来自苹果的CA证书,系统自身的代码都是被苹果签名过的, 而用户从AppStore下载的App也都已被苹果官方进行签名。
- 签名机制可以有效地防止来自外部的攻击。
- 保障沙盒的有效运转,有效地限制app的行为
- 沙盒的配置是绑定在签名中的,即
Entitlements文件,也是强制签名保护的对象。
- 沙盒的配置是绑定在签名中的,即
- 保证设备及系统的安全性
- 分发控制
- 苹果拥有App分发的绝对控制权。
- 在iOS平台上(面向未越狱的用户)公开发行App的合法途径有且只有一种,就是上传到苹果官方的
AppStore供用户下载。- 苹果会进行严格的
审查并签名 - 企业包需要申请企业证书进行签名
- 苹果会进行严格的
- 安全性
在编译
iOS App时,Xcode在编译的打包的流程中会自动进行代码签名- 可以在编译日志界面找到一个
Sign的步骤,内部是调用了codesign命令
- 可以在编译日志界面找到一个
Xcode 签名后的差异(对比无签名状态)
- 多了一个
_CodeSignature文件夹,只有一个文件CodeResources- plist文件,保存签名时
每个文件(除了App的可执行文件)的明文哈希值(单纯哈希,未加密)- 这里的
哈希值是哈希之后再base64编码的明文,并没有用私钥加密
- 这里的
- 有
files和files2两个部分,分别是旧版本和新版本的文件列表,- 用
rules与rules2两个部分描述了新旧版本计算hash时需要被排除的文件以及每个文件的权重。 files中保存的是每个文件的sha1值,files2中同时保存了sha1和sha256。
- 用
- plist文件,保存签名时
- 多了一个
embedded.mobileprovision文件- 即对应的
Provisioning Profile文件,直接拷贝到了app的根目录并重命名
- 即对应的
二进制文件内容有差异,签名后体积变大- 签名后二进制文件多了一个名为
LC_CODE_SIGNATURE的Load Command,即代码签名 - 代码签名是一段纯二进制的数据,(具体结构定义详见苹果官方文档),其中包含如下内容
- Code Directory (旧版本,
sha1) - Requirements
- Entitlements
- Code Directory (新版本,
sha256) - CMS Signature
- Code Directory (旧版本,
Code Directory里面是整个MachO文件的哈希值- 但
不是一次性对整个文件进行哈希 - 而是将MachO文件按照
pageSize(一般是4k即4096字节)进行分页,每一页单独计算哈希(单纯哈希,未加密),并按顺序保存。 - 有两个
Code Directory,除哈希类型,其他内容基本一样 - 文件不一定是
pageSize的整数倍,最后一页往往不足,用额外的字段codeLimit记录文件的实际大小- 通过这个值算出最后一页的实际大小,并提取相应数据计算最后一页的签名。
- 除
MachO外,在第一页的前面,还有5个特殊的负数页,用来保存以下这些额外信息的哈希值。(也是单纯哈希,未加密)- -1 App根目录的Info.plist文件
- -2 Requirements(代码签名的第二部分)
- -3 Resource Directory (_CodeSignature/CodeResources文件)
- -4 暂未使用
- -5 Entitlements (代码签名的第三部分)
- 但
Requirements- 用于指定签名校验时的一些额外的约束,签名时codesign命令会自动生成这部分数据,细节忽略,官方文档有一些描述
Entitlements- 整个被嵌入到签名数据中
- 但不是工程中原始的,Xcode混合上一些默认的参数信息生成的。
CMS Signature- 最重要的部分
Cryptographic Message Syntax,一种标准的签名格式,和Provisioning Profile的签名格式相同- 内容包括但不限于:
- 证书链:包含用于签名的开发者证书及所有上游CA的证书
- 签名者信息
- 哈希算法
- 签名算法(对哈希值进行加密所使用的算法)
加密后的哈希值signedAttrs字段- 需要签名的属性, 是
可选项, 为空表示被签名的数据是原始文件的内容,- 如果不为空则至少要包含
原始文件的类型以及其哈希值,此时被签名的数据就是signedAttrs的内容 - 先计算被签名数据的
哈希,然后再对哈希值进行签名。
- 需要签名的属性, 是
Code Directory的哈希值CDHash作为数据记录在signedAttrs中,作为最终真正被签名(计算哈希+加密)的内容。signedAttrs中再对CDHash进行签名(计算哈希+加密)
- 签名后二进制文件多了一个名为
- 总结一波加密数据情况
_CodeSignature/CodeResources中对每个资源文件计算哈希- 二进制中
Code Directory中对MachO文件本身的每个分页,以及Info.plist、CodeResources、Entitlements等文件计算哈希 - 计算
Code Directory的哈希CDhash,存储在二进制中CMS Signature的signedAttrs中 - 二进制中
CMS Signature的signedAttrs再对signedAttrs进行签名(计算哈希并使用开发者的私钥加密)
- 只有最后一步的涉及
真正加密, 前面几步的均为单纯,只要任意内容有变化,均会因某个环节的哈希不匹配而导致签名校验的失败。
- 多了一个
导出 or 提交App
- 在Xcode Organizer中导出或者提交App时,Xcode会将
Entitlements文件及embedded.mobileprovision文件替换为对应的版本 - 并使用对应的证书
重新签名- AppStore:不可调试,推送为
生产环境,无ProvisionedDevices,发布证书 - Ad Hoc:不可调试,推送为
生产环境,允许安装到已注册的测试设备,发布证书 - Development:可调试,推送为
测试环境,允许安装到已注册的测试设备,开发证书 - Enterprise:不可调试,推送为
生产环境,ProvisionAllDevices,企业级发布证书
- AppStore:不可调试,推送为
- 在Xcode Organizer中导出或者提交App时,Xcode会将
App 安装时
- 由
/usr/libexec/installd完成 installd会通过libmis.dylib校验ProvisioningProfile、Entitlements及签名的合法性- 递归地校验签名时每一个步骤生成的哈希值:
CDHash,Code Directory,_CodeSignature/CodeResources。
- 递归地校验签名时每一个步骤生成的哈希值:
- 由
App 启动时
loader会先将可执行文件加载到虚拟内存,在加载的过程中mach_loader会自动解析MachO文件中的LC_CODE_SIGNATURE并进行校验load_code_signature在解析完签名的数据后会调用mac_vnode_check_singature函数进行验证- 最终也是调用了
libmis.dylib来实现签名的校验,这一校验过程基本与安装时一致,防止安装后的篡改。
- 最终也是调用了
- 加载过程中为了提升加载效率,签名校验并不会去检查
Code Directory与实际的代码是否匹配,仅检查了CMS Signature及Code Directory Hash的合法性。
APP 运行时
- 当
一页代码被加载到虚拟内存后,会立即触发page fault,会判断代码页是否需要签名校验 - 如果需要,则计算当前代码页的哈希值,并与签名中
Code Directory记录的值进行比对。如果不符,且不满足系统预设的例外条件,则会向内核发出CS_KILL指令。
- 当
有关越狱
- 越狱之后,签名
校验机制会被破坏掉。 - 在iOS6/7时代,典型的方式是替换
libmis.dylib中的_MISValidateSignature函数,使其永远返回验证成功,简单粗暴。 - 这样既可在沙盒范围内,声明任意的权限。
- 越狱之后,签名
重签名
- 有时出于各种原因,我们需要对一个App进行
重签名,方便在自己的设备上进行测试。 - 需要使用和原App相同或者至少包含原App所需权限的
Entitlements文件 Entitlements文件中还有一些跟Team ID和App ID相关的配置- 因为我们不能使用已经被其他开发者注册过的ID。使用自己的ID一般也不会有什么问题,但在某些情况下可能导致最终的程序逻辑出现异常,这根具体的代码实现细节有关。
- 正常签名的App中,
Entitlements文件中标识的application-identifier(Bundle ID)和Info.plist中的CFBundleIdentifier的值是相同的, - 但实际在签名校验过程中,系统
不检查二者是否一致,所以即使不一致,也不影响重签名之后的运行。
- 只要确保有正确的
Entitlements,Provisioning Profile与Entitlements匹配,且包含重签时使用的证书及目标设备的UUID,就可以进行重签名了 - 另需要注意,App中除了可执行程序文件外,还会可能会有
Frameworks及Plugins,里面都会包含二进制的代码文件,他们的哈希值也会被存储在_CodeSignature/CodeResources中。所有的二进制代码都必须进行签名,而签名后二进制文件的哈希值就会产生变化,因此需要先对这两个文件夹下的二进制文件进行签名,再对App进行签名。 - (有各种现成的工具可以重签名)
- 有时出于各种原因,我们需要对一个App进行
有关 APNs
Apple Push Notification service
苹果推送通知服务
服务端两种认证方式
- 基于证书的信任认证(旧版,更常用一些)
- 服务端使用
推送证书(推送证书的.p12文件)与APNs通信 每个App需要单独配置。推送证书的有限期为1年(生产的推送证书比开发的推送证书多一个月),过期之后需要重新配置。- 现在的
生产推送证书已经可以同时支持 sandbox 和 生产环境。
- 服务端使用
- 基于Token的信任认证(新版,2016年)
- 在开发者平台申请
APNs Auth Key,得到包含APNs Auth Key的.p8密钥文件。 .p8文件只能下载一次- 服务端使用包含token(
APNs Auth Key)的.p8文件与APNs通信 - 一个认证密钥可用于同一个账号下
多个App服务,而且永远不过期,可以重新申请,使旧的失效。
- 在开发者平台申请
- 基于证书的信任认证(旧版,更常用一些)
服务器与 APNs 通信的时候,必须实现上述两种认证方式之一
device token- 是一个不透明的
NSData,包含一个设备上的一个应用的唯一标识。 - 只有 APNs 能解密并查看
device token中的内容。 - APNs 提供的
device token的长度不定,不要强行解码其大小
- 是一个不透明的
大概的流程
- APP 安装
- APP 启动,APP 向
APNs发送远程推送注册请求 - 苹果给 APP 提供唯一的
device token- 回调方法
application:didRegisterForRemoteNotificationWithDeviceToken:
- 回调方法
- APP 将自己的
device token转发给自己的服务端- 一般按二进制或十六进制的格式发给你的服务器
服务端做好相应的记录服务端要发送推送消息时,使用推送证书+device token+推送内容来发送消息- APNs 进行校验,通过后,会向
设备发送请求的推送信息 设备接收到 APNs 发来的推送之后,操作系统会把通知递送给相应的 APP。
一般情况下如果设备已经注册了
远程推送请求,并且特定 APP 的device token并没有变化,则 APNs 会返回已经存在的device token到设备上APNs 下发新的
device token可能的原因:- 用户安装你的APP到
新设备上 - 用户通过备份
恢复设备 - 用户重装系统后
- 其它系统层面的事件
- 用户安装你的APP到
所以,一般APP在启动的时候必须请求
device token
简单实地操作
Apple ID
- 注册
开发者账号
- 提交信息+交钱
- 购买$99(¥688) / $299
- 如果要公司或企业级,还需要提供邓白氏编码等信息,等苹果联系等等
登录苹果开发者平台
创建 APP ID
- 根据引导,填写各种信息
创建证书(开发)
- Mac - 钥匙串访问 - 证书助理 - 从证书颁发机构请求证书
- 填写
开发者账号的邮箱,存储到磁盘即可 -> 生成 CSR(certSigningRequest)开发证书、发布证书、Push 证书,都需要各弄一个 CSR- 此时本地已经生成了对应的 公钥 + 私钥
- 开发者平台 - Certifications,根据引导,选择对应的类型,上传 CSR,下载 Cer 文件
- 双击安装,导入钥匙链, 自动与 CSR 对应的关联
创建配置文件(描述文件Provisioning Profile)
- 开发者平台 - Provisioning Profile,根据引导,
- 选择对应的类型
- 选择 APP ID
- 选择证书
- 选择设备
- 取个名字
- Generate
- Download
- 双击就添加到 Xcode 中
- 也可以在Xcode中下载(当然,前提是登陆了对应的账号)
- Xcode - Preference - Accounts
- 即可管理相应的证书
- 开发者平台 - Provisioning Profile,根据引导,
创建 APP
- 选择对应的 APP ID
- 填写 APP 信息
- 记得现在需要隐私说明了
Xcode -> 创建项目
Project -> Build Setting
- Signing
- Code Signing Identity
- 证书配置
- Debug
- Release
- ReleaseInHouse(企业级证书)
- Provisioning Profile
- 对应配置
- 也可以配置成 automatic,然后在 TARGETS 里配置
- 证书配置
- Code Signing Identity
- Signing
TARGETS -> General
- Identity
- Bundle Identifier: 即 Bundle Id
- Siging
- Sining(Debug): 选择
Development开发证书对应的 Provisioning Profile - Sining(Relase): 选择
Distribution发布证书对应的 Provisioning Profile
- Identity
debug 打包
- Xcode 点击 Run
导出 ad hoc 或上传 App Store
- Product - Archive
- 需要有
Distribution发布证书以及对应的Provisioning Profile,才能执行Archive
- 需要有
- 生成的文件可以在 Window - Organizer 中的
Archivestab 中找到(Archive执行完毕后会自动打开) - 可以进行导出
ad hoc包,或者上传AppStore等操作- 也可以在 Xcode - Open Developer Tool - Application Loader 来进行相关上传操作
- Product - Archive
相关打包、上传等操作,可以通过 xcode 提供的命令行工具进行操作,可以手工指定各种参数、证书所在位置、证书ID等,方便的进行持续集成、自动化打包等工作。
参考
Published on
tags: