如何使用iText实现PDF多签名及验证?首签遭篡改问题咨询
iText多PDF签名后首个签名显示篡改的问题排查与解决
我之前在做PDF多签名功能时也踩过一模一样的坑,咱们来拆解下问题根源和解决办法:
核心问题原因
多签名PDF的关键在于每次签名都必须以追加(Append)模式写入,如果没开启这个模式,后续签名会直接覆盖PDF的原有内容(包括第一个签名的哈希校验信息),导致第一个签名的完整性被破坏,自然会被判定为篡改。
从你贴的代码片段来看,只看到了PdfReader的初始化逻辑,没看到后续创建PdfStamper的代码——这大概率就是问题所在。
正确的多签名实现流程
1. 第一次签名(初始化签名流程)
PdfReader reader; try { reader = new PdfReader(inputData); // 注意最后一个参数true代表开启Append模式 PdfStamper stamper = PdfStamper.createSignature(reader, new FileOutputStream("first_signed.pdf"), '\0', null, true); // 配置签名参数(示例) PdfSignatureAppearance appearance = stamper.getSignatureAppearance(); appearance.setFieldName("signature_1"); // 唯一的签名字段名 // 设置签名位置、证书等其他参数... // 执行签名逻辑(比如使用PKCS12证书) ExternalSignature externalSignature = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA256, null); ExternalDigest externalDigest = new BouncyCastleDigest(); MakeSignature.signDetached(appearance, externalDigest, externalSignature, chain, null, null, null, 0, CryptoStandard.CMS); stamper.close(); reader.close(); } catch (IOException | GeneralSecurityException | DocumentException e) { LOG.error("签名失败: " + e.getMessage()); // 异常处理逻辑 }
2. 第二次签名(追加签名)
PdfReader reader; try { reader = new PdfReader("first_signed.pdf"); // 同样必须开启Append模式 PdfStamper stamper = PdfStamper.createSignature(reader, new FileOutputStream("final_signed.pdf"), '\0', null, true); PdfSignatureAppearance appearance = stamper.getSignatureAppearance(); appearance.setFieldName("signature_2"); // 必须使用和第一个不同的字段名 // 配置第二个签名的位置、证书等参数... // 执行签名逻辑 ExternalSignature externalSignature = new PrivateKeySignature(secondPrivateKey, DigestAlgorithms.SHA256, null); ExternalDigest externalDigest = new BouncyCastleDigest(); MakeSignature.signDetached(appearance, externalDigest, externalSignature, secondChain, null, null, null, 0, CryptoStandard.CMS); stamper.close(); reader.close(); } catch (IOException | GeneralSecurityException | DocumentException e) { LOG.error("追加签名失败: " + e.getMessage()); // 异常处理逻辑 }
额外注意事项
- 签名字段名必须唯一:如果两次签名用了同一个字段名,会直接覆盖第一个签名,导致其失效。
- 不要修改签名外的内容:在追加签名时,不要对PDF的其他内容(比如文本、图片、水印)进行修改,除非是通过签名许可的方式操作,否则会破坏之前签名的完整性。
- 验证签名的正确姿势:用iText验证多签名时,要逐个遍历签名字段进行验证:
try { PdfReader reader = new PdfReader("final_signed.pdf"); AcroFields acroFields = reader.getAcroFields(); List<String> signatureNames = acroFields.getSignatureNames(); for (String name : signatureNames) { PdfPKCS7 pkcs7 = acroFields.verifySignature(name); boolean isValid = pkcs7.verify(); LOG.info("签名 [" + name + "] 验证结果: " + (isValid ? "有效" : "无效")); // 还可以进一步验证证书链、签名时间戳等信息 } reader.close(); } catch (IOException | GeneralSecurityException e) { LOG.error("验证签名失败: " + e.getMessage()); }
按照这个流程调整你的代码,应该就能解决第一个签名被提示篡改的问题了。
内容的提问来源于stack exchange,提问作者Thế Hải Nguyễn
相关产品推荐
相关产品推荐

