数字签名与数字证书
讲这个”漏洞“之前先讲一下数字签名的原理,理解一般的数字签名验证过程。下面是数字签名的相关概念和验证过程:
数字签名:对一段数据摘要使用私钥进行加密,公钥进行解密校验
数字证书:对数字签名的解密公钥和身份信息使用CA的私钥进行加密,系统信任的CA公钥进行解密
如上图所示,数字签名用来保护所有者的数据,所以从这些数据出发,所有者签发数据的数字签名过程是要先计算这些数据的摘要(也就是对这些数据做个hash运算,目前由于SHA-1开始不安全,正在逐渐换成SHA-2算法),然后使用所有者的私钥进行加密(目前主流还是RSA,也有一些ECC或其他算法),加密后的数据带上所有者的信息按照一定的标准格式组织好其实就是所谓的数字签名。
而接收方(使用者)要验证这个数字签名,一般通过数字证书,因为其中包含两个重要的信息,一个就是用于解密数字签名的公钥(通过这个公钥才能正确解密出数据所有者事先加密好的数据摘要值,用于验证比对数据的一致性),另一个就是指明了这个公钥的所有者的信息(当然要和数字签名所有者的信息一致)。
而数字证书其实是由第三方的可信机构颁发给数字签名所有者的一份数据文件,生成算法和数字签名类似,只不过它是把所有者的公钥和身份这些固定信息数据进行了加密返回给了所有者,此时所有者的身份正常是得到了可信机构的认证了。最后一个环节,数字签名、数字证书通常都是附加到所有者的数据后面一起传送给了使用者,但是如何解密数字证书来进行验证呢?我的理解是,这个要依托于系统,比如windows,他们会维护一批可信机构的列表,在验证证书的时候,就会先从这个列表里校验和获取证书签发机构的身份和公钥,并返回到用户系统进行存储和使用。
正是通过这样一整套的相对完善的机制,最终比较靠谱的保证了接收数据的有效性。而本文所指的Windows程序的数字签名,其实就是指所有者要保护的数据是一些Windows下的可执行程序这样一种情况。
可被利用的“漏洞”
从上面的签名校验流程看,由于整个过程涉及环节比较多,相应的每个环节都存在有被攻破的可能性,比如hash碰撞、根证书伪造等,但是本文不涉及此类问题。实际上,数字签名仍然是保护数据有效的一种可靠的途径,只要操作得当,算法强度足够,被攻破的可能性还是比较低。而本文所说的“漏洞”,也不是上面整个验证过程的漏洞,下面就来看一看这个“漏洞”是什么意思吧。
找一个带有数字签名的程序来实验,就以微软官方的库文件msvcr100.dll为例。右键点击文件属性,可以看到有一个数字签名的标签,依次点击可以查看到下面的签名有效状态,表示这个程序的数字签名验证成功,“程序数据没有被篡改”(这里加引号说明理解需要谨慎,详看下文)。
然后修改这个文件,比如在尾部随便添加几个字节的数据,再次查看数字签名的状态后如下图所示,说明系统检测到文件被篡改,数字签名验证为无效。
这就给我们造成一种感觉,数字签名真的可以保证数据不被篡改,只要数据受到篡改,就会被系统的验证机制检测到并提示签名无效。然而,这种感觉其实并不靠谱,因为我们实际上只是对签名文件添加了一些数据,并没有修改到程序的原始数据区域的部分,理论上只要系统能够找到程序的数字签名并通过上述的验证流程,那么数字签名就仍是有效的,受保护数据没有遭到篡改。那么,有没有办法让这个添加了数据的文件去正确识别数字签名并显示签名有效呢,答案是肯定的,而这也正是本文所指的“漏洞”。
这里直接给出这个“漏洞”的信息:对于一个Windows的可执行程序,签发数字签名的时候需要计算的数据摘要并不会是程序文件的全部数据,而是要排除一些特定区域的数据。而这些区域当然和PE文件结构有关,具体地,不管是签发时还是校验时计算的hash都会排除一个checksum字段、一个Security数据目录字段以及数字签名证书部分的数据。至于原因,当然是为了合理地组织pe程序的数字签名,符合pe文件格式的标准了。下面是三个字段的相关信息:
struct IMAGE_NT_HEADERS
…
struct IMAGE_OPTIONAL_HEADER32
…
DWORD CheckSum //pe映像校验和,dll常用
…
struct DATA_DIR Security
DWORD VirtualAddress //指向数字签名证书数据结构_WIN_CERTIFICATE
DWORD Size //_WIN_CERTIFICATE结构大小
struct _WIN_CERTIFICATE
DWORD dwLength //本结构数据的大小,一般和Security.Size相等
WORD wRevision
WORD wCertificateType
BYTE bCertificate[ANYSIZE_ARRAY] //数字签名证书数据