1. 概要
如果进行过OTA升级的开发者,都或多或少有这样的疑问,如何确定该OTA升级包是可以信任的呢?这其中其实涉及到一个签名验证的流程。
2. 签名生成
在生成正规的固件时,一般会运行生成新key的脚本,并重新修改key中的信息。以网上常用的生成key的脚本为例:
#!/bin/sh
AUTH='/C=CN/ST=xxxx/L=xxxxx/O=xxxxxx/OU=xxxxx/CN=China/emailAddress=xxxxxxx@com'
openssl genrsa -3 -out $1.pem 2048
openssl req -new -x509 -key $1.pem -out $1.x509.pem -days 10000 \
-subj "$AUTH"
echo "Please enter the password for this key:"
openssl pkcs8 -in $1.pem -topk8 -outform DER -out $1.pk8 -passout stdin
其中openssl通过genrsa标准命令生成私钥,默认大小为2048:
openssl genrsa -3 -out $1.pem 2048
生成私钥后,生成证书签署请求,即公钥:
openssl req -new -x509 -key $1.pem -out $1.x509.pem -days 365 \
-subj "$AUTH"
-new: new request
-x509: output a x509 structure instead of a cert. req.该选项说明生成一个自签名的证书。
-keyfile: use the private key contained in file
-days: number of days a certificate generated by -x509 is valid for.
最后通过私钥pem文件生成PKCS8私钥文件
openssl pkcs8 -in $1.pem -topk8 -outform DER -out $1.pk8 -passout stdin
也可以参考android原生的生成key的流程,位于android/development/tools下的make_key脚本i。
自此后,生成了一对公私钥用于签名校验。
3. sign_target_files_apks.py
3.1 对Apk进行重签名流程
sign_target_files_apks在本人之前的用法都局限于将targetfile里的apk进行重签名,其流程如下:
1.获取脚本输入参数
2.获取输入文件以及输出文件参数,读取misc_info文件
input_zip = zipfile.ZipFile(args[0], "r")
output_zip = zipfile.ZipFile(args[1], "w")
misc_info = common.LoadInfoDict(input_zip)
misc_info记录了一些参数,如下:
其中这里关注的是默认签名的路径:
default_system_dev_certificate=build/target/product/security/testkey
3.建立key映射
假如在调用脚本时未指定-d.-k参数,那么默认使用的正是系统自带的testkey。否则,将会映射到指定key目录下的Key
def BuildKeyMap(misc_info, key_mapping_options):
for s, d in key_mapping_options:
if s is None: # -d option
devkey = misc_info.get("default_system_dev_certificate",
"build/target/product/security/testkey")
devkeydir = os.path.dirname(devkey)
OPTIONS.key_map.update({
devkeydir + "/testkey": d + "/releasekey",
devkeydir + "/devkey": d + "/releasekey",
devkeydir + "/media": d + "/media",
devkeydir + "/shared": d + "/shared",
devkeydir + "/platform": d + "/platform",
})
else:
OPTIONS.key_map[s] = d
4.读取targetfile中的证书文件
apk_key_map = GetApkCerts(input_zip)
GetApkCerts的实现如下,其实质是读取了targetfile中/META/apkcerts.txt文件
def GetApkCerts(tf_zip):
certmap = common.ReadApkCerts(tf_zip)
# apply the key remapping to the contents of the file
for apk, cert in certmap.iteritems():
certmap[apk] = OPTIONS.key_map.get(cert, cert)
# apply all the -e options, overriding anything in the file
for apk, cert in OPTIONS.extra_apks.iteritems():
if not cert:
cert = "PRESIGNED"
certmap[apk] = OPTIONS.key_map.get(cert, cert)
return certmap
def ReadApkCerts(tf_zip):
"""Given a target_files ZipFile, parse the META/apkcerts.txt file
and return a {package: cert} dict."""
certmap = {}
for line in tf_zip.read("META/apkcerts.txt").split("\n"):
line = line.strip()
if not line:
continue
m = re.match(r'^name="(.*)"\s+certificate="(.*)"\s+'
r'private_key="(.*)"$', line)
if m:
name, cert, privkey = m.groups()
public_key_suffix_len = len(OPTIONS.public_key_suffix)
private_key_suffix_len = len(OPTIONS.private_key_suffix)
if cert in SPECIAL_CERT_STRINGS and not privkey:
certmap[name] = cert
elif (cert.endswith(OPTIONS.public_key_suffix) and
privkey.endswith(OPTIONS.private_key_suffix) and
cert[:-public_key_suffix_len] == privkey[:-private_key_suffix_len]):
certmap[name] = cert[:-public_key_suffix_len]
else:
raise ValueError("failed to parse line from apkcerts.txt:\n" + line)
return certmap
在这里可以分析下apkcerts文件,其内容格式如下:
name="RecoveryLocalizer.apk" certificate="build/target/product/security/testkey.x509.pem" private_key="build/target/product/security/testkey.pk8"
name="CtsVerifier.apk" certificate="build/target/product/security/testkey.x509.pem" private_key="build/target/product/security/testkey.pk8"<