Android签名的那些事儿

零. 前言

    预期读者:有一定的Android开发基础。

     Android开发者们都知道,Android有一套签名校验机制来防止别人修改apk并重新打包安装。因为apk的内容发生改变,签名结果也不一样,Android系统中是不允许安装同一个包名但不同签名的apk程序的。

    本文先介绍一些相关的背景基础知识,然后再介绍Android和签名流程,最后总结一下流程,让读者对Android和签名流程有个比较清晰的认识。

一. 背景知识

    1. 消息摘要 ( Message Digest )

    消息摘要(Message Digest),又称数字摘要(Digital Digest)或数字指纹(Finger Print)。简单来说,消息摘要就是在消息数据上,执行一个单向的Hash函数,生成一个固定长度的Hash值,这个Hash值即是消息摘要。不同的数据源,消息摘要肯定不一样。

    这个单向的Hash函数就是消息摘要算法。著名的数据摘要算法有RSA公司的MD5算法和SHA-1算法及其大量的变体。

    消息摘要的主要特点有:
    1)无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。例如,应用MD5算法摘要的消息有128个比特位,用SHA-1算法摘要的消息最终有160比特位的输出。
    2)一般来说(不考虑碰撞的情况下),只要输入的原始数据不同,对其进行摘要以后产生的消息摘要也必不相同,即使原始数据稍有改变,输出的消息摘要便完全不同。但是,相同的输入必会产生相同的输出。
    3)具有不可逆性,即只能进行正向的信息摘要,而无法从摘要中恢复出任何的原始消息。

Java中的使用步骤:

//常用算法:MD5、SHA、CRC
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] result = digest.digest(content.getBytes());
//消息摘要的结果一般都是转换成16 进制字符串形式展示
String hex = Hex.encode(result);
//MD5 结果为16 字节(128 个比特位)、转换为16 进制表示后长度是32 个字符
//SHA 结果为20 字节(160 个比特位)、转换为16 进制表示后长度是40 个字符
System.out.println(hex);

  2. 数字签名 ( Signature )

     数字签名方案是一种以电子形式存储消息签名的方法,是非对称加密技术与数字摘要技术的一个具体的应用。

     对于消息的发送者来说,先要生成一对公私钥对,将公钥给消息的接收者。如果消息的发送者有一天想给消息接收者发消息,在发送的信息中,除了要包含原始的消息外,还要加上另外一段消息。这段消息通过如下两步生成:
    1)对要发送的原始消息提取消息摘要
    2)对提取的信息摘要用自己的私钥加密

    通过上面两步生成的消息,就是我们说的数字签名。

    对于消息接收者来说,接收到的信息,包含两个部分,一是原始的消息内容,二是附加的那段数字签名。验证过程如下:

    1)对原始消息部分使用相同摘要算法提取消息摘要;
    2)对附加上的那段数字签名,使用预先得到的公钥解密
    3)比较上面两步各自所得到的两段消息是否一致。如果一致,则表明消息确实是期望的发送者发的,且内容没有被篡改过;相反,如果不一致,则表明消息传送的过程中一定出了问题,消息不可信。

    所以,数字签名的作用就是验证数据来源以及数据完整性

 

    3. 数字证书 ( Certificate )

     前面讲的这种数字签名方法,有一个前提,就是消息的接收者必须要事先得到正确的公钥。如果一开始公钥就被别人篡改了,那坏人就会被你当成好人,而真正的消息发送者给你发的消息会被你视作无效的。而且,很多时候根本就不具备事先沟通公钥的信息通道。那么如何保证公钥的安全可信呢?这就要靠数字证书来解决了。签名文件和证书是成对出现的,二者不可分离。

    数字证书是一个经证书授权中心(Certificate Authority)数字签名的包含公钥拥有者信息以及公钥的文件。数字证书的格式普遍采用的是X.509V3国际标准,一个标准的X.509数字证书通常包含以下内容:

  • 证书的发布机构(Issuer)

    - 该证书是由哪个机构(CA中心)颁发的。

  • 证书的有效期(Validity)

    - 证书的有效期,或者说使用期限。过了该日期,证书就失效了。

  • 证书所有人的公钥(Public-Key)

    - 该证书所有人想要公布出去的公钥。

  • 证书所有人的名称(Subject)

    - 这个证书是发给谁的,或者说证书的所有者,一般是某个人或者某个公司名称、机构的名称、公司网站的网址等。

  • 证书所使用的签名算法(Signature algorithm)

    - 这个数字证书的数字签名所使用的加密算法,这样就可以使用证书发布机构的证书里面的公钥,根据这个算法对数字签名进行解密。

  • 证书发行者对证书的数字签名(Thumbprint)

    - 也就是该数字证书的指纹,用于保证数字证书的完整性,确保证书没有被修改过。

    可以看出,数字证书本身也用到了数字签名技术,只不过签名的内容是整个证书(里面包含了证书所有者的公钥以及其他一些内容)。与普通数字签名不同的是,数字证书的签名者不是随随便便一个普通机构,而是CA机构。一般来说,这些CA机构的根证书已经在设备出厂前预先安装到了你的设备上了。所以,数字证书可以保证证书里的公钥确实是这个证书所有者的,或者证书可以用来确认对方的身份。可见,数字证书主要是用来解决公钥的安全发放问题

   总结一下数字签名与验证的过程。

    数字签名:

    签名验证:

