对于新的签名方案APK Signature Scheme v2,在这篇文章中已经有详细的介绍http://www.tuicool.com/articles/bURRVrj。从这篇文章中可以知道,新的签名方案与旧的签名方案之间的对比是:
图1
新的签名方案生成与旧的签名方案相比,在zip文件中新增了一个APK Signing Block区块。使用新的签名方案以后,在apk下所有的文件中所做的修改,都会导致在android7.0系统上安装失败。那么android7.0上的签名校验过程是怎样的呢?为什么apk下任意文件修改都会导致签名校验失败呢?网上很多文章都对7.0之前的签名校验过程进行了讲解,想了解7.0之前的签名校验过程可以参考文章http://blog.csdn.net/roland_sun/article/details/42029019。
但是很少有文章对7.0及更高版本上的签名校验过程进行分析,所以在这篇文章中,主要针对7.0及以上的签名校验过程进行分析,通过分析7.0系统源码的方式,来向大家描述一下这一过程。
(1) Android平台上所有应用程序安装都是由PackageManangerService(代码位于frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java)来管理的,apk的安装流程与签名验证相关的步骤位于installPackageLI函数中:
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
……
PackageParser pp = new PackageParser();
……
try {
pp.collectCertificates(pkg, parseFlags);
pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
res.setError("Failed collect during installPackageLI", e);
return;
}
……
(2)
在这个函数中,会用到PackageParser这个类
(代码位于
frameworks\base\core\java\android\content\pm\PackageParser.java
,编译后存在于
framework.jar
文件中)是一个apk包的解析器,接下来我们来看其
collectCertificates
函数的实现:
public static void collectCertificates(Package pkg, int parseFlags)
throws PackageParserException {
collectCertificatesInternal(pkg, parseFlags);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
Package childPkg = pkg.childPackages.get(i);
childPkg.mCertificates = pkg.mCertificates;
childPkg.mSignatures = pkg.mSignatures;
childPkg.mSigningKeys = pkg.mSigningKeys;
}
}
(3)
在
collectCertificates函数中,调用了
collectCertificatesInternal函数。所以接下来我们看看
collectCertificatesInternal的函数实现:
private static void collectCertificatesInternal(Package pkg, int parseFlags)
throws PackageParserException {
pkg.mCertificates = null;
pkg.mSignatures = null;
pkg.mSigningKeys = null;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
collectCertificates(pkg, new File(pkg.baseCodePath), parseFlags);
if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
for (int i = 0; i < pkg.splitCodePaths.length; i++) {
collectCertificates(pkg, new File(pkg.splitCodePaths[i]), parseFlags);
}
}
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
(4)在函数
collectCertificatesInternal中调用了
collectCertificates函数的重载函数,在7.0之前,是在
collectCertificates函数中直接调用其重载函数,中间是没有
collectCertificatesInternal函数的。
collectCertificates的重载函数是一个很重要的函数,接下来我们看看在这个函数中做了什么操作:
private static void collectCertificates(Package pkg, File apkFile, int parseFlags)
1 throws PackageParserException {
1151 final String apkPath = apkFile.getAbsolutePath();
1152
1153 // Try to verify the APK using APK Signature Scheme v2.
1154 boolean verified = false;
1155 {
1156 Certificate[][] allSignersCerts = null;
1157 Signature[] signatures = null;
1158 try {
1159 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV2");
1160 allSignersCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
1161 signatures = convertToSignatures(allSignersCerts);
1162 // APK verified using APK Signature Scheme v2.
1163 verified = true;
1164 } catch (ApkSignatureSchemeV2Verifier.SignatureNotFoundException e) {
1165 // No APK Signature Scheme v2 signature found
1166 } catch (Exception e) {
1167 // APK Signature Scheme v2 signature was found but did not verify
1168 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
1169 "Failed to collect certificates from " + apkPath
1170 + " using APK Signature Scheme v2",
1171 e);
1172 } finally {
1173 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
1174 }
……
在这个函数中,首先对签名apk做了v2方式的签名校验(代码从1154-1174)。也就是说首先用针对v2方式的签名方式来做签名校验,如果校验成功
verified
= true。如果在校验的过程中抛出了异常,那么有两种可能:1.apk没有用v2签名方式进行签名;2.apk用了v2签名方式进行签名,但是签名检验没有成功。