android7.0及以上版本签名校验过程详解
对于新的签名方案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<String, byte[]> 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<String> 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 }
(8)
private static X509Certificate[][] verify(RandomAccessFile apk)
121 throws SignatureNotFoundException, SecurityException, IOException {
122 SignatureInfo signatureInfo = findSignature(apk);
123 return verify(apk.getFD(), signatureInfo);
124 }
(9)
/**
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 }
(10)
private static X509Certificate[][] verify(
203 FileDescriptor apkFileDescriptor,
204 SignatureInfo signatureInfo) throws SecurityException {
205 int signerCount = 0;
206 Map<Integer, byte[]> contentDigests = new ArrayMap<>();
207 List<X509Certificate[]> 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 }
(11)
private static void verifyIntegrity(
389 Map<Integer, byte[]> 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 }
- 每个部分的内容分成连续的1 MB大小的块。最后一大块将是较短的,if段长度不是1 MB的倍数。没有块是为空(零长度)段制作的。
- 计算每个块的摘要信息
- 并将所有部分的摘要按照顺序连起来。
apk签名怎么兼容7.0的v2签名方式,及6.0及以下的签名方式:
APK 签名方案 v2 是在 Android 7.0 (Nougat) 中引入的。为了使 APK 可在 Android 6.0 (Marshmallow) 及更低版本的设备上安装,应先使用 JAR 签名功能对 APK 进行签名,然后再使用 v2 方案对其进行签名。
android7.0及以上版本签名校验过程详解相关推荐
- Android签名与校验过程详解
原文:https://blog.csdn.net/gulinxieying/article/details/78677487 目 录 一.签名与校验原理概要 2 1.数字签名简介 2 2. ...
- mysql8.0.20 64位安装教程_MySQL8.0.20压缩版本安装教程图文详解
1.mysql下载地址: http://ftp.ntu.edu.tw/mysql/downloads/mysql-cluster-8.0/ 2.解压以后放在一个文件夹里面,创建my.ini配置文件: ...
- 【甘道夫】HBase(0.96以上版本)过滤器Filter详解及实例代码
说明: 本文参考官方Ref Guide,Developer API和众多博客,并结合实测代码编写,详细总结HBase的Filter功能,并附上每类Filter的相应代码实现. 本文尽量遵从Ref Gu ...
- Faster-RCNN.pytorch的搭建、使用过程详解(适配PyTorch 1.0以上版本)
Faster-RCNN.pytorch的搭建.使用过程详解 引言 faster-rcnn pytorch代码下载 faster-rcnn pytorch配置过程 faster-rcnn pytorch ...
- Android签名机制之---签名验证过程详解
一.前言 今天是元旦,也是Single Dog的嚎叫之日,只能写博客来祛除寂寞了,今天我们继续来看一下Android中的签名机制的姊妹篇:Android中是如何验证一个Apk的签名.在前一篇文章中我们 ...
- MySQL8.0二进制免编译部署过程详解(二)
一.背景介绍 本文主要介绍MySQL二进制免编译软件包的安装过程详解,之所以选择二进制安装包部署MySQL8.0,是因为官方版本已经内置所有功能,在安装的时候可以指定数据库安装路径. 目前官网MySQ ...
- Android 7.0 Audio的Resample过程详解
Android 7.0 Audio的Resample过程详解 Qidi 2017.02.23 (Markdown & Haroopad) [前言] 处理过音频文件的工程师都知道音频数据存在采样 ...
- Android中mesure过程详解 (结合Android 4.0.4 最新源码)
如何遍历并绘制View树?之前的文章Android中invalidate() 函数详解(结合Android 4.0.4 最新源码)中提到invalidate()最后会发起一个View树遍历的请求,并通 ...
- Android中layout过程详解 (结合Android 4.0.4 最新源码)
上一篇文章Android中mesure过程详解 (结合Android 4.0.4 最新源码)介绍了View树的measure过程,相对与measure过程,本文介绍的layout过程要简单多了,正如l ...
最新文章
- 我是LinkedIn的SRE,我把LinkedIn搞挂了
- github使用的一点记录。
- Python中dict用法详解
- UA MATH575B 数值分析下 计算统计物理例题1
- 【深度学习】Batch Normalization(BN)超详细解析
- 新手入门python的注意事项_【新手入门Python语言的方法】
- css background 一半_CSS---阴阳图
- 华人团队再获ACL最高奖,这次来自字节跳动的NLP基础研究
- 地铁译:Spark for python developers --- 搭建Spark虚拟环境 4...
- ubuntu上常用的软件安装
- 知识点记录:李群李代数,微分流形,微分几何,图论
- 数学建模—一元回归分析
- 米思齐(Mixly)初体验—触摸式开关
- 微信卡券开发错误自排查参考文档
- sgu 309 Real Fun
- Css、less和Sass(SCSS)的区别
- PLSQL连接登录失败
- Solver 配置详解
- win10 启动自动修复失败
- 关于友盟9.3.8版本集成QQ无效问题