验证签名
所谓验证签名,就是检查APK中的签名结构是否符合一定的要求,这里的签名实际上是APK的整体签名。而在签名块中,存在很多项数据需要验证,比如APK的摘要信息,证书信息,SDK版本信息等,这些都是APK的签名数据。所以在整个签名的验证中,以上信息是全部都要验证的。不过在v3版本中添加的新特性是针对验证证书信息的修订,所以接下来也是重点分析验签中的证书验证的部分。
验证签名流程
因为签名的验证就是发生在一个apk包的安装过程中,所以为了更清楚验证签名的时机,有必要了解整个安装的分类与大致流程。Android安装应用主要有如下四种方式:
-
系统应用安装:开机时完成,没有安装界面
-
网络下载的应用安装:通过市场应用完成,没有安装界面
-
ADB工具安装:没有安装界面
-
第三方应用安装:通过packageinstall.apk应用安装,有安装界面
但是其实无论通过哪种方式安装都要通过PackageManagerService来完成安装的主要工作,最终在PMS中会去验证签名信息,流程如下
安装过程中如果发现有v3签名块,则必须使用v3签名的验证机制,不能绕过。否则才使用v2签名的验证机制,以此类推。
验证完整性
数据完整性校验v3与v2版本相同,原理如下:
签名块包括对apk第一部分,第二部分,第三部分的二进制内容做加密保护,摘要算法以及签名算法。签名块本身不做加密,这里需要特殊注意的是由于第三部分包含了对第二部分的引用偏移,因此如果签名块做了改变,比如在签名过程中增加一种签名算法,或者增加签名者等信息就会导致这个引用偏移发生改变,因此在算摘要的时候需要剔除这个因素要以第三部分对签名块的偏移来做计算。
验证证书
v2
v2版本签名验证证书步骤:
-
利用PublicKey解密Signature,得到SignerData的hash明文
-
计算SignerData的hash值
-
两个值进行比较,如果相同则认为APK没有被修改过,解析出SignerData中的证书。否则安装失败
-
如果是第一次安装,直接将证书保存在应用信息中
-
如果是更新安装,即设备中原来存在这个应用,验证之前的证书是否与本次解析的证书相同。若相同,则安装成功,否则失败
v3
v3版本签名验证证书步骤:(前三步同v2)
-
利用PublicKey解密Signature,得到SignerData的hash明文
-
计算SignerData的hash值
-
两个值进行比较,如果相同则认为APK没有被修改过,解析出SignerData中的证书。否则安装失败
-
逐个解析出level块证书并验证,并保存为这个应用的历史证书
-
如果是第一次安装,直接将证书与历史证书一并保存在应用信息中
-
如果是更新安装,验证之前的证书与历史证书,是否与本次解析的证书或者历史证书中存在相同的证书,其中任意一个证书符合即可安装
新特性场景举例
其实就是当开发者需要更换证书时,即可直接用新证书新的私钥进行签名。不过为了让老应用相信新的证书,则需要用老证书来保证。举个例子,有两个level块:level 1与level 2:
-
level 1放置老证书的信息
-
level 2中放置新证书的信息以及这段数据的签名
-
level 2中的签名是由老私钥进行签名的,则需要用老证书的公钥来验证
-
校验原来的证书与level 1 相同,则相信本次更新的level 2 的证书,即签名APK的证书
-
完成安装并记录新证书信息
关于v3签名的google注释
以下主要是在ApkSignatureSchemeV3Verifier.java文件中的有关于v3签名的一些函数的注释,官方原文可供参考
/**
-
Returns the certificates associated with each signer for the given APK without verification.
-
This method is dangerous and should not be used, unless the caller is absolutely certain the
-
APK is trusted. Specifically, verification is only done for the APK Signature Scheme v3
-
Block while gathering signer information. The APK contents are not verified.
-
@throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3.
-
@throws IOException if an I/O error occurs while reading the APK file.
*/
/**
-
Verifies APK Signature Scheme v3 signatures of the provided APK and returns the certificates
-
associated with each signer.
-
@throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3.
-
@throws SecurityException if an APK Signature Scheme v3 signature of this APK does not
-
verify.
-
@throws IOException if an I/O error occurs while reading the APK file.
*/
/**
-
Returns the APK Signature Scheme v3 block contained in the provided APK file and the
-
additional information relevant for verifying the block against the file.
-
@throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3.
-
@throws IOException if an I/O error occurs while reading the APK file.
*/
/**
-
Verifies the contents of the provided APK file against the provided APK Signature Scheme v3
-
Block.
-
@param signatureInfo APK Signature Scheme v3 Block and information relevant for verifying it
-
against the APK file.
*/
// make sure that the last certificate in the Proof-of-rotation record matches
// the one used to sign this APK.
// Proof-of-rotation struct:
// A uint32 version code followed by basically a singly linked list of nodes, called levels
// here, each of which have the following structure:
// * length-prefix for the entire level
// - length-prefixed signed data (if previous level exists)
// * length-prefixed X509 Certificate
// * uint32 signature algorithm ID describing how this signed data was signed
// - uint32 flags describing how to treat the cert contained in this level
// - uint32 signature algorithm ID to use to verify the signature of the next level. The
// algorithm here must match the one in the signed data section of the next level.
// - length-prefixed signature over the signed data in this level. The signature here
// is verified using the certificate from the previous level.
// The linking is provided by the certificate of each level signing the one of the next.
v3验签代码分析
PackageManagerService.InstallPackageLI()
无论是哪种方式的安装应用,最后都会执行到这个真正安装函数,这个函数位于PMS,这个函数代码比较长,这里保留比较关键的代码来说明
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, “parsePackage”);
final PackageParser.Package pkg;
try {
pkg = pp.parsePackage(tmpPackageFile, parseFlags);