在命令行中使用自签名证书对PDF进行签名
!! 本文代码已上传Github 使用GPL-3.0协议开源 !!
有天我打开网上的PDF文件时,看到这样一行提示:
此文档已经过数字签名
我大惊,我平时会用GPG对文件进行签名,但得到的也只是一个单独的签名文件。难道,GPG创建的签名可以嵌入PDF?我想起以前在Windows上用PDF Reader时见过“签名”菜单,但是那看起来只是手绘了个图形贴上去,看上去好像也不高级,背后好像也没有特别的算法撑腰。这个“数字签名”却有证书,有颁发日期,到期时间,SHA-256指纹,和签名算法,看起来很专业,就像访问HTTPS站点时的SSL证书。
我决定要给自己的PDF也加上这样的数字签名,最好能有个很有信服力的图章,上面写着“signed by Pengbo”。
我在网上展开搜索,却发现linux下,好像少有这样的进行签名的工具,KDE的PDF查看器Okular有这个功能,但它找不到我的证书(恼
那就只能另寻办法啦。
我在网上搜查到的信息告诉我PDF签名需要S/MIME证书,一般由权威CA颁发,价格在……
等等!我签名自己的文件还要付钱?不可能!
CA创建S/MIME证书总要用一些工具吧,我能不能用这样的工具创建自签名证书?
这一工具就是openssl,自己建过网站的人应该对它不陌生,处理HTTPS时多少会碰到。OpenSSL 是一个用于实现安全通信的软件包,它由一组密码学函数库组成。它的主要目标是通过使用公开的密码学算法来保护数据的机密性、完整性和身份验证。它支持对称加密、非对称加密、数字签名、证书管理等功能。
背景知识了解的差不多了,动手吧!
生成
如果你没有openssl这个软件包,请安装它。
首先生成自己的私钥(不知道什么意思的话可以查查非对称加密)
1 | openssl genrsa -aes128 -out myself.key 2048 |
-aes128 表示用AES128算法加密私钥,可选的还有-aes192,-aes256,2048表示私钥长度,越长越安全,但也越慢,我一般用4096位的。
1 | openssl req -new -days 365 -key myself.key -out myself.csr |
这个命令创建证书请求,正常情况下,你应该把这个证书请求发送给CA的工作人员,但今天我们要自己签发,这个待会儿再说。
-days 表示证书有效期,我太懒了,这辈子都不想换,疯狂加0,现在过期时间在34世纪。(这辈子用不完了
别学我,这样不太“正规”,我给你的示例,有效期是1年。
你可能会问:有效期只有一年,那一年以后数字签名是不是就不能验证了呢?别怕,后面我们利用时间戳解决这个问题。
1 | openssl x509 -in myself.csr -out myself.crt -req -signkey myself.key -days 365 |
这一步本该由CA做的,可是贫穷提高了我们的计算机水平,我们现在要自己签发。signkey后面就是我们自己的私钥,如果你像让证书看起来更逼真,可以先自建CA,再用自建CA的私钥签发自己的私钥创建的证书请求,你可以在网上找到教程。
-days参数依然是有效期。
这一步后,你得到了一个x509证书,你可以用它来加密,签名,但是,我们用于PDF签名的证书需要是 PKCS#12 格式的,所以再转下格式。
1 | openssl pkcs12 -export -out myself.pfx -inkey myself.key -in myself.crt |
没什么特别的。
至此,myself.pfx已经可以用于PDF签名了,你可以将这一pfx证书导入Kleopatra(需另外安装),然后打开Okular,在设置>配置后端程序>PDF>签名后端程序选中GnuPG,然后就可以用工具>数字签名了。(当然这是我后来才发现的)
签名
经过艰苦的搜查,我尝试了pdftk,pdfsig等工具,都不太行。
最后我找到了pyhanko,一个专门用于签章pdf的python包,先安装它。
可以用pip安装:
1 | pip install pyhanko[opentype] |
[opentype]表示安装opentype可选项,因为我们要生成可见签章,需要处理字体问题。
然后,上大招:
1 |