我使用 iTextPdf 对 PDF 进行签名和完整性检查,由 Alfresco 提供支持
这是签名代码:
public void signItem(NodeRef itemToSign, String signer) {
try{
// retrieving user's public and private key
Certificate chain[] = getCertificate(signer);
PrivateKey pk = getPrivateKey(signer);
String digestAlgorithm = DigestAlgorithms.SHA512;
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
// Getting content of item to sign
InputStream originalInputStream = getNodeRefInputStream(itemToSign);
PdfReader pdfReader = new PdfReader(originalInputStream);
// get an outputStream on the item to sign nodeRef and give to the
// pdfStamper
ByteArrayOutputStream outputStream = getNodeRefOutputStream(itemToSign);
// logger.info("Before" + outputStream);
PdfStamper pdfStamper = PdfStamper.createSignature(pdfReader, outputStream, '\0', new File("temp"), true);
// Creating the appearance
PdfSignatureAppearance appearance = pdfStamper.getSignatureAppearance();
appearance.setReason("freeze");
appearance.setLocation("koosserydesk");
appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "signature space");
// the sign document is subject to future approval signatures
appearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_FORM_FILLING);
// Creating the signature
ExternalDigest digest = new BouncyCastleDigest();
ExternalSignature signature = new PrivateKeySignature(pk, digestAlgorithm, provider.getName());
// signing...
MakeSignature.signDetached(appearance, digest, signature, chain, null, null, null, 0, CryptoStandard.CMS);
// get the signed input stream
InputStream signedInputStream = new ByteArrayInputStream(outputStream.toByteArray());
// replace the itemToSign content with the signed content
ContentWriter writer = getWriter(itemToSign);
writer.putContent(signedInputStream);
} catch (Exception e) {
// do something
}
}
这是完整性检查的代码
public void checkDocIntegrity(NodeRef itemToSign) throws KoosseryDeskServerException {
/** check the integrity of the document **/
ArrayList<String> signatureNames;
PdfPKCS7 pkcs7;
boolean result = false;
try {
InputStream is = getNodeRefInputStream(itemToSign);
PdfReader reader = new PdfReader(is);
AcroFields fields = reader.getAcroFields();
signatureNames = fields.getSignatureNames();
String name = signatureNames.get(0);
System.out.println("Siganture names = " + signatureNames);
System.out.println("Document revision: " + fields.getRevision(name) + " of " + fields.getTotalRevisions());
pkcs7 = fields.verifySignature(name);
result = pkcs7.verify();
System.out.println("Is the document integrity check OK? : "+result);
} catch (Exception e) {
// do something
}
}
当我对使用上述signItem函数签名的文档运行完整性检查时,我总是得到以下输出:
Siganture names = [signature space]
Document revision: 1 of 2
Is the document integrity check OK? : false
我猜完整性检查总是错误的,因为在签名后添加了第二次修订,但是: 我不知道为什么我会收到两个文档修订版,但我没有添加任何注释或其他批准签名。
请告诉我我做错了什么? 谢谢!
最佳答案
简而言之
看起来您的方法getNodeRefOutputStream
返回一个ByteArrayOutputStream
,其中已经包含要开始的原始文档的副本,或者您的方法getWriter
返回一个 ContentWriter
,它附加到现有内容,而不是替换现有内容。
结果是,最终结果文档是(A)原始文档和(B)原始文档加签名的串联。
要解决此问题,请更改或替换错误的方法调用以返回一个对象,该对象可以有效地用压模输出替换原始内容。
详细
分析您的 PDF 很快就会发现它有些损坏,因为它实际上由 组成,而不是预期的两个 修订版(首先是原始 PDF,然后是为签名而创建的添加内容)三个部分(首先是原始 PDF,然后是原始 PDF,然后是为使用交叉引用对其进行签名而创建的附加内容,就好像原始部分在前面但只有一次)。
效果是这样的
- 聚合的交叉引用是错误的,它们指向原始文档的第二个副本中不适当的位置,而不是实际添加的对象,并且 startxref 指针指向原始 PDF 的第二个副本中的某处, 也;因此,Adobe Reader 在后台“修复”了 PDF,从而在关闭文档时引发“是否要保存更改”对话框;
- 签名字节范围中的间隙位于原始文档的第二个副本内的某个位置,特别是不包含编码的签名字节;因此,Adobe Reader - 期望在该间隙中出现签名 - 发出此签名中包含的格式或信息中的错误信号;
- 带符号的字节范围在某种程度上达到了原始版本的第二个副本,导致 iText 报告两个修订,首先是带符号字节范围末尾的数据,然后是超出带符号字节范围的数据;和
- 签名哈希值被破坏,因为签名字节范围不包含原始文档加上签名附加内容(实际签名字节除外),而是包含原始文档加上原始文档第二个副本的一些奇怪部分;这会导致 iText 验证失败。
(您可能需要阅读信息安全堆栈交换上的 this answer 以了解详细信息。)
对于 iText 类来说,这种行为是闻所未闻的。因此,这似乎是由您的代码引起的。
查看您发布的原始文档的重复代码很可能是由于您的代码造成的
- 标记为
getNodeRefOutputStream
返回的ByteArrayOutputStream
(如果该流是使用原始文档的副本初始化的)或 - 将结果 PDF 写入由
getWriter
返回的ContentWriter
(如果该类的putContent
方法实际上附加到现有内容)。
因此,我建议不要将 outputStream
设置为 getNodeRefOutputStream
返回的 ByteArrayOutputStream
来设置 outputStream
到一个空的new ByteArrayOutputStream()
;如果这没有帮助,我建议寻找 getWriter
或 ContentWriter.putContent
的替代品。
关于java - iTextPdf : why misleading number of revisions when verifiying signature?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40418205/