签名PDF一压就失效,接压缩需求前先查这5件事

接外包时常碰到这种需求:系统里用户上传的 PDF 自动压一下,存储能省一点。听上去好做,接个压缩库就完事。可这类系统里传上来的有一部分是签过名的电子合同和电子发票。压缩会不会把签名弄坏我心里没底,于是在报工期之前先拿测试文件跑了一轮。结论先放这儿:签过名的文件一压签名必坏,压缩只能放在签名之前。下面是我现在接这类需求前会逐条确认的 5 件事。

测试文件是我自己造的一份单页虚构协议,用自建的测试根证书签名。签完一共 931,980 字节。验签用了 poppler 的 pdfsig 26.08.0 和 pyHanko 0.33.0,两边都只信任这张测试根证书。真实 CA 证书、带时间戳的签名、Acrobat 和福昕里的签名面板我都没测。

先从结构层找已签的签名字段

判断一份 PDF 签没签名,我不靠在字节里搜关键字,对象流打包过的文件里 SigFlags 是搜不到的。判定走结构层:读 AcroForm 里的字段,找类型是 /Sig 并且带 /V 值的那个再读 SigFlags。我那份测试文件读出来 SigFlags 是 3,已签字段是 Sig1。外包接的系统五花八门,客户那边的 PDF 是什么工具生成的我控制不了,多装一个 pypdf 换来不用猜,这笔账划得来。

还有一种情况连结构层也救不了。签名被工具删掉之后,文件里就没有任何痕迹了。Ghostscript 压过的那份我再去查,字节层三个关键字都是 0。结构层也什么都没有,从文件本身看不出它原来签过。上传进来的文件如果已经被别的工具处理过,这一条判断是会失效的。

再看签名有没有覆盖到文件末尾

找到签名字段后我会顺手比一下 ByteRange。签名锁的是两段字节区间,中间留空放签名值。原文件的 ByteRange 是 0 923749 931275 705,第二段 931275 加 705 正好等于文件长度 931,980。末端对得上,说明签完之后没人再往后追加过东西。我后来在这份文件后面再追加一个签名,第一个签名的 ByteRange 就不再到文件尾了。这时候就知道它签完之后还有修订,后面那几条要接着查。

签名后的改动只能增量追加

PDF 允许在文件末尾追加新的一段,原来的字节一个不动。我试了三种追加:再签一个名、加一条文字批注、往页面内容里加一行字。三份文件开头的字节都和原文件一样,%%EOF 从 2 段变成 3 段。原签名覆盖区的哈希也没变,第一个签名照样验得过。作为对照,我把同样的批注换成整份重写的方式保存。那份文件 931,610 字节,两个工具都判签名无效。所以签完名的文件要改,只能走增量追加。

不同验签工具的结论不一样

签名验得过不代表文件没被改过。往页面里加一行字的那份 pdfsig 报的是 Signature is Valid,只额外提了一句签名没覆盖整个文件。pyHanko 会比对前后两个版本,判成 ILLEGAL_MODIFICATIONS。加批注那份 pyHanko 也判了非法修改,看输出是它的默认策略认不出这种方式追加的批注。会签那份 pyHanko 判的是可接受的修改。我的理解是验签工具各有各的策略。给客户交付时得写清楚用哪个工具、哪个版本验的。Acrobat 会怎么判我没测,不敢替它下结论。

压缩一律放在签名之前

最后才是压缩本身。我测了 5 个工具:qpdf 重写、Ghostscript 的 /ebook、iLovePDF 推荐档、PDF24 默认档。第五个是浏览器本地压缩,我用的是图映 ImgIng(imging.cn/)的电子书档,跑在 Chromium 149 上。5 个工具全是整份重写。%%EOF 都只剩 1 段,开头的字节全变了。结果分两类。qpdf、图映和 PDF24 保留了签名字典,ByteRange 的数字原样照抄。可内容已经变了,pdfsig 报 Illegal values in ByteRange array。图映那份只有 64,903 字节,ByteRange 却还写到 931,980。一看就对不上。Ghostscript 和 iLovePDF 更干脆。整个签名域连 SigFlags 一起删了,pdfsig 报文件里没有签名。

处理方式 输出字节 签名字典 pdfsig pyHanko
原文件 931,980 在 Valid INTACT
qpdf 重写 930,748 在 ByteRange 非法 INVALID
Ghostscript /ebook 39,876 被删 无签名 无签名可验
图映 电子书档 64,903 在 ByteRange 非法 INVALID
iLovePDF 推荐档 37,513 被删 无签名 无签名可验
PDF24 默认 50,877 在 ByteRange 非法 INVALID

后一类更麻烦。用户打开文件不会看到签名无效的提示,因为签名已经不存在了。我现在给这类需求的方案是在上传流程里加一步判断。查到已签字段就跳过压缩、原样存档。没签名的才进压缩。要压缩又要签名的文件,让业务方在签名之前压好再签。

真要动手接这类需求,可以先拿一份签过名的样本用 pdfsig 跑一遍。确认了手上的验签结果再决定流程怎么排。

相关推荐
inksci1 小时前
发布Ai生成的html markdown 推荐
前端·javascript·html
Lank_M1 小时前
PDF签完名还能改什么?会签能过,压缩不行
前端
补码缩写1 小时前
压缩前查PDF签名,字节搜索会漏掉对象流
前端
星光不问赶路人1 小时前
Vue 项目类型检查:深入理解 vue-tsc --build
前端·vue.js
修炼的dance1 小时前
压完的PDF验签不报错,是因为签名被删了
前端
ModStart1 小时前
想玩 AI 翻唱?SeedVC 模型 + AIGCPanel 上手
前端
ModStart1 小时前
ProcHub v0.7.0 命令行工具上线,进程、配置与 WSL 都能在终端管理
前端
那年那棵树2 小时前
基于WebGis的人生如旅地图系统
前端·arcgis·vue·课程设计