First of all, as has been discussed in comments to the question and to Bharat's answer:
The need to update signature appearances after applying the signature indicates a bad architecture of the signing solution.
In the case at hand this bad architecture appears to be a result of the requirements ("appearances must include certificate information" in combination with "certificate is not available before signing"). Nonetheless this is a bad architecture and should be improved after reviewing and revising the requirements.
But it indeed is possible under benign circumstances to update signature appearances: If the existing signatures allow for "form fill-ins and annotation changes" and do not completely lock the respective signature fields, the appearances of signatures can be updated in an incremental update without invalidating the signatures (validators may warn about a change, though).
Updating generic PDF signatures
The PDF specification does not specifically define a structure for the appearance of a signature field, a generic solution simply has to replace the appearance stream of each signature field widget annotation with a new one. This can be done like this using iText 5.5.x for .Net:
using (PdfReader pdfReader = new PdfReader(SRC))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(DEST, FileMode.Create, FileAccess.Write), '\0', true))
{
AcroFields acroFields = pdfStamper.AcroFields;
foreach (String signatureName in acroFields.GetSignatureNames())
{
PdfPKCS7 pkcs7 = acroFields.VerifySignature(signatureName);
X509Certificate signerCert = pkcs7.SigningCertificate;
String signerName = CertificateInfo.GetSubjectFields(signerCert).GetField("CN");
PdfAppearance appearance = PdfAppearance.CreateAppearance(pdfStamper.Writer, 100, 100);
ColumnText columnText = new ColumnText(appearance);
Chunk chunk = new Chunk();
chunk.SetSkew(0, 12);
chunk.Append("Signed by:");
columnText.AddElement(new Paragraph(chunk));
chunk = new Chunk();
chunk.SetTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, 1, BaseColor.BLACK);
chunk.Append(signerName);
columnText.AddElement(new Paragraph(chunk));
columnText.SetSimpleColumn(0, 0, 100, 100);
columnText.Go();
PdfDictionary appDict = new PdfDictionary();
appDict.Put(PdfName.N, appearance.IndirectReference);
AcroFields.Item field = acroFields.GetFieldItem(signatureName);
for (int i = 0; i < field.Size; i++)
{
PdfDictionary widget = field.GetWidget(i);
PdfArray rect = widget.GetAsArray(PdfName.RECT);
float x = Math.Min(rect.GetAsNumber(0).FloatValue, rect.GetAsNumber(0).FloatValue);
float y = Math.Min(rect.GetAsNumber(1).FloatValue, rect.GetAsNumber(3).FloatValue);
widget.Put(PdfName.RECT, new PdfArray(new float[] { x, y, x + 100, y + 100 }));
}
field.WriteToAll(PdfName.AP, appDict, AcroFields.Item.WRITE_WIDGET);
field.MarkUsed(acroFields, AcroFields.Item.WRITE_WIDGET);
}
}
As you can see the code extracts the subject's common name from the signer certificate and writes it (prefixed by a "Signed by:"
line) to the new appearance. If you need other data in your your replacement appearance, simply change the data added to the columnText
and/or the appearance
accordingly.
Furthermore, the code replace all appearances with new ones which are 100×100 in size. You of course can adapt this to your requirements, too.
This essentially is a port of code from this answer to C#.
Updating PDF signatures using the Adobe specific layers
Adobe Acrobat Reader uses a specific scheme to build its signature appearances and even adds a certain functionality to signatures built according to an older version of this scheme. As mentioned above, the PDF specification does not prescribe any such scheme; actually it even forbids such a functionality, cf. this answer.
Nonetheless many stack overflow questions in particular from India seem to indicate that signatures following that obsolete scheme are often required there by clients.
If one follows this scheme, the appearance itself is constructed as a hierarchy of form XObjects, in particular a set of so called "layers" n0 through n4 from which n2 is the layer onto which a signer is expected to apply its identity.
The generic solution above can be adapted as follows to comply with this scheme:
using (PdfReader pdfReader = new PdfReader(SRC))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(DEST, FileMode.Create, FileAccess.Write), '\0', true))
{
AcroFields acroFields = pdfStamper.AcroFields;
foreach (String signatureName in acroFields.GetSignatureNames())
{
PdfPKCS7 pkcs7 = acroFields.VerifySignature(signatureName);
X509Certificate signerCert = pkcs7.SigningCertificate;
String signerName = CertificateInfo.GetSubjectFields(signerCert).GetField("CN");
AcroFields.Item field = acroFields.GetFieldItem(signatureName);
for (int i = 0; i < field.Size; i++)
{
PdfDictionary widget = field.GetWidget(i);
Rectangle rect = PdfReader.GetNormalizedRectangle(widget.GetAsArray(PdfName.RECT));
PdfAppearance appearance = PdfAppearance.CreateAppearance(pdfStamper.Writer, rect.Width, rect.Height);
ColumnText columnText = new ColumnText(appearance);
Chunk chunk = new Chunk();
chunk.SetSkew(0, 12);
chunk.Append("Signed by:");
columnText.AddElement(new Paragraph(chunk));
chunk = new Chunk();
chunk.SetTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, 1, BaseColor.BLACK);
chunk.Append(signerName);
columnText.AddElement(new Paragraph(chunk));
columnText.SetSimpleColumn(0, 0, rect.Width, rect.Height - 15);
columnText.Go();
PdfDictionary xObjects = GetAsDictAndMarkUsed((PdfStamperImp)pdfStamper.Writer, widget, PdfName.AP, PdfName.N, PdfName.RESOURCES, PdfName.XOBJECT, PdfName.FRM, PdfName.RESOURCES, PdfName.XOBJECT);
xObjects.Put(PdfName.N2, appearance.IndirectReference);
}
}
}
making use of the following helper method:
PdfDictionary GetAsDictAndMarkUsed(PdfStamperImp writer, PdfDictionary dictionary, params PdfName[] names)
{
PRIndirectReference reference = null;
foreach (PdfName name in names)
{
if (dictionary != null)
{
dictionary = dictionary.GetDirectObject(name) as PdfDictionary;
if (dictionary != null)
{
if (dictionary.IndRef != null)
reference = dictionary.IndRef;
}
}
}
if (reference != null)
writer.MarkUsed(reference);
return dictionary;
}
(Beware: This code assumes the signatures to follow the Adobe scheme; if you are not sure that your input does, add some sanity checks and default to the generic solution above.)
Is it possible to change the appearance of the signature within the document after signing it?
then you have posted your own duplicate – TheGeneralos
both for aPdfStamper
and forMakeSignature.SignDeferred
. This produces a hodgepodge of two pdfs in the stream. Even if a pdf reader can repair it, any signature in it will be broken due to the repair changes. – mkl