Android安装APK完整性校验(V1签名)
app在安装是会采集签名信息来验证apk的完成性
APK安装时签名校验过程
在apk安装时会先对安装包惊醒解析,PackageParser.java类就是负责解析apk的,其中的collectCertificates函数开始收集签名信息,代码如下
private static void collectCertificates(Package pkg, File apkFile, boolean skipVerify)
throws PackageParserException {
final String apkPath = AbsolutePath();
int minSignatureScheme = SigningDetails.SignatureSchemeVersion.JAR;
if (pkg.applicationInfo.isStaticSharedLibrary()) {
// must use v2 signing scheme
minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
}
SigningDetails verified;
if (skipVerify) {
// systemDir APKs are already trusted, save time by not verifying
verified = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
apkPath, minSignatureScheme);
} else {
verified = ApkSignatureVerifier.verify(apkPath, minSignatureScheme);
}
// Verify that entries are signed consistently with the first pkg
// we encountered. Note that for splits, certificates may have
/
/ already been populated during an earlier parse of a base APK.
if (pkg.mSigningDetails == SigningDetails.UNKNOWN) {
pkg.mSigningDetails = verified;
} else {
if (!Signature.areExactMatch(pkg.mSigningDetails.signatures, verified.signatures)) {
throw new PackageParserException(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
apkPath + " has mismatched certificates");
}
}
}
安装过程中 skipVerify 当然是false,所以调⽤
ApkSignatureVerifier.verify(apkPath, minSignatureScheme);
public static PackageParser.SigningDetails verify(String apkPath,
@SignatureSchemeVersion int minSignatureSchemeVersion)
throws PackageParserException {
if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
// V3 and before are older than the requested minimum signing version
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
+ " or newer for package " + apkPath);
}
/
/ first try v3
try {
ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
ApkSignatureSchemeV3Verifier.verify(apkPath);
ApkSignatureSchemeV3Verifier.verify(apkPath);
Certificate[][] signerCerts = new Certificate[][] { s };
Signature[] signerSigs = convertToSignatures(signerCerts);
Signature[] pastSignerSigs = null;
int[] pastSignerSigsFlags = null;
if (vSigner.por != null) {
/
/ populate proof-of-rotation information
pastSignerSigs = new Signature[s.size()];
pastSignerSigsFlags = new int[vSigner.por.flagsList.size()];
for (int i = 0; i < pastSignerSigs.length; i++) {
pastSignerSigs[i] = new Signature(s.get(i).getEncoded());
pastSignerSigsFlags[i] = vSigner.(i);
}
}
return new PackageParser.SigningDetails(
signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
pastSignerSigs, pastSignerSigsFlags);
} catch (SignatureNotFoundException e) {
// not signed with v3, try older if allowed
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) { throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No APK Signature Scheme v3 signature in package " + apkPath, e);
}
} catch (Exception e) {
// APK Signature Scheme v2 signature found but did not verify
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Failed to collect certificates from " + apkPath
+ " using APK Signature Scheme v3", e);
} finally {
}
// redundant, protective version check
if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V2) {
// V2 and before are older than the requested minimum signing version
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No signature found in package of version " + minSignatureSchemeVersion
+ " or newer for package " + apkPath);
}
// try v2
try {
Certificate[][] signerCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
Signature[] signerSigs = convertToSignatures(signerCerts);
return new PackageParser.SigningDetails(
signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V2);
} catch (SignatureNotFoundException e) {
// not signed with v2, try older if allowed
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) { throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No APK Signature Scheme v2 signature in package " + apkPath, e);
}
} catch (Exception e) {
// APK Signature Scheme v2 signature found but did not verify
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Failed to collect certificates from " + apkPath
+ " using APK Signature Scheme v2", e);
} finally {
}
// redundant, protective version check
if (minSignatureSchemeVersion > SignatureSchemeVersion.JAR) {
// V1 and is older than the requested minimum signing version
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
+ " or newer for package " + apkPath);
}
// v2 didn't work, try jarsigner
return verifyV1Signature(apkPath, true);
}
此⽅法中会判断签名版本,此次主要分析V1,也就是
verifyV1Signature(apkPath, true);
private static PackageParser.SigningDetails verifyV1Signature(
String apkPath, boolean verifyFull)
throws PackageParserException {
int objectNumber = verifyFull ? NUMBER_OF_CORES : 1;
boolean isPerfLockAcquired = false;
android获取真正的签名
StrictJarFile[] jarFile = new StrictJarFile[objectNumber];
final ArrayMap<String, StrictJarFile> strictJarFiles = new ArrayMap<String, StrictJarFile>();
try {
final Certificate[][] lastCerts;
final Signature[] lastSigs;
if (sPerfBoost == null) {
sPerfBoost = new BoostFramework();
}
if (sPerfBoost != null && !isPerfLockAcquired && verifyFull) {
//Use big enough number here to hold the perflock for entire PackageInstall session
sPerfBoost.perfHint(BoostFramework.VENDOR_HINT_PACKAGE_INSTALL_BOOST,
null, VERIFICATION_TIME_OUT, -1);
Slog.d(TAG, " Perflock acquired for PackageInstall ");
isPerfLockAcquired = true;
}
// we still pass verify = true to ctor to collect certs, even though we're not checking
// the whole jar.
for (int i = 0; i < objectNumber; i++) {
jarFile[i] = new StrictJarFile(
apkPath,
true, // collect certs
verifyFull); // whether to reject APK with stripped v2 signatures (b/27887819)
}
final List<ZipEntry> toVerify = new ArrayList<>();
// Gather certs l, which every APK must have, as an optimization
// to not need to verify the whole APK when verifyFUll == false.
final ZipEntry manifestEntry = jarFile[0].findEntry(
PackageParser.ANDROID_MANIFEST_FILENAME);
if (manifestEntry == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Package " + apkPath + " has no manifest");
}
lastCerts = loadCertificates(jarFile[0], manifestEntry);
if (ArrayUtils.isEmpty(lastCerts)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Package " + apkPath + " has no certificates at entry "
+ PackageParser.ANDROID_MANIFEST_FILENAME);
}
lastSigs = convertToSignatures(lastCerts);
// fully verify all contents, except l and the META-INF/ files.
if (verifyFull) {
final Iterator<ZipEntry> i = jarFile[0].iterator();
while (i.hasNext()) {
final ZipEntry entry = i.next();
if (entry.isDirectory()) continue;
final String entryName = Name();
if (entryName.startsWith("META-INF/")) continue;
if (entryName.equals(PackageParser.ANDROID_MANIFEST_FILENAME)) continue;
toVerify.add(entry);
}
class VerificationData {
public Exception exception;
public int exceptionFlag;
public boolean wait;
public int index;
public Object objWaitAll;
public boolean shutDown;
}
VerificationData vData = new VerificationData();
vData.objWaitAll = new Object();
final ThreadPoolExecutor verificationExecutor = new ThreadPoolExecutor(
NUMBER_OF_CORES,
NUMBER_OF_CORES,
1,/*keep alive time*/
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());
for (ZipEntry entry : toVerify) {
Runnable verifyTask = new Runnable(){
public void run() {
try {
if (ptionFlag != 0 ) {
Slog.w(TAG, "VerifyV1 exit with exception " + ptionFlag);
return;
}
String tid = String(Thread.currentThread().getId());
StrictJarFile tempJarFile;
synchronized (strictJarFiles) {
tempJarFile = (tid);
if (tempJarFile == null) {
if (vData.index >= NUMBER_OF_CORES) {
vData.index = 0;
}
tempJarFile = jarFile[vData.index++];
strictJarFiles.put(tid, tempJarFile);
}
}
final Certificate[][] entryCerts = loadCertificates(tempJarFile, entry);
if (ArrayUtils.isEmpty(entryCerts)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Package " + apkPath + " has no certificates at entry "
+ Name());
}
// make sure all entries use the same signing certs
final Signature[] entrySigs = convertToSignatures(entryCerts);
if (!Signature.areExactMatch(lastSigs, entrySigs)) {
throw new PackageParserException(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
"Package " + apkPath + " has mismatched certificates at entry "
+ Name());
}
} catch (GeneralSecurityException e) {
synchronized (vData.objWaitAll) {
}
} catch (PackageParserException e) {
synchronized (vData.objWaitAll) {
}
}
}};
synchronized (vData.objWaitAll) {
if (ptionFlag == 0) {
}
}
}
vData.wait = true;
verificationExecutor.shutdown();
while (vData.wait) {
try {
if (ptionFlag != 0 && !vData.shutDown) {
Slog.w(TAG, "verifyV1 Exception " + ptionFlag);
verificationExecutor.shutdownNow();
vData.shutDown = true;
}
vData.wait = !verificationExecutor.awaitTermination(50,
TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Slog.w(TAG,"VerifyV1 interrupted while awaiting all ");
}
}
if (ptionFlag != 0)
throw new ptionFlag,
"Failed to collect certificates from " + apkPath, ption);
}
return new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR);
} catch (GeneralSecurityException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING, "Failed to collect certificates from " + apkPath, e);
} catch (IOException | RuntimeException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Failed to collect certificates from " + apkPath, e);
} finally {
if (isPerfLockAcquired && sPerfBoost != null) {
sPerfBoost.perfLockRelease();
isPerfLockAcquired = false;
Slog.d(TAG, " Perflock released for PackageInstall ");
}
strictJarFiles.clear();
for (int i = 0; i < objectNumber ; i++) {
closeQuietly(jarFile[i]);
}
}
}
此⽅法⽐较长,先看
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论