I need to sign pdf document using external digest. This digest is signed in smart card, then signed digest is inserted into the document. Signed document contains rectangle with text but signature is invalid.
Adobe pdf reader says its "BER encoding problem" and doesn't even show certificate info, pdf studio viewer shows certificate info but says that "Document HAS been modified".
// 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);
I provide sample document signed using this code. It doesn't matter that it's signed without tsa or ocsp.
I tried different versions of itextpdf, but result was the same. Also I know there could be problem using big documents with IOUtils.toByteArray(), but ignore that for now.