手机是如何确保从app store下载的app是没被恶意篡改,以及如何验证app开发者身份的呢?
一、数字签名(digit signruare)
数字签名基于公钥加密技术,能够验证数据的完整性和真实性。涉及公钥、私钥、数字证书,私钥用来签名,公钥用来验证,数字证书验证用户身份。
签名
签名过程使用哈希函数和私钥加密得到数字签名。
- 经过哈希函数处理的数据得到一串固定长度且唯一的哈希值,即原始数据被更改后经过相同的哈希函数处理将得到不同的值。而且,该过程不可逆,即用处理结果推不出原始数据,这保证了数据的完整性。
- 用私钥加密该哈希值,就得到数据的数字签名。私钥是用户自己保存,公钥是公开的,与私钥配对使用。只有用与私钥配对的公钥的才能对数字签名解密,这就保证了数据的真实性,即数据确实是某用户加密的。
签名过程:将原始数据经过哈希函数处理,得到哈希值;用私钥加密得到的哈希值,得到数字签名值s1。如图,
验证
将原始数据用签名过程相同的哈希函数处理,得到哈希值h1;接着使用公钥解密s1,得到h2。比较h1和h2值,如果两者相等,验证成功。不相等,验证失败。如图,
数字证书
数字签名可以确保数据没有被篡改,但仍有一个问题。任何人都能申请一对公私钥,那如何知道解密的公钥就是某个用户的呢?这时就要用户向一个证书认证机构(Certification Authority,简称CA)申请一个数字证书,将用户的信息和用户的公钥,以及该CA的信息绑定在一起。将这些信息用CA的私钥进行数字签名,该数字签名和用户信息、用户公钥、CA信息组成了数字证书。
数字证书验证:CA的公钥生产手机时就被导入系统的,是可信赖的。所以用该公钥可以验证数字证书的真实性。
数字证书申请
数字证书是数字签名的基础,是用来验证用户身份的证明。那如何向苹果公司申请数字证书(即开发者证书)呢?
在申请证书之前,应在本地电脑生成一个CSR文件(CertificateSigningRequest),CSR文件是证书请求文件,使用keychain生成,同时生成一对密钥:公钥和私钥。私钥保存在keychain中,CSR文件绑定该公钥和一些证书申请信息。
开发者用CSR文件向苹果开发者网站申请数字证书(开发者证书)。苹果将CSR内的公钥和开发者信息绑定在一起进行数字签名,生成开发者证书(以.cer为后缀)。详细申请信息可参考这里
注意
- CSR文件没有私钥,申请到的开发者证书也没有私钥,私钥是保存到本地的。双击开发者证书安装到keychain,证书将自动绑定对应的私钥。
- 如果将开发者证书安装到其他电脑(非生成CSR文件的电脑),该证书是没有私钥的。前面说过,私钥是用来签名的,所以该证书在其他电脑不能用来签名。
- 如果想其他电脑也能签名,就需要在生成CSR文件的电脑上,将开发者证书导出为.p12证书文件。
- .p12文件基于PKCS#12标准,包含私钥和证书在同一个文件,并用密码来保护。
有配对私钥的证书下面有个小钥匙。在Mac上通过keychain可以查证书内容。
iOS代码签名与验证
签名
xcode在为app签名时,并不是一次将整个安装包进行签名的。而是将每个文件独自签名,采用的规则如下:
- Mach-O可执行文件:独自签名后,将签名数据写入到该可执行文件中。
- 资源文件:将所有资源文件,比如图片,plist文件等独自签名,并且将签名信息记录在_CodeSignature/CodeResources文件中。
开发者只需要在xcode设置有效的开发者证书(包含私钥),在编译过程中,自动会将代码签名。我们也可以使用codesign
命令签名,或者查看签名信息。
- 查看系统中可以用来对代码进行签名的证书
$ security find-identity -v -p codesigning
1) 6F58B7E08291BFFEB94746AE4416331679BB3ED3 "iPhone Developer: 1666522608@qq.com (DY6MN4A4Y8)"
- 为app设置签名
$ codesign -s 'iPhone Developer: 1666522608@qq.com (DY6MN4A4Y8)' Example.app
- 删除已存在的签名,重新为app设置签名,加上-f参数
$ codesign -f -s 'iPhone Developer: 1666522608@qq.com (DY6MN4A4Y8)' Example.app
- 查看签名信息
$ codesign -vv -d Example.app
Executable=/Users/apple/Library/Developer/Xcode/DerivedData/Example-IOSegobnxqrpyiplcezotfhlhohfykp/Build/Products/Debug-iphoneos/Example.app/v2ex
Identifier=singro.v2ex.qh
Format=app bundle with Mach-O universal (armv7 arm64)
CodeDirectory v=20400 size=35314 flags=0x0(none) hashes=1095+5 location=embedded
Signature size=4800
Authority=iPhone Developer: 1666522608@qq.com (DY6MN4A4Y8)
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=2018年10月30日 17:26:44
Info.plist entries=35
TeamIdentifier=J5FZC7U82Y
Sealed Resources version=2 rules=13 files=44
Internal requirements count=1 size=180
验证
app在安装前进行验证,分为两步:
- 用苹果私钥验证开发者证书;
- 用开发者的公钥验证安装包。
使用codesign命令验证:
$ codesign --verify Example.app
配置文件(provisioning profile)
有了开发者证书,才能验证开发者的身份,以及app安装包。那么系统是如何拿到开发者证书的呢?这就是配置文件要做的事情,它绑定了开发者的证书、app id、entitlements(授权)信息等。在xcode打包完成后,配置文件被放置在app安装包中。
配置文件是一个根据密码讯息语法 (Cryptographic Message Syntax) 加密的文件,采用 CMS 格式进行加密使得配置文件可以被设置签名,所以在苹果给你这个文件之后文件就不能被改变了。
使用security
可以查看配置文件的信息。
$ security cms -D -i example.mobileprovision
开发者证书被包含在DeveloperCertificates
选项里面,所有的证书都是基于 Base64 编码符合 PEM (Privacy Enhanced Mail, RFC 1848) 格式的。要查看一个证书的详细内容,将编码过的文件内容复制粘贴到一个文件中去,像下面这样:
-----BEGIN CERTIFICATE-----
MIIFnjCCBIagAwIBAgIIE/IgVItTuH4wDQYJKoZIhvcNAQEFBQAwgZYxCzA…
-----END CERTIFICATE-----`
然后使用 openssl x509 -text -in file.pem
来显示证书详细内容。
小结
app代码签名基于数字签名,涉及了公钥、私钥、数字证书。私钥用于签名,公钥用于验证,数字证书用于证明开发者身份。iOS开发者先在本地生成CSR文件,然后用该CSR文件向苹果开发者中心申请开发者证书。在xcode设置有效的证书后,在构建过程中使用开发者的私钥进行签名。手机在安装app前先验证开发者证书,然后用开发者的公钥验证app安装包。开发者证书被绑定在配置文件(provisioning profile)内,并且配置文件在构建完成后被放置在app安装包内。
参考
Code Signing Guide
iOS代码签名(Code Signing)
代码签名探析
iOS Code Signing