type
status
date
summary
slug
tags
category
icon
password
前置知识
PKCS#7、X.509、DER 、PEM、数字签名、数字证书 这些都是在处理公钥加密、数字签名时,常见的一些名词,但是我一直对他们不甚了解,尤其是前面两个。下面的内容是对它们的一些介绍。DER 和 PEM
首先,二者都是常用的用于公钥密码密钥/证书的编码方式,而且可以相互转换。
DER
DER:Distinguished Encoding Rules,可分辩编码规则。具体来说,DER 是对 ASN.1 值的一种二进制编码方式。
在电信和计算机网络领域,ASN.1(Abstract Syntax Notation One) 是一套标准,是描述数据的表示、编码、传输、解码的灵活的记法。它提供了一套正式、无歧义和精确的规则以描述独立于特定计算机硬件的对象结构。
PEM
PEM:Privacy-Enhanced Mail,隐私增强邮件。简单来说,PEM就是对DER数据进行 Base64 编码后,前后加上头。格式如下:
数字证书 和 X.509
数字证书
数字证书是在 Internet 上唯一地标识人员和资源的电子文件。数字证书可以防止中间人攻击,具体的介绍可以看下面的链接:数字证书简介
X.509
X.509 是数字证书的一种标准格式,通俗来说就是一种数字证书的发布者、公钥等信息的组织形式。下图就是一个 X.509 格式的数字证书的内容:

数字签名 和 PKCS#7
数字签名
什么是数字签名,以及它和数字证书的关系。参考:数字签名是什么?
PKCS#7
在密码学中,
PKCS#7是用于存储签名或加密数据 标准语法。我个人的理解是,在传输签名后的数据时,我们传输的数据需要包括:原文、数字证书(在 PKCS#7 中就是 X.509 格式的)、签名算法(如 RSA Signature with SHA-256)、签名数据等数据,而PKCS#7 就是规定了这些数据的组织形式的一个标准。我们可以使用 
openssl 工具对 PKCS#7格式的数据,进行解析、展示:
正题
这里主要是为了解答我自己的这么几个问题:
- 在日常开发中,通过 PackageInfo.signatures 拿到的是什么?
 
- Android 的签名验证机制是如何工作的?
 
阅读以下内容之前,请先阅读下文:Android 签名机制 v1、v2、v3
好,看完后第二个问题解决了。🤣
问题一
分析
分析源代码写的很乱,可以自己看省流
第一个问题还是不知道,通过对 Android 源代码的最终发现,PackageInfo.signatures 是在 PackageParser 这个类进行的赋值,而 PackageParser 则是使用 ApkSignatureVerifier#verify 这个方法获取的签名信息。这个类中依次尝试使用 
V4、V3、V2、V1对apk 进行签名校验,我们这里挑最具通用性,也最容易理解的 V1 来进行阅读。V1、V2、V3、V4 签名,参考 Android 官方文档:应用签名
首先,获取 apk 文件 META-INF 目录下,以 RSA、DSA、EC 为拓展名的文件,作为"证书文件"。以 CERT.RSA 为例,接下来就会获取 CERT.SF 文件内容,然后校验 
CERT.SF文件内容。前面提到了 PKCS#7,CERT.RSA 就是 PKCS#7 格式的签名数据,其中包括数字证书、签名算法(如 RSA Signature with SHA-256)、签名数据等数据,可以说除了原文该有的基本都有。而原文就是 CERT.SF 的文件内容。
然后是使用签名文件(SF 文件)检验 MF 文件没有被修改过,并把SF文件和其对应的证书链对应起来了,还有把SF文件和SF文件中所包含的文件对应起来(看起来好像是一个文件可能被多个 SF 文件包含,但是我没见过)。
然后,获取 AndroidManifest.xml 文件对应的 ZipEntry, 调用 
loadCertificates函数,结果经过 convertToSignatures 得到的结果就是 PackageInfo.signatures看来关键就在于 loadCertificates 这个函数了,
首先是题外的,readFullyIgnoringContents 是在干什么呢? 读了又不要,很奇怪吧。其实 jarFile.getInputStream 获取的是 StrictJarFile.JarFileInputStream
这里还有一个比较关键的函数 initEntry,它把 MANIFEST.MF 文件的解析结果(包括摘要算法、摘要值)、对这个 entry 进行签名的证书链(一般都是自签名的就一个,也没链)列表(也就是 .SF 列表,一般来说就一个)融入到了 Entry 中。
而 JarFile.JarFileInputStream 的 read 方法是经过重写的
所以这个调用是为了检查 APK 中包含的所有文件,对应的摘要值与 MANIFEST.MF 文件中记录的值是否一致。
回归正题,getCertificateChains 方法获取了转换所需的 Certificate[][] ,他的很简单,其实就是从前面代码中的verifiedEntries中获取对应 entry 的 certChains。
好的总结一下,我的理解就是获取的对 AndroidManifest.xml 这个文件进行签名的证书。在一般情况下,就是 CERT.RSA 文件(PKCS#7格式)中的证书部分。
那么 convertToSignatures 做了什么呢?
省流
好,PackageInfo.signatures 得到其实就是公钥数字证书转换为 DER 格式的二进制数据。
Python 可以直接用 
androguard 获取,注意!其提供的 get_signatures 是不对的,如下图它直接返回了签名数据的全部内容,而我们需要的只有公钥数字证书部分。