二. Android签名流程

    下面通过apk签名的流程来了解一下Android签名机制。

   1. 签名工具( jarsigner 与 signapk)

    Android应用的签名工具有两种:jarsignersignapk。它们的签名算法没什么区别,主要是签名使用的文件不同

  • jarsigner:jdk自带的签名工具,可以对jar进行签名。使用keystore文件进行签名。生成的签名文件默认使用keystore的别名命名。

      如果我们查看一个keystore文件的内容,会发现里面包含有一个MD5和SHA1摘要,这个就是keystore文件中私钥的数据摘要,这个信息也是我们在申请很多开发平台账号时需要填入的信息。

通过如下命令可以查看keystore文件信息。

keytool -list -v -keystore /Users/victor/Downloads/debug.keystore

  • signapk:Android sdk提供的专门用于Android应用的签名工具。使用pk8、x509.pem文件进行签名。其中pk8是私钥文件,x509.pem是含有公钥的文件。生成的签名文件统一使用“CERT”命名。

(因为这两个工具都是给apk签名的,所以keystore文件和pk8,x509.pem之间是是可以转化的。具体可以参考下面的链接。
   https://blog.csdn.net/ilittleone/article/details/17914995)

   2. 签名流程

    下面,我们通过一个apk来看看具体的签名流程。首先,通过解压apk包可以看到有如下内容。主要是看META-INF文件夹里面的。在META-INF文件夹下有三个文件:MANIFEST.MF、CERT.SF、CERT.RSA。它们就是签名过程中生成的文件,下面就逐一来了解一下这些文件在签名过程中发挥的作用。

  • MANIFEST.MF

    该文件中保存的内容其实就是逐一遍历apk中的所有条目,如果是目录就跳过,如果是一个文件,就用SHA1(或者SHA256)消息摘要算法提取出该文件的摘要然后进行Base64编码后,作为“SHA1-Digest”属性的值写入到MANIFEST.MF文件中的一个块中。该块有一个“Name”属性, 其值就是该文件在apk包中的路径。

  • CERT.SF

    看起来和MANIFEST.MF的内容差不多,但是多了一些条目。里面的属性对应的字段如下表所示。

名称说明
Signature-Version: 1.0
Created-By: 1.0 (Android)
X-Android-APK-Signed: 2
版本信息
SHA1-Digest-Manifest: lHTFY2hWhZevjuV1qSDhq2cDJU8=对MANIFEST.MF整个文件进行消息摘要结果的Base64编码,消息摘要采用SHA1算法

Name: AndroidManifest.xml

该文件在apk包中的路径
SHA1-Digest: 4Pr32iaRA3WhM2BIY9Xb2tsPRDE=SHA1-Digest字段是对MF中对应字段内容的消息摘要结果的Base64编码
  • CERT.RSA

 最后再看看CERT.RSA文件。

看到的是二进制文件,因为RSA文件加密了,所以我们需要用openssl命令查看。

openssl pkcs7 -inform DER -in CERT.RSA -text -noout -print_certs

上面的这些信息,可以对应下图:

这里会把之前生成的CERT.SF文件,用私钥计算出签名, 然后将签名以及包含公钥信息的数字证书一同写入 CERT.RSA 中保存。

 

下面简单总结一下签名过程:

综上,apk的保护链是 .(RSA|DSA|EC) -> .SF -> MANIFEST.MF -> 包中的每一个文件。

 

  • 签名验证过程: 

    签名验证是发生在apk的安装过程中,一共分为三步:

    1)检查apk中包含的所有文件,对应的摘要值与MANIFEST.MF文件中记录的值一致。

    2)使用证书文件(RSA文件)检验签名文件(SF文件)没有被修改过。

    3)使用签名文件(SF文件)检验MF文件没有被修改过。

另外提一下,提供CERT.SF的作用如下面的官方所述:

    主要是提供一个签名验证的兜底机制。

 

    最后,来思考一下,Android中为何要用这种方式进行加密签名?为什么这种签名机制能保证apk被篡改的时候一定能被系统检测出来?

    我们来看一下,如果apk文件被篡改后会发生什么。

    如果你改变了apk包中的任何文件,那么在apk安装校验时,改变后的文件摘要信息与MANIFEST.MF的检验信息不同,于是验证失败。
    如果你再对更改的过的文件相应的算出新的摘要值,然后更改MANIFEST.MF文件里面对应的属性值,那么必定与CERT.SF文件中算出的摘要值不一样,验证失败。
    最后,如果你还不死心,继续计算MANIFEST.MF的摘要值,相应的更改CERT.SF里面的值,那么数字签名值必定与CERT.RSA文件中记录的不一样,还是失败。
    那么能不能继续伪造数字签名呢?不可能,因为没有数字证书对应的私钥
    所以,如果要重新打包后的应用程序能再Android设备上安装,必须对其进行重签名。    

 

    从上面的分析可以得出,只要修改了apk中的任何内容,就必须重新签名,不然会提示安装失败。

----------------------  end  -----------------------

参考:

《Android应用安全防护和逆向分析》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值