I am using itext7 version 7.1.5 in my application. My scenario is as following: - Take the hash of the document - Sign the hash from external signing server and get Pkcs7 - Embe
To sign a PDF with iText 7 using a remote server to create the actual cryptographic signature, you can simply implement an iText signature interface which executes the call of the remote service in its Sign
method for the given data.
Furthermore, you clarified in a comment that your
external signing server returns PKCS7 signature signed data.
Thus, the interface to use is an IExternalSignatureContainer
(and not a IExternalSignature
which one would have used if the server returned merely the primitive signature bytes).
I don't know how you communicate with your server; in particular I don't know whether the API you use requires you to provide the complete signed data or merely the hash thereof; and in the former case, whether to provide them as stream or array. Thus, I put hints into the Sign
method for each case:
public class ExternalServiceSignatureContainer : IExternalSignatureContainer
{
public void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
signDic.Put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached);
}
public byte[] Sign(Stream data)
{
// Call your external signing service to create a CMS signature container
// for the data in the InputStream
// Depending on your API to access that service you may either be able to
// directly call it with the stream
// return YOUR_SIGNING_API_CALL_FOR_STREAM(data);
// (or a byte[] generated from the stream contents)
// return YOUR_SIGNING_API_CALL_FOR_ARRAY(StreamUtil.InputStreamToArray(data));
// as parameter, or you may first have to hash the data yourself
// (e.g. as follows) and send your hash to the service.
// byte[] hash = DigestAlgorithms.Digest(data, DigestAlgorithms.SHA256);
// return YOUR_SIGNING_API_CALL_FOR_HASH(hash)
// dummy
return new byte[0];
}
}
You can then use this class as follows:
PdfReader pdfReader = new PdfReader(DOCUMENT_TO_SIGN);
PdfSigner pdfSigner = new PdfSigner(pdfReader, RESULT_STREAM, new StampingProperties().UseAppendMode());
pdfSigner.SetFieldName("Signature1");
ImageData imageData = ImageDataFactory.Create(SIGNATURE_IMAGE);
PdfSignatureAppearance sigAppearance = pdfSigner.GetSignatureAppearance();
sigAppearance.SetContact("ContactInfo");
sigAppearance.SetLocation("Location");
sigAppearance.SetPageNumber(1);
sigAppearance.SetPageRect(new Rectangle(100, 500, imageData.GetWidth() / 2, imageData.GetHeight() / 2));
sigAppearance.SetReason("SigningReason");
sigAppearance.SetSignatureGraphic(imageData);
sigAppearance.SetRenderingMode(RenderingMode.GRAPHIC);
sigAppearance.SetSignatureCreator("Malik");
pdfSigner.GetDocument().GetCatalog().SetModified();
int estimatedSize = 12000;
pdfSigner.SignExternalContainer(new ExternalServiceSignatureContainer(), estimatedSize);
Some remarks:
I added
sigAppearance.SetPageRect(new Rectangle(100, 500, imageData.GetWidth() / 2, imageData.GetHeight() / 2));
because I also used a different document to test. As that other document does not originally contain a signature field named Signature1, I needed to set the rectangle to get a visible signature. In case of your document which already has such a signature field, that line can be dropped but is ignored anyways.
The line
pdfSigner.GetDocument().GetCatalog().SetModified();
needs to be added because of a small bug in iText: When signing an existing field (not a new field), the AcroForm dictionary (in particular if contained directly in the Catalog like in your document), is not marked as modified and, therefore, not updated in the incremental update. This prevents the SigFlags in your document from being set which causes Adobe Reader not display the top signature bar.
You might want to change the estimate
int estimatedSize = 12000;
of the signature container size, in particular if the signature containers you retrieve always are much smaller or sometimes larger than 12000 bytes.