我需要使用外部摘要签署pdf文档。这个摘要是用智能卡签名的,然后将签名摘要插入到文档中。签名文档包含带文本的矩形但签名无效。
Adobe pdf阅读器说它的" BER编码问题"并且甚至不显示证书信息,pdf工作室查看器显示证书信息,但表示"文档已被修改"。
// Create reader & stamper
PdfReader pdfReader = new PdfReader("sample.pdf");
pdfReader.setAppendable(true);
FileOutputStream fileOutputStream = new FileOutputStream("out.pdf");
PdfStamper stp = PdfStamper.createSignature(pdfReader, fileOutputStream, '\0', null, true);
// Create the appearance
PdfSignatureAppearance sap = stp.getSignatureAppearance();
sap.setVisibleSignature(new Rectangle(0, 500, 100, 600), 1, "Signature1");
sap.setLayer2Text("Signed by " + certificate.getSubjectX500Principal().getName());
sap.setCertificate(certificate);
sap.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
dic.setReason(sap.getReason());
dic.setLocation(sap.getLocation());
dic.setContact(sap.getContact());
dic.setDate(new PdfDate(sap.getSignDate()));
sap.setCryptoDictionary(dic);
// Create space in document for signature
Integer reservedSpace = 8192;
HashMap<PdfName, Integer> exc = new HashMap<>();
exc.put(PdfName.CONTENTS, reservedSpace * 2 + 2);
sap.preClose(exc);
// Create hash
byte[] sapBytes = IOUtils.toByteArray(sap.getRangeStream());
MessageDigest digest = MessageDigest.getInstance(DigestAlgorithms.SHA256);
byte[] hash = digest.digest(sapBytes);
// Create PKCS7 structure
ExternalDigest externalDigest = hashAlgorithm -> DigestAlgorithms.getMessageDigest(hashAlgorithm, null);
PdfPKCS7 sgn = new PdfPKCS7(null, chain, DigestAlgorithms.SHA256, null, externalDigest, false);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, MakeSignature.CryptoStandard.CMS);
sh = MessageDigest.getInstance(DigestAlgorithms.SHA256, "BC").digest(sh);
// Sign digest with private key (in smart card in real application)
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] signedHash = cipher.doFinal(sh);
// Prepare bytes for insertion into document
sgn.setExternalDigest(signedHash, null, "RSA");
byte[] encodedSign = sgn.getEncodedPKCS7(hash, null, null, null, MakeSignature.CryptoStandard.CMS);
byte[] paddedSig = new byte[reservedSpace];
System.arraycopy(encodedSign, 0, paddedSig, 0, encodedSign.length);
// Insert bytes into document
PdfDictionary dic2 = new PdfDictionary();
dic2.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true));
sap.close(dic2);
我使用此代码提供sample document签名。在没有tsa或ocsp的情况下进行签名并不重要。
我尝试了不同版本的itextpdf,但结果是一样的。另外我知道在IOUtils.toByteArray()中使用大文档可能会有问题,但暂时忽略它。