Merge "v4 signing schema parsing and verification."
This commit is contained in:
committed by
Android (Google) Code Review
commit
780d2bb75d
@@ -61,7 +61,6 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper;
|
||||
import android.content.pm.parsing.AndroidPackage;
|
||||
import android.content.pm.parsing.ApkParseUtils;
|
||||
import android.content.pm.parsing.PackageImpl;
|
||||
import android.content.pm.parsing.PackageInfoUtils;
|
||||
import android.content.pm.parsing.ParsedPackage;
|
||||
import android.content.pm.permission.SplitPermissionInfoParcelable;
|
||||
import android.content.pm.split.DefaultSplitAssetLoader;
|
||||
@@ -5967,12 +5966,14 @@ public class PackageParser {
|
||||
@IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN,
|
||||
SigningDetails.SignatureSchemeVersion.JAR,
|
||||
SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
|
||||
SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3})
|
||||
SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
|
||||
SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4})
|
||||
public @interface SignatureSchemeVersion {
|
||||
int UNKNOWN = 0;
|
||||
int JAR = 1;
|
||||
int SIGNING_BLOCK_V2 = 2;
|
||||
int SIGNING_BLOCK_V3 = 3;
|
||||
int SIGNING_BLOCK_V4 = 4;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
53
core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
Normal file
53
core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,6 +92,24 @@ public class ApkSignatureVerifier {
|
||||
@SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
|
||||
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) {
|
||||
// V3 and before are older than the requested minimum signing version
|
||||
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
|
||||
@@ -99,7 +117,13 @@ public class ApkSignatureVerifier {
|
||||
+ " 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 {
|
||||
return verifyV3Signature(apkPath, verifyFull);
|
||||
} catch (SignatureNotFoundException e) {
|
||||
@@ -141,6 +165,59 @@ public class ApkSignatureVerifier {
|
||||
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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user