Loading core/java/android/content/pm/PackageParser.java +3 −2 Original line number Original line Diff line number Diff line Loading @@ -61,7 +61,6 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.parsing.AndroidPackage; import android.content.pm.parsing.AndroidPackage; import android.content.pm.parsing.ApkParseUtils; import android.content.pm.parsing.ApkParseUtils; import android.content.pm.parsing.PackageImpl; import android.content.pm.parsing.PackageImpl; import android.content.pm.parsing.PackageInfoUtils; import android.content.pm.parsing.ParsedPackage; import android.content.pm.parsing.ParsedPackage; import android.content.pm.permission.SplitPermissionInfoParcelable; import android.content.pm.permission.SplitPermissionInfoParcelable; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.DefaultSplitAssetLoader; Loading Loading @@ -5967,12 +5966,14 @@ public class PackageParser { @IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN, @IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN, SigningDetails.SignatureSchemeVersion.JAR, SigningDetails.SignatureSchemeVersion.JAR, SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2, SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2, SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3}) SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4}) public @interface SignatureSchemeVersion { public @interface SignatureSchemeVersion { int UNKNOWN = 0; int UNKNOWN = 0; int JAR = 1; int JAR = 1; int SIGNING_BLOCK_V2 = 2; int SIGNING_BLOCK_V2 = 2; int SIGNING_BLOCK_V3 = 3; int SIGNING_BLOCK_V3 = 3; int SIGNING_BLOCK_V4 = 4; } } @Nullable @Nullable Loading core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java 0 → 100644 +53 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.util.apk; import android.os.incremental.IncrementalManager; import java.io.File; import java.security.cert.Certificate; import sun.security.pkcs.PKCS7; import sun.security.pkcs.ParsingException; /** * APK Signature Scheme v4 verifier. * * @hide for internal use only. */ public class ApkSignatureSchemeV4Verifier { /** * Extracts APK Signature Scheme v4 signatures of the provided APK and returns the certificates * associated with each signer. */ public static Certificate[] extractCertificates(String apkFile) throws SignatureNotFoundException, SecurityException { final byte[] rawSignature = IncrementalManager.unsafeGetFileSignature( new File(apkFile).getAbsolutePath()); if (rawSignature == null || rawSignature.length == 0) { throw new SignatureNotFoundException("Failed to obtain raw signature from IncFS."); } try { PKCS7 pkcs7 = new PKCS7(rawSignature); return pkcs7.getCertificates(); } catch (ParsingException e) { throw new SecurityException("Failed to parse signature and extract certificates", e); } } } core/java/android/util/apk/ApkSignatureVerifier.java +78 −1 Original line number Original line Diff line number Diff line Loading @@ -92,6 +92,24 @@ public class ApkSignatureVerifier { @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) throws PackageParserException { throws PackageParserException { if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V4) { // 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 v4 try { return verifyV4Signature(apkPath, minSignatureSchemeVersion, verifyFull); } catch (SignatureNotFoundException e) { // not signed with v4, try older if allowed if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V4) { throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No APK Signature Scheme v4 signature in package " + apkPath, e); } } if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) { if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) { // V3 and before are older than the requested minimum signing version // V3 and before are older than the requested minimum signing version throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, Loading @@ -99,7 +117,13 @@ public class ApkSignatureVerifier { + " or newer for package " + apkPath); + " or newer for package " + apkPath); } } // first try v3 return verifyV3AndBelowSignatures(apkPath, minSignatureSchemeVersion, verifyFull); } private static PackageParser.SigningDetails verifyV3AndBelowSignatures(String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) throws PackageParserException { // try v3 try { try { return verifyV3Signature(apkPath, verifyFull); return verifyV3Signature(apkPath, verifyFull); } catch (SignatureNotFoundException e) { } catch (SignatureNotFoundException e) { Loading Loading @@ -141,6 +165,59 @@ public class ApkSignatureVerifier { return verifyV1Signature(apkPath, verifyFull); return verifyV1Signature(apkPath, verifyFull); } } /** * Verifies the provided APK using V4 schema. * * @param verifyFull whether to verify all contents of this APK or just collect certificates. * @return the certificates associated with each signer. * @throws SignatureNotFoundException if there are no V4 signatures in the APK * @throws PackageParserException if there was a problem collecting certificates */ private static PackageParser.SigningDetails verifyV4Signature(String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) throws SignatureNotFoundException, PackageParserException { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV4" : "certsOnlyV4"); try { Certificate[] certs = ApkSignatureSchemeV4Verifier.extractCertificates(apkPath); Certificate[][] signerCerts = new Certificate[][]{certs}; Signature[] signerSigs = convertToSignatures(signerCerts); if (verifyFull) { // v4 is an add-on and requires v2/v3 signature to validate against its certificates final PackageParser.SigningDetails nonstreaming = verifyV3AndBelowSignatures( apkPath, minSignatureSchemeVersion, false); if (nonstreaming.signatureSchemeVersion <= SignatureSchemeVersion.JAR) { throw new SecurityException( "V4 signing block can only be verified along with V2 and above."); } if (nonstreaming.signatures.length == 0 || nonstreaming.signatures.length != signerSigs.length) { throw new SecurityException("Invalid number of signatures in " + nonstreaming.signatureSchemeVersion); } for (int i = 0, size = signerSigs.length; i < size; ++i) { if (!nonstreaming.signatures[i].equals(signerSigs[i])) { throw new SecurityException("V4 signature certificate does not match " + nonstreaming.signatureSchemeVersion); } } } return new PackageParser.SigningDetails(signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V4); } catch (SignatureNotFoundException e) { throw e; } catch (Exception e) { // APK Signature Scheme v4 signature found but did not verify throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Failed to collect certificates from " + apkPath + " using APK Signature Scheme v4", e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } /** /** * Verifies the provided APK using V3 schema. * Verifies the provided APK using V3 schema. * * Loading Loading
core/java/android/content/pm/PackageParser.java +3 −2 Original line number Original line Diff line number Diff line Loading @@ -61,7 +61,6 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.parsing.AndroidPackage; import android.content.pm.parsing.AndroidPackage; import android.content.pm.parsing.ApkParseUtils; import android.content.pm.parsing.ApkParseUtils; import android.content.pm.parsing.PackageImpl; import android.content.pm.parsing.PackageImpl; import android.content.pm.parsing.PackageInfoUtils; import android.content.pm.parsing.ParsedPackage; import android.content.pm.parsing.ParsedPackage; import android.content.pm.permission.SplitPermissionInfoParcelable; import android.content.pm.permission.SplitPermissionInfoParcelable; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.DefaultSplitAssetLoader; Loading Loading @@ -5967,12 +5966,14 @@ public class PackageParser { @IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN, @IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN, SigningDetails.SignatureSchemeVersion.JAR, SigningDetails.SignatureSchemeVersion.JAR, SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2, SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2, SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3}) SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4}) public @interface SignatureSchemeVersion { public @interface SignatureSchemeVersion { int UNKNOWN = 0; int UNKNOWN = 0; int JAR = 1; int JAR = 1; int SIGNING_BLOCK_V2 = 2; int SIGNING_BLOCK_V2 = 2; int SIGNING_BLOCK_V3 = 3; int SIGNING_BLOCK_V3 = 3; int SIGNING_BLOCK_V4 = 4; } } @Nullable @Nullable Loading
core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java 0 → 100644 +53 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.util.apk; import android.os.incremental.IncrementalManager; import java.io.File; import java.security.cert.Certificate; import sun.security.pkcs.PKCS7; import sun.security.pkcs.ParsingException; /** * APK Signature Scheme v4 verifier. * * @hide for internal use only. */ public class ApkSignatureSchemeV4Verifier { /** * Extracts APK Signature Scheme v4 signatures of the provided APK and returns the certificates * associated with each signer. */ public static Certificate[] extractCertificates(String apkFile) throws SignatureNotFoundException, SecurityException { final byte[] rawSignature = IncrementalManager.unsafeGetFileSignature( new File(apkFile).getAbsolutePath()); if (rawSignature == null || rawSignature.length == 0) { throw new SignatureNotFoundException("Failed to obtain raw signature from IncFS."); } try { PKCS7 pkcs7 = new PKCS7(rawSignature); return pkcs7.getCertificates(); } catch (ParsingException e) { throw new SecurityException("Failed to parse signature and extract certificates", e); } } }
core/java/android/util/apk/ApkSignatureVerifier.java +78 −1 Original line number Original line Diff line number Diff line Loading @@ -92,6 +92,24 @@ public class ApkSignatureVerifier { @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) throws PackageParserException { throws PackageParserException { if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V4) { // 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 v4 try { return verifyV4Signature(apkPath, minSignatureSchemeVersion, verifyFull); } catch (SignatureNotFoundException e) { // not signed with v4, try older if allowed if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V4) { throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No APK Signature Scheme v4 signature in package " + apkPath, e); } } if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) { if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) { // V3 and before are older than the requested minimum signing version // V3 and before are older than the requested minimum signing version throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, Loading @@ -99,7 +117,13 @@ public class ApkSignatureVerifier { + " or newer for package " + apkPath); + " or newer for package " + apkPath); } } // first try v3 return verifyV3AndBelowSignatures(apkPath, minSignatureSchemeVersion, verifyFull); } private static PackageParser.SigningDetails verifyV3AndBelowSignatures(String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) throws PackageParserException { // try v3 try { try { return verifyV3Signature(apkPath, verifyFull); return verifyV3Signature(apkPath, verifyFull); } catch (SignatureNotFoundException e) { } catch (SignatureNotFoundException e) { Loading Loading @@ -141,6 +165,59 @@ public class ApkSignatureVerifier { return verifyV1Signature(apkPath, verifyFull); return verifyV1Signature(apkPath, verifyFull); } } /** * Verifies the provided APK using V4 schema. * * @param verifyFull whether to verify all contents of this APK or just collect certificates. * @return the certificates associated with each signer. * @throws SignatureNotFoundException if there are no V4 signatures in the APK * @throws PackageParserException if there was a problem collecting certificates */ private static PackageParser.SigningDetails verifyV4Signature(String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) throws SignatureNotFoundException, PackageParserException { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV4" : "certsOnlyV4"); try { Certificate[] certs = ApkSignatureSchemeV4Verifier.extractCertificates(apkPath); Certificate[][] signerCerts = new Certificate[][]{certs}; Signature[] signerSigs = convertToSignatures(signerCerts); if (verifyFull) { // v4 is an add-on and requires v2/v3 signature to validate against its certificates final PackageParser.SigningDetails nonstreaming = verifyV3AndBelowSignatures( apkPath, minSignatureSchemeVersion, false); if (nonstreaming.signatureSchemeVersion <= SignatureSchemeVersion.JAR) { throw new SecurityException( "V4 signing block can only be verified along with V2 and above."); } if (nonstreaming.signatures.length == 0 || nonstreaming.signatures.length != signerSigs.length) { throw new SecurityException("Invalid number of signatures in " + nonstreaming.signatureSchemeVersion); } for (int i = 0, size = signerSigs.length; i < size; ++i) { if (!nonstreaming.signatures[i].equals(signerSigs[i])) { throw new SecurityException("V4 signature certificate does not match " + nonstreaming.signatureSchemeVersion); } } } return new PackageParser.SigningDetails(signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V4); } catch (SignatureNotFoundException e) { throw e; } catch (Exception e) { // APK Signature Scheme v4 signature found but did not verify throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Failed to collect certificates from " + apkPath + " using APK Signature Scheme v4", e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } /** /** * Verifies the provided APK using V3 schema. * Verifies the provided APK using V3 schema. * * Loading