数字签名是一种权利保护手段,也是一种安全验证措施。在网络中扮演极其重要的角色。
由于需要在Windows平台上给自主软件进行签名,查阅资料,主要借鉴了 makecert 制作数字证书 和 使用SignTool.exe对文件进行数字签名两篇博文,成功完成签名。
本文记录一下签名的过程。
在Windows下微软提供了一套完整的数字签名套件,一般包含在Windows SDK中,可参考 https://docs.microsoft.com/zh-cn/windows-hardware/drivers/devtest/tools-for-signing-drivers 。
主要用到了四个工具:makecert.exe、cert2spc.exe、pvk2pfx.exe 和 signtool.exe。Windows SDK太大,也可以去网上下载单独的exe程序和相应的dll库。
主要步骤:
1.自制数字证书
用于代码签名的数字证书目前没有免费的,有能力的可以去购买。做实验的话,就自己生成一份自签名证书就OK了。signtool签名工具需要pfx格式的证书,因此下面主要是制作一个pfx的证书。Windows SDK内的四个exe都是命令行形式的,看一下帮助信息也不太难。
(1) 首先制作一份通用的自签名证书cer。用makecert,中间会要求输入密码,假设为123
makecert.exe -r -n "CN=宇宙香烟厂" -sv path/yuzhou.pvk path/yuzhou.cer
1 | MakeCert用于创建x.509标准的证书。命令格式 |
这样就得到了通用证书文件yuzhou.cer和对应的私钥文件yuzhou.pvk
(2) 将cer证书转换为SPC发布者证书
cert2spc.exe path/yuzhou.cer path/yuzhou.spc
这样就得到了发布者证书yuzhou.spc
(3) 将公钥(SPC)和私钥合并为一个windows格式的包含私钥的证书pfx
pvk2pfx.exe -pvk path/yuzhou.pvk -spc path/yuzhou.spc -pi 123 -pfx path/yuzhou.pfx1
2
3
4
5Pvk2Pfx将.spc、.cer和.pvk 文件中包含的公钥和私钥信息复制到个人信息交换文件.pfx中。命令格式
pvk2pfx /pvk pvkfilename.pvk [/pi pvkpassword] /spc spcfilename.ext [/pfx pfxfilename.pfx [/po pfxpassword] ]
-pfx 表示生成的pfx文件名
-po 表示pfx的加密密码,如果忽略,则使用原来pvk私钥的密码(-pi)
也可以省去第2步,直接用pvk和cer生成pfx。只要把-spc的输入换为cer文件即可pvk2pfx.exe -pvk path/yuzhou.pvk -spc path/yuzhou.cer -pi 123 -pfx path/yuzhou.pfx
这样一份用于代码签名的数字证书yuzhou.pfx就做好了
2.用数字证书签名并打上时间戳
SignTool用于对文件进行数字签名,验证文件中的签名和时间戳文件。signtool.exe sign -f path/yuzhou.pfx -p 123 /t http://timestamp.digicert.com path/bin.exe
1 | /f 指定PFX文件 |
这样文件就被证书签名并打上了时间戳。别人想假冒你也假冒不了了。
效果
demo文件
没有签名之前
签名之后
可以看到exe文件有了数字签名和时间戳
注意:SignTool只能对windows平台的可执行文件exe、dll进行签名,不能处理linux等其他平台的可执行文件
扩展
走过的坑:沃通有一个签名工具 wosigncode.exe ,但是它总是报错说证书类型不匹配,希望沃通能修复这个bug。
加密 和 签名
加密:通过一种算法将一个数变成另一个数,目的是为了原数据的保密,使其在没有密码的情况下无法得知原文内容;
签名:通常伴随着摘要提取算法(比如md5、sha1、sha2等哈希算法),所有者A对全文或摘要用私钥进行加密,并把加密后的信息和原文一起发送给目标B。目的是保证原文的完整性、防纂改。因为原文一旦有变化,md5值就会有明显的变化,通过比对A发来的解密后的摘要和B自己对原文生成的摘要就能知道原文有没有改变。同时B只能用A的公钥才能解密出A发来的加密摘要,保证了来源的真实性,防止纂改。
比较可知,加密和签名是不同的操作,有不同的目的。加密就是防止原文被偷窥;签名也用到了加密,但目的不是防止原文被偷窥,而是防止原文被纂改。
公钥 和 私钥
X509是公钥证书的标准格式。公钥证书是非对称加密的一种方式,即加密解密用不同的密码。非对称机密算法主要使用RSA算法,即生成公钥和私钥一对密码,公钥和私钥一一对应;用公钥加密的数据只能用对应的私钥解密,同样,用私钥加密的数据也只能用对应的公钥。
对称加密(比如AES)就是加密解密都用同一个密码。
SSL证书 和 代码签名证书
SSL证书
在HTTPS通信中的SSL协议,就是使用了RSA非对称加密算法。服务器端的公钥和网站证书是公开的,任何人都可以获取,但是服务器的私钥是保密的,放在安全的地方。
虽然SSL通信的基础是RSA非对称加密算法,但是在建立通信后却不用RSA算法了。一是因为RSA算法的计算量大,速度慢;二是因为公钥是公开的,私钥是服务器保密的,这样就只能由用户向服务器发送保密数据,而服务器不能向用户发送保密数据,因为用户用公钥加密数据后,只能用服务器的私钥解密,而私钥只有服务器拥有,数据是安全的;而服务器用私钥加密的数据,能够被所有拥有公钥的人解密,而公钥任何人都能获取,数据等于没有加密。所以SSL中是非对称加密、对称加密、密钥交换算法等一系列算法的共同参与。
简单说明HTTPS通信的过程:
1.当浏览器访问网站时,首先下载网站的公钥和证书,通过CA机构验证证书确实属于该网站后才进行后续的数据通信;并确定加密套件
2.浏览器向服务器发送一个随机数cr
3.服务器也生成一个随机数sr,并生成DH算法的交换参数sdh,然后将cr、sr、sdh一起用私钥签名后发送给浏览器
4.浏览器用公钥验证cr、sr、sdh的真实性,如果cr没有篡改则表明通信安全。然后也生成自己的DH算法的交换参数cdh,并用公钥加密后(也可以不加密)发送给服务器
5.服务器和浏览器都用cdh和sdh生成DH算法最后的密钥ps,DH算法的特性保证了两端能够生成相同的密钥ps,这个密钥只有通信双方知道,其他人是无法获得的,也无法通过前面的cr、sr、sdh、cdh等参数计算出来
6.服务器和浏览器将cr、sr、ps合在一起用加密套件确定的算法(一般为伪随机函数)生成相同的密码key,作为对称加密的密码。这个密码同样也只有双方知道,其他人也无法计算出来
7.随后的数据,双方都用对称加密算法的key加密解密数据。
代码签名证书
代码签名是对可执行文件或脚本进行数字签名以确认软件作者及保证软件在签名后未被修改或损坏的措施。此措施使用加密散列来验证真实性和完整性。
作者用自己的证书私钥加密文件的摘要作为签名,然后将签名集成到文件中。也可以打上时间戳,证明文件发布的时间。
用户下载该文件后,可以查看文件的签名,用作者的公钥解密出摘要。如果文件被篡改了,则摘要将不一样,表明文件有问题了。
因为私钥加密后的数据,拥有公钥只能查看,不能更改,这也就保证了只有原作者才能修改。如果其他人修改原文件或者摘要,则公钥解密摘要后的结果与文件摘要运算后的结果是不一样的。
可以看出SSL证书和代码签名证书的使用场景不一样,不能混用。
linux下的数字签名
在linux下一般用openssl套件,用私钥加密/签名文件摘要,用公钥验证。
买不起数字签名证书的可以用openssl自制一份
1 生成私钥openssl genpkey -out privkey.pem
生成的privkey.pem文件内容如下
——-BEGIN RSA PRIVATE KEY——-
MIIEpAIBAAKCAQEAkzVWCEgUnVlhwQ8VEfs2Wmi6srTjrhlPiNUhM0D8x8k0hJtK
rmBgzxHRkyzgBlRdhHqosQ/M81cnV0dxO87jwvAnavlabza965qMDqAZeTnP+5/n
2 从私钥中提取公钥openssl rsa -in privkey.pem -outform PEM -pubout -out pubkey.pem
生成的pubkey.pem文件内容如下
——-BEGIN PUBLIC KEY——-
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA30YfoyFXaqND++Xyyjba
nCO45yCI7mydN6AzRzblFxs6YFv9Dlh14D9ZkFENuMLqWsIM+OEPm0BfI+gTC9sf
签名
1 用私钥对文件摘要签名openssl dgst -sha256 -sign privkey.pem -out myfile.sign myfile
2 用公钥对文件摘要验证openssl dgst -sha256 -verify pubkey.pem -signature myfile.sign myfile
如果正确则Verified OK,否则Verification Failure
也可以用rsautl套件签名
1 私钥签名openssl rsautl -sign -inkey privkey.pem -in myfile -out myfile.sign
2 公钥验证openssl rsautl -verify -inkey pubkey.pem -pubin -in myfile.sign -out myfile.vdiff myfile myfile.v
但是这种方式有文件大小的限制,也就是还要先对文件生成摘要,然后对摘要签名。
对于小文件,rsautl就是直接用私钥将小文件加密成文件副本,然后用公钥把文件副本还原成文件,比较得到的文件和原文件是否一致。
为什么只能直接加密小文件,这是加密原理确定的,数据长度不能大于密钥长度,否则就要分组了。
可以看到,linux下的文件签名是单独生成了一个签名文件,并没有将签名信息附加到原文件内。对比windows下,查看exe文件的属性就能看到签名信息。这是因为linux下的可执行文件格式ELF目前还没有统一的标准存放签名信息,但是有人尝试对ELF扩展两个段,存放签名,并且从内核上支持读取验证这两个段,比如我看到的一篇文献