Merge "ApkSignatureVerifier refactor."
This commit is contained in:
committed by
Android (Google) Code Review
commit
f6bb512694
@@ -66,46 +66,49 @@ public class ApkSignatureVerifier {
|
||||
public static PackageParser.SigningDetails verify(String apkPath,
|
||||
@SignatureSchemeVersion int minSignatureSchemeVersion)
|
||||
throws PackageParserException {
|
||||
return verifySignatures(apkPath, minSignatureSchemeVersion, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @throws PackageParserException if the APK's signature failed to verify.
|
||||
* or greater is not found, except in the case of no JAR signature.
|
||||
*/
|
||||
public static PackageParser.SigningDetails unsafeGetCertsWithoutVerification(
|
||||
String apkPath, int minSignatureSchemeVersion)
|
||||
throws PackageParserException {
|
||||
return verifySignatures(apkPath, minSignatureSchemeVersion, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the provided APK using all allowed signing schemas.
|
||||
* @return the certificates associated with each signer.
|
||||
* @param verifyFull whether to verify all contents of this APK or just collect certificates.
|
||||
* @throws PackageParserException if there was a problem collecting certificates
|
||||
*/
|
||||
private static PackageParser.SigningDetails verifySignatures(String apkPath,
|
||||
@SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
|
||||
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);
|
||||
+ " or newer for package " + apkPath);
|
||||
}
|
||||
|
||||
// first try v3
|
||||
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV3");
|
||||
try {
|
||||
ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
|
||||
ApkSignatureSchemeV3Verifier.verify(apkPath);
|
||||
Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
|
||||
Signature[] signerSigs = convertToSignatures(signerCerts);
|
||||
Signature[] pastSignerSigs = null;
|
||||
if (vSigner.por != null) {
|
||||
// populate proof-of-rotation information
|
||||
pastSignerSigs = new Signature[vSigner.por.certs.size()];
|
||||
for (int i = 0; i < pastSignerSigs.length; i++) {
|
||||
pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
|
||||
pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
|
||||
}
|
||||
}
|
||||
return new PackageParser.SigningDetails(
|
||||
signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
|
||||
pastSignerSigs);
|
||||
return verifyV3Signature(apkPath, verifyFull);
|
||||
} 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 {
|
||||
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
|
||||
}
|
||||
|
||||
// redundant, protective version check
|
||||
@@ -117,26 +120,14 @@ public class ApkSignatureVerifier {
|
||||
}
|
||||
|
||||
// try v2
|
||||
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV2");
|
||||
try {
|
||||
Certificate[][] signerCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
|
||||
Signature[] signerSigs = convertToSignatures(signerCerts);
|
||||
|
||||
return new PackageParser.SigningDetails(
|
||||
signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V2);
|
||||
return verifyV2Signature(apkPath, verifyFull);
|
||||
} 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 {
|
||||
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
|
||||
}
|
||||
|
||||
// redundant, protective version check
|
||||
@@ -148,14 +139,83 @@ public class ApkSignatureVerifier {
|
||||
}
|
||||
|
||||
// v2 didn't work, try jarsigner
|
||||
return verifyV1Signature(apkPath, true);
|
||||
return verifyV1Signature(apkPath, verifyFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the provided APK and returns the certificates associated with each signer.
|
||||
* Verifies the provided APK using V3 schema.
|
||||
*
|
||||
* @param verifyFull whether to verify all contents of this APK or just collect certificates.
|
||||
* @return the certificates associated with each signer.
|
||||
* @throws SignatureNotFoundException is there are no V3 signatures in the APK
|
||||
* @throws PackageParserException if there was a problem collecting certificates
|
||||
*/
|
||||
private static PackageParser.SigningDetails verifyV3Signature(String apkPath,
|
||||
boolean verifyFull) throws SignatureNotFoundException, PackageParserException {
|
||||
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV3" : "certsOnlyV3");
|
||||
try {
|
||||
ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
|
||||
verifyFull ? ApkSignatureSchemeV3Verifier.verify(apkPath)
|
||||
: ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(
|
||||
apkPath);
|
||||
Certificate[][] signerCerts = new Certificate[][]{vSigner.certs};
|
||||
Signature[] signerSigs = convertToSignatures(signerCerts);
|
||||
Signature[] pastSignerSigs = null;
|
||||
if (vSigner.por != null) {
|
||||
// populate proof-of-rotation information
|
||||
pastSignerSigs = new Signature[vSigner.por.certs.size()];
|
||||
for (int i = 0; i < pastSignerSigs.length; i++) {
|
||||
pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
|
||||
pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
|
||||
}
|
||||
}
|
||||
return new PackageParser.SigningDetails(signerSigs,
|
||||
SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs);
|
||||
} catch (SignatureNotFoundException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
// APK Signature Scheme v3 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 {
|
||||
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the provided APK using V2 schema.
|
||||
*
|
||||
* @param verifyFull whether to verify all contents of this APK or just collect certificates.
|
||||
* @return the certificates associated with each signer.
|
||||
* @throws SignatureNotFoundException is there are no V2 signatures in the APK
|
||||
* @throws PackageParserException if there was a problem collecting certificates
|
||||
*/
|
||||
private static PackageParser.SigningDetails verifyV2Signature(String apkPath,
|
||||
boolean verifyFull) throws SignatureNotFoundException, PackageParserException {
|
||||
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV2" : "certsOnlyV2");
|
||||
try {
|
||||
Certificate[][] signerCerts = verifyFull ? ApkSignatureSchemeV2Verifier.verify(apkPath)
|
||||
: ApkSignatureSchemeV2Verifier.unsafeGetCertsWithoutVerification(apkPath);
|
||||
Signature[] signerSigs = convertToSignatures(signerCerts);
|
||||
return new PackageParser.SigningDetails(signerSigs,
|
||||
SignatureSchemeVersion.SIGNING_BLOCK_V2);
|
||||
} catch (SignatureNotFoundException e) {
|
||||
throw 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 {
|
||||
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the provided APK using JAR schema.
|
||||
* @return the certificates associated with each signer.
|
||||
* @param verifyFull whether to verify all contents of this APK or just collect certificates.
|
||||
* @throws PackageParserException if there was a problem collecting certificates
|
||||
*/
|
||||
private static PackageParser.SigningDetails verifyV1Signature(
|
||||
@@ -277,7 +337,7 @@ public class ApkSignatureVerifier {
|
||||
*
|
||||
* @throws CertificateEncodingException if it is unable to create a Signature object.
|
||||
*/
|
||||
public static Signature[] convertToSignatures(Certificate[][] certs)
|
||||
private static Signature[] convertToSignatures(Certificate[][] certs)
|
||||
throws CertificateEncodingException {
|
||||
final Signature[] res = new Signature[certs.length];
|
||||
for (int i = 0; i < certs.length; i++) {
|
||||
@@ -296,99 +356,29 @@ public class ApkSignatureVerifier {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @throws PackageParserException if the APK's signature failed to verify.
|
||||
* or greater is not found, except in the case of no JAR signature.
|
||||
* Returns the minimum signature scheme version required for an app targeting the specified
|
||||
* {@code targetSdk}.
|
||||
*/
|
||||
public static PackageParser.SigningDetails unsafeGetCertsWithoutVerification(
|
||||
String apkPath, 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);
|
||||
public static int getMinimumSignatureSchemeVersionForTargetSdk(int targetSdk) {
|
||||
if (targetSdk >= Build.VERSION_CODES.R) {
|
||||
return SignatureSchemeVersion.SIGNING_BLOCK_V2;
|
||||
}
|
||||
return SignatureSchemeVersion.JAR;
|
||||
}
|
||||
|
||||
// first try v3
|
||||
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "certsOnlyV3");
|
||||
try {
|
||||
ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
|
||||
ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
|
||||
Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
|
||||
Signature[] signerSigs = convertToSignatures(signerCerts);
|
||||
Signature[] pastSignerSigs = null;
|
||||
if (vSigner.por != null) {
|
||||
// populate proof-of-rotation information
|
||||
pastSignerSigs = new Signature[vSigner.por.certs.size()];
|
||||
for (int i = 0; i < pastSignerSigs.length; i++) {
|
||||
pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
|
||||
pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
|
||||
}
|
||||
}
|
||||
return new PackageParser.SigningDetails(
|
||||
signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
|
||||
pastSignerSigs);
|
||||
} 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 v3 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 {
|
||||
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
|
||||
/**
|
||||
* Result of a successful APK verification operation.
|
||||
*/
|
||||
public static class Result {
|
||||
public final Certificate[][] certs;
|
||||
public final Signature[] sigs;
|
||||
public final int signatureSchemeVersion;
|
||||
|
||||
public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) {
|
||||
this.certs = certs;
|
||||
this.sigs = sigs;
|
||||
this.signatureSchemeVersion = signingVersion;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// first try v2
|
||||
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "certsOnlyV2");
|
||||
try {
|
||||
Certificate[][] signerCerts =
|
||||
ApkSignatureSchemeV2Verifier.unsafeGetCertsWithoutVerification(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 {
|
||||
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
|
||||
}
|
||||
|
||||
// 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, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -416,7 +406,7 @@ public class ApkSignatureVerifier {
|
||||
*/
|
||||
public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
|
||||
throws IOException, SignatureNotFoundException, SecurityException, DigestException,
|
||||
NoSuchAlgorithmException {
|
||||
NoSuchAlgorithmException {
|
||||
// first try v3
|
||||
try {
|
||||
return ApkSignatureSchemeV3Verifier.generateApkVerity(apkPath, bufferFactory);
|
||||
@@ -446,30 +436,4 @@ public class ApkSignatureVerifier {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum signature scheme version required for an app targeting the specified
|
||||
* {@code targetSdk}.
|
||||
*/
|
||||
public static int getMinimumSignatureSchemeVersionForTargetSdk(int targetSdk) {
|
||||
if (targetSdk >= Build.VERSION_CODES.R) {
|
||||
return SignatureSchemeVersion.SIGNING_BLOCK_V2;
|
||||
}
|
||||
return SignatureSchemeVersion.JAR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of a successful APK verification operation.
|
||||
*/
|
||||
public static class Result {
|
||||
public final Certificate[][] certs;
|
||||
public final Signature[] sigs;
|
||||
public final int signatureSchemeVersion;
|
||||
|
||||
public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) {
|
||||
this.certs = certs;
|
||||
this.sigs = sigs;
|
||||
this.signatureSchemeVersion = signingVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user