需求描述
最近来了一个需求,客户要在 10 份 PDF 上使用电子签名,签完了以后还要合并再进行一次总的签名
众所周知,电子签名当文档本身内容发生变化后,它的 hash 值就变了,和电子签名存储的 hash 值不一致,电子签名就失效了,客户觉得这个失效很难看,希望能保留印章图片,去除签名证书
pdfbox 简单介绍
pdfbox 是一个支持 PDF 文档开发转换的 java 开源库
在我看来,这个开源库最棒的功能就是
- 分割和合并 PDF 文档
- 将 PDF 保存为图片
思路简单描述
PDF 的数据结构可以这样理解,文档的内容中,所有的文本,图片,签名都是一个个对象,对象直接有互相的引用,签名相当于一个图片对象,对象包含了一个引用对象,引用指向一个签名证书对象,签名证书对象也包含一个指向图片对象的引用对象。
我的思路就是过滤出签名证书,直接把这些签名对象删掉,这样就能只保留图片了。
需要说明的是这只是我的简单理解。
代码实现
public class DocumentConvertNoCert { public static void convertNoCert(OutputStream os, byte[] file) { PDDocument pdDocument; try { pdDocument = PDDocument.load(file); PDDocumentCatalog documentCatalog = pdDocument.getDocumentCatalog(); PDAcroForm acroForm = documentCatalog.getAcroForm(); PDFieldTree fieldTree = acroForm.getFieldTree(); List<PDField> fields = new ArrayList<>(); for (PDField pdField : fieldTree) { if (pdField instanceof PDSignatureField){ fields.add(pdField); } } acroForm.flatten(fields,true); pdDocument.save(os); pdDocument.close(); } catch (IOException e) { throw new BizException("转换无证书文档失败",e); } } }
最终效果
我本地的旧版 Adobe pdf 阅读器对于签名图片的点击没有反应,和普通图片一样。
客户的新版 pdf 阅读点击签名图片时会显示签名数据错误。
之后使用 Adobe pdf 阅读器进行文件合并后,文件就看上去正常了,也可以继续正常地签名。