浅谈程序的数字签名

理论基础


数字签名它是基于非对称密钥加密技术与数字摘要算法技术的应用,它是一个包含电子文件信息以及发送者身份,并能够鉴别发送者身份以及发送信息是否被篡改的一段数字串。

一段数字签名数字串,它包含电子文件经过Hash编码后产生的数字摘要,即一个Hash函数值以及发送者的公钥和私钥三部分内容。发送方通过私钥加密后发送给接收方,接收方使用公钥解密,通过对比解密后的Hash函数值确定数据电文是否被篡改。

数字签名(又称公钥数字签名)是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。

它是一种类似写在纸上的普通的物理签名,但是在使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。


(图片来源网络)

数字签名方案是一种以电子形式存储消息签名的方法。一个完整的数字签名方案应该由两部分组成:签名算法和验证算法。


(图片来源于网络)



android数字签名


在android的APP应用程序安装过程中,系统首先会检验APP的签名信息,如果发现签名文件不存在或者校验签名失败,系统则会拒绝安装,所以APP应用程序在发布到市场之前一定要进行签名。

在OTA升级中也必须使用到数字签名进行校验,在应用版本迭代必须使用相同的证书签名,不然会生成一个新的应用,导致更新失败。在更新过程中使用相同的证书签名的应用可以共享代码和功能



App安装过程中签名检验的流程:

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

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

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




(图片来源网络)

CERT.RSA包含数字签名以及开发者的数字证书。CERT.RSA里的数字签名是指对CERT.SF的摘要采用私钥加密后的数据;

MANIFEST.MF文件中是APP中每个文件名称和摘要SHA256;

CERT.SF则是对MANIFEST.MF的摘要。



android中的数字签名有2个主要作用:

1、能定位消息确实是由发送方签名并发出来的,其他假冒不了发送方的签名。

2、确定消息的完整性,签名它代表文件的特征,文件发生变化,数字签名的数值也会发送变化。

Anroid中的签名证书不需要权威机构认证,一般是开发者的自签名证书。所以签名信息中会包含有开发者信息,在一定程度上可以防止应用被破解二次打包成山寨的APP应用,所以签名信息也是用于对APP包防二次打包的一个校验功能点。


(上图是android studio中自创建签名的界面)

在 Android Studio中通过上图创建签名信息后,最终会生成一个 .jks 的文件,它是用作证书和私钥的二进制文件。


(上图是反编译工具直接查看app的签名信息),也可以通过jarsigner,jadx,jeb等工具查看app的签名信息。

从上图中可以看到这个APP采用了V1和V2签名信息,Android中的签名目前主要由V1、V2、V3、V4组成的。

v1签名方案:基于 JAR 签名,签名完之后是META-INF 目录下的三个文件:MANIFEST.MF、CERT.SF、CERT.RSA。通过这三个文件校验来确保APP中的每个文件都不被改动。

APK v1的缺点就是META-INF目录下的文件并不在校验范围内,所以之前多渠道打包等都是通过在这个目录下添加文件来实现的。

V2签名方案:它是在Android 7.0系统中引入,为了使 APP可以在 Android 6.0 (Marshmallow) 及更低版本的设备上安装,应先使用 JAR 签名功能对 APP 进行签名,然后再使用 v2 方案对其进行签名。它是一个全文件的签名方案,它能够发现对 APP的受保护部分进行的所有更改,从而有助于加快验证速度并增强完整性保证。

V2签名,它会在 APP文件中插入一个APP签名分块,该分块位于“ZIP 中央目录”部分之前并紧邻该部分。在“APP签名分块”内,v2 签名和签名者身份信息会存储在 APK 签名方案 v2 分块中。



V3签名方案:它是Android 9.0系统中引入,基于 v2签名的升级,Android 9 支持 APK密钥轮替,这使应用能够在 APK 更新过程中更改其签名密钥。为了实现轮替,APK 必须指示新旧签名密钥之间的信任级别。v3 在 APK 签名分块中添加了有关受支持的 SDK 版本和 proof-of-rotation 结构的信息。

下面链接官方对V3签名相关的说明
https://source.android.google.cn/security/apksigning/v3
APK 密钥轮替功能可以参考:
https://developer.android.google.cn/about/versions/pie/android-9.0

V4签名方案:它是在Android 11.0 引入,用来支持 ADB 增量 APK 安装。通过 APK 签名方案 v4 支持与流式传输兼容的签名方案。v4 签名基于根据 APK 的所有字节计算得出的 Merkle 哈希树。

Android 11 将签名存储在单独的 .apk.idsig 文件中。

下面2个链接是官方对V4签名的相关说明
https://source.android.google.cn/security/apksigning/v4
https://developer.android.google.cn/about/versions/11/features


从上面的签名信息截图中,也可以看到android的签名采用的是X.509V3国际标准。

这个标准下约定了签名证书必须包含以下的内容。

1、证书的序列号

2、证书所使用的签名算法

3、证书的发行机构名称,命名规则一般采用X.500格式

4、证书的有效期

5、证书的所有人的名称

6、证书所有人的公开密钥

7、证书发行者对证书的签名



从上图APP的签名信息中数字签名要包含摘要加密算法:MD5、SHA-1、SHA-256

MD5是一种不可逆的加密算法。

