pdfbox 中的数字签名默认为“批准签名”,它不阻止文档修改,但任何修改都会使签名失效;若需允许编辑后仍保持签名有效,必须使用“认证签名”并设置 mdp 级别(如 level 2),但该功能在 pdfbox 3.x+ 中需手动注入权限字典,且 adobe acrobat 会严格限制编辑行为。
pdfbox 中的数字签名默认为“批准签名”,它不阻止文档修改,但任何修改都会使签名失效;若需允许编辑后仍保持签名有效,必须使用“认证签名”并设置 mdp 级别(如 level 2),但该功能在 pdfbox 3.x+ 中需手动注入权限字典,且 adobe acrobat 会严格限制编辑行为。
在 Apache PDFBox 中,对签名类型与文档修改权限的关系存在常见误解:“批准签名”(approval signature)本身并不赋予文档可编辑性,它仅表示签署者认可当前内容;而真正控制后续修改能力的是“认证签名”(certification signature)及其关联的 MDP(Modification Detection and Prevention)策略。
根据 PDF 规范(ISO 32000-1, §12.8.1),只有认证签名可指定 DocMDP 权限字典,并通过 TransformMethod 和 P 值(0–3)定义允许的变更类型:
⚠️ 关键事实:
以下是在 PDFBox 3.0.2+ 中创建 Level 2 认证签名(允许表单填写与文本编辑)的最小可行示例:
public void signWithCertification(PDDocument document, OutputStream output, PrivateKey key, Certificate[] certChain, String reason) throws IOException { // 创建认证签名对象 PDSignature signature = new PDSignature(); signature.setType(COSName.SIG); signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED); signature.setName("Certified by System"); signature.setReason(reason); signature.setLocation("Server"); signature.setSignDate(Calendar.getInstance()); signature.setCertification(true); // ⚠️ 必须设为 true 才启用认证模式 // 添加签名前,必须先设置文档级权限(MDP) COSDictionary permissions = new COSDictionary(); permissions.setInt(COSName.P, 2); // 允许表单填写 + 注释 + 签名 permissions.setItem(COSName.TYPE, COSName.getPDFName("SigPermissions")); // 将权限字典挂载到文档根节点(/Perms → /DocMDP) COSDictionary root = document.getDocumentCatalog().getCOSObject(); COSDictionary permsDict = new COSDictionary(); permsDict.setItem(COSName.DOC_MDP, permissions); root.setItem(COSName.PERMS, permsDict); // 执行签名(注意:必须用 saveIncrementalForCertification) document.addSignature(signature, this); document.saveIncrementalForCertification(output); // ✅ 关键:非 saveIncremental()}
? 注意事项:
✅ 总结:您原代码的问题不在于签名逻辑错误,而在于混淆了“批准签名”与“认证签名”的语义本质。若业务场景要求用户后续编辑文本,请将内容重构为 PDF 表单字段,并使用 P=2 认证签名;若需完全自由编辑,则无法在保留有效签名的前提下实现——此时应考虑工作流分离:签名前完成所有编辑,或采用时间戳+哈希存证等替代方案。