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

iText Java v8.0.3远程PKCS7签名PDF遇篡改报错求解决方案

问题:iText 8.0.3 两阶段PDF签名触发“文档已被篡改”提示

场景描述

使用iText Java v8.0.3试用版实现PDF两阶段签名流程:

  • 先生成文档哈希,发送至签名服务器获取PKCS7签名
  • 将PKCS7签名应用到原PDF文档

遇到的问题:

  • 官方文档提及的PdfPadesSigner类未包含prepareDocumentForSignature()和addSignatureToPreparedDocument()方法
  • 尝试v5.5.13.3版本的PdfPKCS7.getAuthenticatedAttributeBytes方法时,出现签名验证错误,且无签名证书可传入构造器
  • 使用PdfTwoPhaseSigner实现后,最终签名验证提示**"The document has been altered or corrupted since the Signature was applied"**

当前实现代码

步骤1:生成文档哈希

pdfTwoPhaseSigner =  new PdfTwoPhaseSigner(new PdfReader(inputStream), new FileOutputStream(tmpFileName));
    
SignerProperties signerProperties = new SignerProperties();
signerProperties.setPageRect(new Rectangle(36, 748, 144, 780));
signerProperties.setFieldName("swa");
signerProperties.setReason("test reason");
signerProperties.setLocation("test location");
        
SignatureFieldAppearance signatureFieldAppearance = new SignatureFieldAppearance("swa");
signatureFieldAppearance.setContent("Test Name", "Test description");    
signerProperties.setSignatureAppearance(signatureFieldAppearance);        
byte[] digestedDocumentBytes = pdfTwoPhaseSigner.prepareDocumentForSignature(signerProperties, "SHA256", PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached, (8192 * 2 + 2), true);      // ETSI_CAdES_DETACHED
return digestedDocumentBytes;

步骤2:远程签名

调用签名服务器接口,返回byte[] pkcs7(完整PKCS7签名数据)和X509Certificate[] chain(证书链)。

步骤3:应用签名到PDF

CMSContainer cms = new CMSContainer();
SignerInfo signerInfo = new SignerInfo();
signerInfo.setSigningCertificate(chain[0]);
signerInfo.setSignature(pkcs7);
signerInfo.setDigestAlgorithm(new AlgorithmIdentifier(DigestAlgorithms.getAllowedDigest("SHA256")));
cms.addCertificates(chain);
cms.setSignerInfo(signerInfo);
    
outputStream = new FileOutputStream(outputFile);
try (PdfDocument document = new PdfDocument(new PdfReader(new FileInputStream(tmpFileName)))) {
PdfTwoPhaseSigner.addSignatureToPreparedDocument(document, "swa", outputStream, cms);
}
finally {
outputStream.close();
}

解决方案

核心问题

手动构建SignerInfo时错误地将完整PKCS7数据作为签名值传入,导致签名结构不合法,触发篡改检测。远程返回的pkcs7是完整的CMS签名容器,包含所有必要的签名属性,无需手动拆分组装。

修正后的实现代码

步骤1:生成文档哈希(保持原逻辑不变)

PdfTwoPhaseSigner pdfTwoPhaseSigner = new PdfTwoPhaseSigner(new PdfReader(inputStream), new FileOutputStream(tmpFileName));

SignerProperties signerProperties = new SignerProperties();
signerProperties.setPageRect(new Rectangle(36, 748, 144, 780));
signerProperties.setFieldName("swa");
signerProperties.setReason("test reason");
signerProperties.setLocation("test location");

SignatureFieldAppearance signatureFieldAppearance = new SignatureFieldAppearance("swa");
signatureFieldAppearance.setContent("Test Name", "Test description");
signerProperties.setSignatureAppearance(signatureFieldAppearance);

byte[] digestedDocumentBytes = pdfTwoPhaseSigner.prepareDocumentForSignature(
    signerProperties, 
    "SHA256", 
    PdfName.Adobe_PPKLite, 
    PdfName.Adbe_pkcs7_detached, 
    8192 * 2 + 2, 
    true
);
return digestedDocumentBytes;

步骤3:修正签名应用逻辑

直接使用远程返回的PKCS7字节数组构建CMSContainer:

// 替换为远程签名返回的实际数据
byte[] pkcs7Bytes = ...;
X509Certificate[] chain = ...;

// 直接从完整PKCS7数据解析CMSContainer
CMSContainer cms = new CMSContainer(pkcs7Bytes);

try (FileOutputStream outputStream = new FileOutputStream(outputFile);
     PdfDocument document = new PdfDocument(new PdfReader(new FileInputStream(tmpFileName)))) {
    PdfTwoPhaseSigner.addSignatureToPreparedDocument(document, "swa", outputStream, cms);
}

关键注意事项

  1. CMSContainer构建:远程返回的PKCS7是完整签名结构,必须用new CMSContainer(pkcs7Bytes)直接解析,禁止手动拆分组装SignerInfo。
  2. 临时文件一致性:步骤1生成的临时文件必须与步骤3使用的完全一致,中途不能修改或覆盖。
  3. 签名格式匹配:步骤1中指定的签名子过滤器(PdfName.Adbe_pkcs7_detached)必须与签名服务器生成的PKCS7格式匹配,若服务器生成CAdES格式,需改为PdfName.ETSI_CAdES_DETACHED。

内容的提问来源于stack exchange,提问作者Swaran

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 11:18:23