理论篇:iOS 应用重签名

导语

 
  在整理阅读:iOS App签名的原理了解了签名的过程,现在重新回头整理一下 iOS 重签名的一些内容。   

环境 & 工具

 

macOS Sierra 10.12.4
Xcode 8.3.1

数字签名

 
  数位签章(又称公钥数位签章,英语:Digital Signature )是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术实现,用于鉴别数字信息的方法。

  在这里指 codesign 的参数 identity ,它的生成是用 SHA-1 算法对整个数字证书内容进行摘要生成的 40 位的 16 进制的字符序列。

Entitlement

 
在 《深入解析Mac OS X & iOS 操作系统》一书中提到:   

 

  Entitlement 是一种权限声明机制,从概念上非常像 .NetJava 中使用的声明式权限机制。在 iOSentitlement plist 是直接嵌入在应用程序的二进制中的,并且由苹果进行数字签名

 

关于 Entitlement 文件是嵌入到应用程序的二进制中的论断,已进行验证。

描述文件

 
  Apple 用于描述与管理应用安装的文件,其中包含了证书,包名,可安装设备等信息。   

总结

  • 数字签名: 用于解决已获得授权的应用的完整性 (是否完整)
  • 描述文件: 用于解决应用是否能被授权安装 (是否安装)
  • Entitlement: 用于解决该应用的权限 (是否有权限)

过程分析

 
  一开始不明白没事,总有明白的。关于签名这件事,Xcode 是了解的,因此考虑从它入手。
  
  在 Xcode 上用 ⌘ B 去编译一个 iOS 的工程,通过查看本次运行的日志信息中的签名(Sign)部分。我们可以了解到 iOS 应用开发时的签名工作是由 macOS 上的 codesign 负责的。
  
Snip20170409_4

“预处理”

  1. cd 到工程目录
  2. export CODESIGN_ALLOCATE 定义 CODESIGN_ALLOCATE 环境变量
  3. export PATH : 临时添加 PATH 环境变量,主要为了让 Xcode 中的一些指令能被找到
  4. 读取描述文件。

正文: codesign

 
通过查看 codesignman 手册来确定参数的具体含义

 

-f, –force
 When signing, causes codesign to replace any existing signature on the path(s) given. Without this option, existing signatures will not be replaced, and the signing oper-ation fails.

 

使用 -f,--force 参数会在签名时替换掉原有的签名,大约就是个是否进行覆盖操作的意思。

 

-s, –sign identity
 Sign the code at the path(s) given using this identity. See SIGNING IDENTITIES below.

SIGNING IDENTITIES
To be used for code signing, a digital identity must be stored in a keychain that is on the calling user’s keychain search list. All keychain sources are supported if properly
configured. In particular, it is possible to sign code with an identity stored on a supported smart card. If your signing identity is stored in a different form, you need to
make it available in keychain form to sign code with it.

 

打开钥匙串, 找到签名的数字证书,指令中 identity 的值是 Fingerprints40 位的 16 进制 SHA-1 值。

关于 SHA-1MD5 都是数字签名算法。

Fingerprints 是指纹的意思,算隐含唯一性。

 

–entitlements path
When signing, take the file at the given path and embed its con-
tents in the signature as entitlement data. If the data at path
does not already begin with a suitable binary (“blob”) header,
one is attached automatically.

 

找到对应路径下的 xxx.app.xcent 文件,具体内容大概如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>application-identifier</key>
<string>XXX.包名</string>
<key>com.apple.developer.team-identifier</key>
<string>XXX</string>
<key>get-task-allow</key>
<true/>
<key>keychain-access-groups</key>
<array>
<string>XXX.包名</string>
</array>
</dict>
</plist>

get-task-allow :允许其他进程附上该进程,比如开发环境下 Xcode 会用 lldb 附上进程进行调试。已验证过在 AdHoc 包中的该字段也确实会变成 false