SHA1:它是由NISTNSA设计为同DSA一起使用的,它对长度小于264的输入,产生长度为160bit的散列值,因此抗穷举(brute-force)性更好。

SHA-256 是 SHA-1 的升级版,现在 Android 签名使用的默认算法都已经升级到 SHA-256 了。

摘要算法中又涉及到对称加密和非对加密

对称加密就是在加密和解密过程中需要使用同一个密钥

非对称加密使用公钥/私钥中的公钥来加密明文,然后使用对应的私钥来解密密文。



APP中如果没采用加固保护,容易出现二次打包重新签名的山寨APP。

APP中二次打包流程:破解者需要对APK文件做反编译分析,反编译为smali代码,并对某些关键函数或者资源进行修改,再回编译为apk文件并重签名。

常见的对抗二次打包的方案:

1、签名校验

原理:二次打包会篡改签名,通过签名前后的变化可以检测是否被二次打包;但是这种很容易被hook掉。

2、文件校验

原理:二次打包前后apk关键文件hash值比较,判断是否被修改;但是这种很容易被hook掉。

3、核心函数转为jni层实现

原理:java层代码转为jni层实现,jni层代码相对而言篡改难度更大;写大量反射代码降低了开发效率。

 

window数字签名
 

Window的数字签名是微软的一种安全保障机制。

Window数字签名中的签名证书用于验证开发者身份真实性、保护代码的完整性。用户下载软件时,能通过数字签名验证软件来源可信,确认软件、代码没有被非法篡改或植入病毒。所以,软件开发者会在软件发行前使用代码签名证书为软件代码添加数字签名。

对于一个Windows的可执行应用程序,签发数字签名的时候需要计算的数据摘要并不会是程序文件的全部数据,而是要排除一些特定区域的数据。而这些区域当然和PE文件结构有关,具体地,不管是签发时还是校验时计算的hash都会排除一个checksum字段、一个Security数据目录字段以及数字签名证书部分的数据。




Window签名的RSA算法:通过公钥与私钥来判断私钥的合法。

公钥与私钥具有对称性,既可以通过私钥加密,公钥解密,以此来论证私钥持有者的合法身份。也可以通过公钥加密,私钥解密,来对私钥持有者发信息而不被泄露。

由于在交换公钥时免不了遭遇中间人劫持,因此window程序的签名证书,都需要第三方权威机构的认证,并不像android程序一样开发者可以对自己程序签发证书。




(查看某程序的数字签名信息)

从上面截图中看到了摘要算法用到sha1和sha256。

由于SHA-256更强的安全性,现在SHA-256已经作为代码签名证书的行业标准签名算法。

从上图中看到程序拥有2个签名信息,也就是双签名机制。

双签名就是对一个软件做两次签名,先进行SHA1签名,之后再进行SHA2签名的做法就叫做双签名。双签名需要一张支持SHA1和SHA2算法的代码签名证书,利用具备双签名功能的工具导入申请的代码签名证书对软件或应用程序进行双签名,签发后的软件或应用程序就支持SHA1和SHA2签名算法。

Windows10要求使用SHA2算法签名,而Windows7(未更新补丁的)因其兼容性只能使用SHA1算法签名,那么使用一张支持双签SHA1和SHA2算法的代码签名证书就可以实现。

软件签名校验的流程图


(上图来源网络)

Windows系统验证签名流程

1、系统UAC功能开启(用户账户控制功能,默认开启);

2、程序启动时,进行CA校验程序签名信息;

    2.1、使用同样算法对软件产生Hash表

    2.2、使用公钥产生一个Hash表认证摘要

    2.3、比较程序的Hash表认证摘要 与 自己生成的Hash表认证摘要是否一致。

3、程序在window系统执行功能。



数字签名的验证过程本质:

1、通过对要验证的软件创建hash数据;

2、使用发布者的公共密匙来解密被加密的hash数据;

3、最后比较解密的hash和新获得的hash,如果匹配说明签名是正确的,软件没有被修改过。

代码实现校验程序是否有签名,它本质上就是被加密的hash和发布者的数字证书被插入到要签名的软件,最后在进行校验签名信息。


(实现判断程序是否有签名功能)



代码实现可以通过映射文件方式,然后去安装PE文件结构去读取,读取到可选头中的数据目录表,通过判断数据目录表中

的IMAGE_DIRECTORY_ENTRY_SECURITY的虚拟地址和大小不为空,那么就表示改应用程序有签名,因为数据签名都是存在在这个字段中。





同样如果要将某个应用程序的签名信息给抹除了,也是一样的思路,将数据目录表中的IMAGE_DIRECTORY_ENTRY_SECURITY的大小和地址都设置为0即可。




下图通过PE工具,可以查看这个字段Security的虚拟地址和大小不为空那么表示应用程序经过签名的。






小结
 

数字签名不管是在android端还是window端,它都是一种应用程序的身份标志,在安全领域中对应用程序的数字签名校验是一个很常见的鉴别真伪的一个手段。

现在很多杀毒的厂商也都是通过这个数字签名维度,作为一个该应用程序是否可信程序的校验,虽然一些安全杀毒厂商签完名后还是误报毒,那这只能找厂商开白名单了。







作者:小道安全


欢迎关注微信公众号 :小道安全