压完的PDF验签不报错,是因为签名被删了

上周同事丢给我一份签过字的电子协议,说附件太大发不出去,让我顺手压一下。我手都放到压缩工具上了才想起签名这回事。压完的文件签名还认不认?换个工具会不会不一样?我当时没敢压,让他换了个能传大附件的渠道。这周我专门造了一份测试文件,拿五个常见的压缩和重写工具挨个跑了一遍。结论先放这儿。五个工具压完签名全部失效,其中两个干脆把签名删了,拿验签工具去查连错都不报。

先造一份能验过的签名件

真实合同不能拿来测,我就自己造了一份单页的虚构协议。页面上写着 fictional, for testing only,里面放了一张 1800×1200 的合成图片,整个文件 931,980 字节。证书也是自己用 openssl 建的:一个测试根证书加一个虚构的签名人。签名用 pyHanko 打上去。验签用两个工具,一个是 poppler 的 pdfsig,一个是 pyHanko 自带的校验,两边都只信任我自建的根证书。原文件两边都验过了。pdfsig 报 Signature is Valid,pyHanko 报 INTACT:TRUSTED,UNTOUCHED。

签名覆盖的范围写在签名字典的 ByteRange 里。这份文件是 0 923749 931275 705。翻成人话就是文件开头的一大截加上末尾的一小截。中间空出来的那一截放签名值本身。两段加起来正好到文件末尾。我是中文系转行的,看到这里才算搞懂签名在验什么。它不管页面长什么样,只认这两段字节有没有变过一个比特。

五个工具压完分成两类

同一份 signed.pdf 我依次交给 qpdf(普通重写和对象流打包两种)、Ghostscript 的 /ebook、iLovePDF 推荐档、PDF24 默认档,还有图映的电子书档。图映这一项我用的是图映 ImgIng(imging.cn/)的 PDF 压缩,跑在一台 M4 Mac 的 Chromium 149 里。拿回来的文件我测了两遍:pdfsig 一遍,pyHanko 一遍。结果很整齐地分成了两类。

工具 输出字节 签名字典 pdfsig pyHanko
qpdf 重写 930,748 还在 ByteRange 非法 INVALID
qpdf 对象流 930,178 还在 ByteRange 非法 INVALID
Ghostscript /ebook 39,876 整个没了 没有签名 无签名可验
图映 电子书档 64,903 还在 ByteRange 非法 INVALID
iLovePDF 推荐 37,513 整个没了 没有签名 无签名可验
PDF24 默认 50,877 还在 ByteRange 非法 INVALID

第一类是 qpdf、图映和 PDF24。签名字典还留在文件里,ByteRange 的数字原样照抄,可文件内容已经变了。图映那份最直观。ByteRange 还写着到 931,980 字节为止,实际文件只剩 64,903 字节。pdfsig 直接报 Illegal values in ByteRange array。pyHanko 的说法更具体,原话是 The signature does not cover the entire file,最后判 INVALID。这类文件好歹留着签名的壳,验签时会明明白白报错,至少你知道它坏了。

我又看了每份输出的结构。五个工具的产物都只剩一个 %%EOF,开头的字节也和原文件不一样了。我猜它们都是把整份文件读进来再从头写一遍。删掉没用的对象、把图片换成更小的编码,压缩效果全靠这一步。签名覆盖的那两段字节也就跟着变了。

删掉签名的文件和没签过的一样

第二类是 Ghostscript 和 iLovePDF。它们把整个签名域都删了,iLovePDF 那份连 SigFlags 都没留下。pdfsig 的输出是 does not contain any signatures,pyHanko 那边干脆无签名可验。这句输出很平静,读起来不像出了错,可原文件明明是签过名的。

我觉得这一类比第一类麻烦得多。我翻了 Ghostscript 那份的字节,/ByteRange、/Type /Sig、/SigFlags 一个都搜不到,按结构读也没有表单字典。拿去跟两份从来没签过的公开文件比,一本 1887 年的扫描字典和一份美国地质调查局的火山报告,结果一模一样。也就是说,光拿着压完的文件,谁也看不出它原来签过名。收到的人手里要是没有原件,根本没法发现。有件事我没做,压完的文件我没拿 Acrobat 或福昕打开看签名面板,它们会怎么显示我不知道,我能保证的只有 pdfsig 和 pyHanko 这两个工具的结论。

签名件先压后签

这事说到底不怪哪个工具。压缩就是要动字节,签名就是要锁字节,两者天生冲突。我现在的顺序是先压后签,签好的文件就别再进任何压缩流程,附件太大就换个能传大文件的渠道。签过名的原件我会单独留一份,压出来的东西哪怕看着一切正常,也不拿它当正本。拿到别人发来的 PDF,我会先用 pdfsig 跑一下,命令行就一句 pdfsig 文件名.pdf。报 Signature is Valid 就原样转发,报 does not contain any signatures 的时候,我会多问一句对方手里有没有签过名的版本。

相关推荐
ModStart1 小时前
ProcHub v0.7.0 命令行工具上线,进程、配置与 WSL 都能在终端管理
前端
那年那棵树1 小时前
基于WebGis的人生如旅地图系统
前端·arcgis·vue·课程设计
XZ-0700011 小时前
week9-三元表达式
java·前端·python
叫我铁汁儿吧1 小时前
Redux的原理
前端
云和数据.ChenGuang1 小时前
name=“张三“ age=18 weight=100.3 print (name,age,weight)
java·服务器·前端
代码猎人1 小时前
前端怎么优化兼容性
前端
Nayana1 小时前
《Web 到 HarmonyOS》-- 进程、线程与通信:谁能通、怎么通
前端
叫我铁汁儿吧1 小时前
JS 中的this
前端
狂潮lister2 小时前
狂潮·元理论 | 第5篇:想为你的AI培养个性吗!哈哈哈……(6个测试,教你幻觉的正确玩法)
前端·人工智能