对于新的签名方案APK Signature Scheme v2,在这篇文章中已经有详细的介绍http://www.tuicool.com/articles/bURRVrj。从这篇文章中可以知道,新的签名方案与旧的签名方案之间的对比是:
图1
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
……
PackageParser pp = new PackageParser();
……
try {
pp.collectCertificates(pkg, parseFlags);
pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
res.setError("Failed collect during installPackageLI", e);
return;
}
……
public static void collectCertificates(Package pkg, int parseFlags)
throws PackageParserException {
collectCertificatesInternal(pkg, parseFlags);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
Package childPkg = pkg.childPackages.get(i);
childPkg.mCertificates = pkg.mCertificates;
childPkg.mSignatures = pkg.mSignatures;
childPkg.mSigningKeys = pkg.mSigningKeys;
}
}
private static void collectCertificatesInternal(Package pkg, int parseFlags)
throws PackageParserException {
pkg.mCertificates = null;
pkg.mSignatures = null;
pkg.mSigningKeys = null;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
collectCertificates(pkg, new File(pkg.baseCodePath), parseFlags);
if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
for (int i = 0; i < pkg.splitCodePaths.length; i++) {
collectCertificates(pkg, new File(pkg.splitCodePaths[i]), parseFlags);
}
}
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private static void collectCertificates(Package pkg, File apkFile, int parseFlags)
1 throws PackageParserException {
1151 final String apkPath = apkFile.getAbsolutePath();
1152
1153 // Try to verify the APK using APK Signature Scheme v2.
1154 boolean verified = false;
1155 {
1156 Certificate[][] allSignersCerts = null;
1157 Signature[] signatures = null;
1158 try {
1159 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV2");
1160 allSignersCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
1161 signatures = convertToSignatures(allSignersCerts);
1162 // APK verified using APK Signature Scheme v2.
1163 verified = true;
1164 } catch (ApkSignatureSchemeV2Verifier.SignatureNotFoundException e) {
1165 // No APK Signature Scheme v2 signature found
1166 } catch (Exception e) {
1167 // APK Signature Scheme v2 signature was found but did not verify
1168 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
1169 "Failed to collect certificates from " + apkPath
1170 + " using APK Signature Scheme v2",
1171 e);
1172 } finally {
1173 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
1174 }
……
StrictJarFile jarFile = null;
1199 try {
1200 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
1201 // Ignore signature stripping protections when verifying APKs from system partition.
1202 // For those APKs we only care about extracting signer certificates, and don't care
1203 // about verifying integrity.
1204 boolean signatureSchemeRollbackProtectionsEnforced =
1205 (parseFlags & PARSE_IS_SYSTEM_DIR) == 0;
1206 jarFile = new StrictJarFile(
1207 apkPath,
1208 !verified, // whether to verify JAR signature
1209 signatureSchemeRollbackProtectionsEnforced);
1210 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
1211
1212 // Always verify manifest, regardless of source
1213 final ZipEntry manifestEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
1214 if (manifestEntry == null) {
1215 throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
1216 "Package " + apkPath + " has no manifest");
1217 }
1218
1219 // Optimization: early termination when APK already verified
1220 if (verified) {
1221 return;
1222 }
……
public StrictJarFile(String fileName,
74 boolean verify,
75 boolean signatureSchemeRollbackProtectionsEnforced)
76 throws IOException, SecurityException {
77 this.nativeHandle = nativeOpenJarFile(fileName);
78 this.raf = new RandomAccessFile(fileName, "r");
79
80 try {
81 // Read the MANIFEST and signature files up front and try to
82 // parse them. We never want to accept a JAR File with broken signatures
83 // or manifests, so it's best to throw as early as possible.
84
if (verify) {
85 HashMap metaEntries = getMetaEntries();
86 this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
87 this.verifier =
88 new StrictJarVerifier(
89 fileName,
90 manifest,
91 metaEntries,
92 signatureSchemeRollbackProtectionsEnforced);
93 Set files = manifest.getEntries().keySet();
94 for (String file : files) {
95 if (findEntry(file) == null) {
96 throw new SecurityException(fileName + ": File " + file + " in manifest does not exist");
97 }
98 }
99
100 isSigned = verifier.readCertificates() && verifier.isSignedJar();
101 } else {
102 isSigned = false;
103 this.manifest = null;
104 this.verifier = null;
105 }
106 } catch (IOException | SecurityException e) {
107 nativeClose(this.nativeHandle);
108 IoUtils.closeQuietly(this.raf);
109 throw e;
110 }
/**
97 * Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates
98 * associated with each signer.
99 *
100 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
101 * @throws SecurityException if a APK Signature Scheme v2 signature of this APK does not verify.
102 * @throws IOException if an I/O error occurs while reading the APK file.
103 */104 public static X509Certificate[][] verify(String apkFile)
105 throws SignatureNotFoundException, SecurityException, IOException {
106 try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
107 return verify(apk);
108 }
109 }
private static X509Certificate[][] verify(RandomAccessFile apk)
121 throws SignatureNotFoundException, SecurityException, IOException {
122 SignatureInfo signatureInfo = findSignature(apk);
123 return verify(apk.getFD(), signatureInfo);
124 }
/**
127 * APK Signature Scheme v2 block and additional information relevant to verifying the signatures
128 * contained in the block against the file.
129 */130 private static class SignatureInfo {
131 /** Contents of APK Signature Scheme v2 block. */132 private final ByteBuffer signatureBlock;
133
134 /** Position of the APK Signing Block in the file. */135 private final long apkSigningBlockOffset;
136
137 /** Position of the ZIP Central Directory in the file. */138 private final long centralDirOffset;
139
140 /** Position of the ZIP End of Central Directory (EoCD) in the file. */141 private final long eocdOffset;
142
143 /** Contents of ZIP End of Central Directory (EoCD) of the file. */144 private final ByteBuffer eocd;
145
146 private SignatureInfo(
147 ByteBuffer signatureBlock,
148 long apkSigningBlockOffset,
149 long centralDirOffset,
150 long eocdOffset,
151 ByteBuffer eocd) {
152 this.signatureBlock = signatureBlock;
153 this.apkSigningBlockOffset = apkSigningBlockOffset;
154 this.centralDirOffset = centralDirOffset;
155 this.eocdOffset = eocdOffset;
156 this.eocd = eocd;
157 }
158 }
private static X509Certificate[][] verify(
203 FileDescriptor apkFileDescriptor,
204 SignatureInfo signatureInfo) throws SecurityException {
205 int signerCount = 0;
206 Map contentDigests = new ArrayMap<>();
207 List signerCerts = new ArrayList<>();
208 CertificateFactory certFactory;
209 try {
210 certFactory = CertificateFactory.getInstance("X.509");
211 } catch (CertificateException e) {
212 throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
213 }
214 ByteBuffer signers;
215 try {
216 signers = getLengthPrefixedSlice(signatureInfo.signatureBlock);
217 } catch (IOException e) {
218 throw new SecurityException("Failed to read list of signers", e);
219 }
220 while (signers.hasRemaining()) {
221 signerCount++;
222 try {
223 ByteBuffer signer = getLengthPrefixedSlice(signers);
224 X509Certificate[] certs = verifySigner(signer, contentDigests, certFactory);
225 signerCerts.add(certs);
226 } catch (IOException | BufferUnderflowException | SecurityException e) {
227 throw new SecurityException(
228 "Failed to parse/verify signer #" + signerCount + " block",
229 e);
230 }
231 }
232
233 if (signerCount < 1) {
234 throw new SecurityException("No signers found");
235 }
236
237 if (contentDigests.isEmpty()) {
238 throw new SecurityException("No content digests found");
239 }
240
241 verifyIntegrity(
242 contentDigests,
243 apkFileDescriptor,
244 signatureInfo.apkSigningBlockOffset,
245 signatureInfo.centralDirOffset,
246 signatureInfo.eocdOffset,
247 signatureInfo.eocd);
248
249 return signerCerts.toArray(new X509Certificate[signerCerts.size()][]);
250 }
ByteBuffer signedData = getLengthPrefixedSlice(signerBlock);
ByteBuffer signatures = getLengthPrefixedSlice(signerBlock);
byte[] publicKeyBytes = readLengthPrefixedByteArray(signerBlock);
private static String getSignatureAlgorithmJcaKeyAlgorithm(int sigAlgorithm) {
704 switch (sigAlgorithm) {
705 case SIGNATURE_RSA_PSS_WITH_SHA256:
706 case SIGNATURE_RSA_PSS_WITH_SHA512:
707 case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
708 case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
709 return "RSA";
710 case SIGNATURE_ECDSA_WITH_SHA256:
711 case SIGNATURE_ECDSA_WITH_SHA512:
712 return "EC";
713 case SIGNATURE_DSA_WITH_SHA256:
714 return "DSA";
715 default:
716 throw new IllegalArgumentException(
717 "Unknown signature algorithm: 0x"
718 + Long.toHexString(sigAlgorithm & 0xffffffff));
719 }
private static void verifyIntegrity(
389 Map expectedDigests,
390 FileDescriptor apkFileDescriptor,
391 long apkSigningBlockOffset,
392 long centralDirOffset,
393 long eocdOffset,
394 ByteBuffer eocdBuf) throws SecurityException {
395
396 if (expectedDigests.isEmpty()) {
397 throw new SecurityException("No digests provided");
398 }
399
400 // We need to verify the integrity of the following three sections of the file:
401 // 1. Everything up to the start of the APK Signing Block.
402 // 2. ZIP Central Directory.
403 // 3. ZIP End of Central Directory (EoCD).
404 // Each of these sections is represented as a separate DataSource instance below.
405
406 // To handle large APKs, these sections are read in 1 MB chunks using memory-mapped I/O to
407 // avoid wasting physical memory. In most APK verification scenarios, the contents of the
408 // APK are already there in the OS's page cache and thus mmap does not use additional
409 // physical memory.
410 DataSource beforeApkSigningBlock =
411 new MemoryMappedFileDataSource(apkFileDescriptor, 0, apkSigningBlockOffset);
412 DataSource centralDir =
413 new MemoryMappedFileDataSource(
414 apkFileDescriptor, centralDirOffset, eocdOffset - centralDirOffset);
415
416 // For the purposes of integrity verification, ZIP End of Central Directory's field Start of
417 // Central Directory must be considered to point to the offset of the APK Signing Block.
418 eocdBuf = eocdBuf.duplicate();
419 eocdBuf.order(ByteOrder.LITTLE_ENDIAN);
420 ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, apkSigningBlockOffset);
421 DataSource eocd = new ByteBufferDataSource(eocdBuf);
422
423 int[] digestAlgorithms = new int[expectedDigests.size()];
424 int digestAlgorithmCount = 0;
425 for (int digestAlgorithm : expectedDigests.keySet()) {
426 digestAlgorithms[digestAlgorithmCount] = digestAlgorithm;
427 digestAlgorithmCount++;
428 }
429 byte[][] actualDigests;
430 try {
431 actualDigests =
432 computeContentDigests(
433 digestAlgorithms,
434 new DataSource[] {beforeApkSigningBlock, centralDir, eocd});
435 } catch (DigestException e) {
436 throw new SecurityException("Failed to compute digest(s) of contents", e);
437 }
438 for (int i = 0; i < digestAlgorithms.length; i++) {
439 int digestAlgorithm = digestAlgorithms[i];
440 byte[] expectedDigest = expectedDigests.get(digestAlgorithm);
441 byte[] actualDigest = actualDigests[i];
442 if (!MessageDigest.isEqual(expectedDigest, actualDigest)) {
443 throw new SecurityException(
444 getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
445 + " digest of contents did not verify");
446 }
447 }
448 }