You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:56:58