diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java index 64635098f7f48..b933664fcd69a 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -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; - } - } }