1.通过.RSA文件的名字找到对应的.SF文件,
然后在获取MANIFEST.MF文件和.RSA文件,到这里三个文件就都拿到了。
拿到三个文件数据后,会调用verifySignature方法, 这个方法,
参数是.SF文件和.RSA文件的内存流对象;
这个方法中通过用X509文件解析了.RSA文件,
这个方法最终的目的是获取到RSA文件中的证书;
然后同.SF文件的数据进行对比,如果不相同会抛出异常:
即通过rsa的公钥文件 验证 .SF签名文件的合法性
openssl pkcs7 -inform DER -in CERT.RSA -noout -print_certs -text
打包的时候用rsa的私钥对 CERT.SF的sha256签名信息 进行加密
放到CERT.RSA的最后256字节
验证的时候是用公钥 解密这256字节
解出来 基本上都是0xff, 解密出来的数据大概这鸟样:
00 01 ff ... ff 很多ff
最后48字节不是ff
16字节 描述接下来的32字节明文签名信息
32字节 CERT.SF的sha256签名信息
因为sha256真正有用的信息只有32字节
所以会有很多的ff填充
解出来的32字节明文信息 必须等于 原来打包进去的 CERT.SF的32字节的sha256签名信息
说明 CERT.SF和CERT.RSA是一套的
没有被篡改
因为一旦修改了 CERT.SF,签名必改
CERT.RSA 公钥解密 出来的签名数据 不一样
如果 把公钥也改了 是可以验证通过的
因为CERT.SF和CERT.RSA是一套的
但是 这种情况 只能先卸载原来的apk,再重新安装 没有问题
如果想升级安装的话 是不行的
因为在升级安装的时候 会比较
原来包中的公钥与待替换的包的公钥 是否一致
不一致的话 直接拒绝 升级安装 (pm install -r)
2.校验MANIFEST.MF文件和CERT.SF文件对应的摘要值
在JarVerifier.java中的verifyCertificate方法中,会继续执行校验MANIFEST.MF文件和.SF文件的操作:
调用这个方法就完成了MANIFEST.MF文件和.SF文件的对比.
计算MANIFEST.MF 文件的sha256,与存储在.SF中的签名SHA-256-Digest-Manifest:比较
相同 说明 MANIFEST.MF没有被篡改
但是前提是 CERT.SF没有被预先修改
怎么保证呢?
由第一步保证 rsa文件(CERT.RSA)先保证 CERT.SF的合法性
3.既然都合法 那剩下的就是逐个验证 MANIFEST.MF
的文件项的实际hash值与记录的hash值 匹不匹配
校验APK文件摘要值同MANIFEST.MF文件中对应值是否相同
接下来回到PackageParser.java中的collectCertificates方法,
创建 StrictJarFile对象后,
会检查一下是否有清单文件(AndroidManifest.xml),
如果没有则直接抛出异常。然后遍历查询到META-INF文件
验证单个文件是在构造函数里 实现的
// fully verify all contents, except for AndroidManifest.xml and the META-INF/ files.
if (verifyFull) {
final Iterator
while (i.hasNext()) {
final ZipEntry entry = i.next();
if (entry.isDirectory()) continue;
final String entryName = entry.getName();
//Slog.d( TAG, "entryName = " + entryName );
if (entryName.startsWith("META-INF/")) continue;
if (entryName.equals(PackageParser.ANDROID_MANIFEST_FILENAME)) continue;
toVerify.add(entry);
}
for (ZipEntry entry : toVerify) {
final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
//Slog.d( TAG, "entry n= " + entry.getName());
if (ArrayUtils.isEmpty(entryCerts)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Package " + apkPath + " has no certificates at entry "
+ entry.getName());
}
// make sure all entries use the same signing certs
final Signature[] entrySigs = convertToSignatures(entryCerts);
//Slog.d( TAG, "entrySigs = " + entrySigs[0].toCharsString() );
if (!Signature.areExactMatch(lastSigs, entrySigs)) {
throw new PackageParserException(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
"Package " + apkPath + " has mismatched certificates at entry "
+ entry.getName());
}
}
}
验证单个文件是否签名匹配在loadCertificates中完成了
疑惑点是:
// make sure all entries use the same signing certs
final Signature[] entrySigs = convertToSignatures(entryCerts);
//Slog.d( TAG, "entrySigs = " + entrySigs[0].toCharsString() )
if (!Signature.areExactMatch(lastSigs, entrySigs))
为什么还要比较 Signature.areExactMatch(lastSigs, entrySigs)
make sure all entries use the same signing certs?
确保所有的文件有相同的证书?什么是证书?还是没有完全搞明白.........