keychain-access-groups : 为了开发者能用钥匙串在自己的应用间传递信息(比如:密码)。

embedded.mobileprovision (描述文件)

 
  最后生成的包中有 embedded.mobileprovision 文件,这个就是我们从苹果后台下载的描述文件,其中包含了应用的包名,证书,可安装设备,过期时间等等信息,在签名时会被打包到应用中。因为不签名Xcode 无法通过编译,无法验证,因此只是觉得应该是签名操作时生成的。

  在重签名过程中,网上会说从自己以前打包的应用里面拷贝,当然是可以的,但是从苹果后台下载要重签名的描述文件,然后重新命名为 embedded.mobileprovision 也是一个方案。

  下面是一个 embedded.mobileprovision 的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AppIDName</key>
<string>XC Wildcard</string>
<key>ApplicationIdentifierPrefix</key>
<array>
<string>ZEY3XXXXXX</string>
</array>
<key>CreationDate</key>
<date>2017-03-08T06:25:32Z</date>
<key>Platform</key>
<array>
<string>iOS</string>
</array>
<key>DeveloperCertificates</key>
<array>
<data>MIIF...fA=</data>
<data>MIIF...94=</data>
</array>
<key>Entitlements</key>
<dict>
<key>keychain-access-groups</key>
<array>
<string>ZEY3XXXXXX.*</string>
</array>
<key>get-task-allow</key>
<true/>
<key>application-identifier</key>
<string>ZEY3XXXXXX.*</string>
<key>com.apple.developer.team-identifier</key>
<string>ZEY3XXXXXX</string>
</dict>
<key>ExpirationDate</key>
<date>2018-03-08T06:25:32Z</date>

<key>Name</key>
<string>iOS Team Provisioning Profile: *</string>
<key>ProvisionedDevices</key>
<array>
<string>2b8ffa94ec91d9ce9c94da9ad423b88888888888</string>
</array>
<key>TeamIdentifier</key>
<array>
<string>ZEY3XXXXXX</string>
</array>
<key>TeamName</key>
<string>Deliang Wang</string>
<key>TimeToLive</key>
<integer>365</integer>
<key>UUID</key>
<string>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</string>
<key>Version</key>
<integer>1</integer>
</dict>

总结

  1. “预处理” 做好一些前期的处理工作(设置环境变量,读取描述文件等)
  2. -signSHA-1 值 ,从钥匙串中找到对应的数字证书
  3. --entitlements 确定应用的权限
  4. codesign 指令对应用的二进制文件,资源,动态库进行签名(二进制的签名保存在自己内部)

 
安装过程:

  iOS 设备用验证 embedded.mobileprovision 文件的数字证书,取出其中的公钥,能取出证明经过了苹果服务器的授权,即该公钥是有效的,用该公钥去解密本地私钥加密的信息(_CodeSignature),得到真实签名值,然后同样计算,验证文件的完整性,完整性通过后就可以安装。

方案

 
重签名一般就是在苹果认可的体系下:

  • AppStore 分发
  • 开发者调试安装
  • AdHoc / 企业包分发

让应用顺利安装。

主要是让 iOS 设备认为完整性没有被破坏

关于工具 Apple 已经提供了,就是 codesign

说明

 
  一开始我以为重签名并没有太多可以写的内容,毕竟网上关于如何重签名的资料还是挺多的,也有不少优秀的工具可以很方便的完成重签名的操作。
  
  为了让自己确定知道哪些因素会影响重签名,测试(折腾)了挺久才大致了解了点,因为这部分目前比较乱,需要时间去整理,而且实际重签名操作还有一些情况需要考虑: 多 target,注入 dylib ,所以打算拆成两篇,把具体的如何重签名的操作在实践篇中再谈谈。

  

参考

  1. Wiki - 数字签名
  2. What does get-task-allow do in Xcode?
  3. Simplifying using Keychain Access Groups