[英]Is it possible to change the appearance of the signature within the document after signing it?
在計算用於簽名的文檔的哈希值之前,我要使用以下代碼在文檔中添加TextField。 因為我關注此鏈接,所以在使用iTextSharp對pdf文件簽名后更改了簽名外觀這是一個在所有頁面上添加簽名並在第一頁上添加文本字段的代碼。 文本字段的目的是從證書中提取“ IssuedTo”並將其顯示在簽名外觀上。
在以更新模式進行電子簽名之前打開pdf:
XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Signatures");
string signature = nodeList[0].FirstChild.InnerText;
string src = Server.MapPath("~/ESignFiles/" + file_withoutExtn + "_temp.pdf");
string dest = Server.MapPath("~/ESignFiles/" + file_withoutExtn + "_multiple_signed.pdf");
///add text
AddText(src, dest);
///add text
using (PdfReader reader = new PdfReader(src))
{
using (FileStream os = new FileStream(dest, FileMode.Create))
{
byte[] encodedSignature = Convert.FromBase64String(signature);
IExternalSignatureContainer external = new MyExternalSignatureContainer(encodedSignature);
MakeSignature.SignDeferred(reader, "sign1", os, external);
}
}
將文本添加到臨時pdf的代碼
public void AddText(String src, String dest) {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileStream(dest, FileMode.Create), '\0', true);
ColumnText.ShowTextAligned(stamper.GetOverContent(1), Element.ALIGN_LEFT, new Phrase("client name"), 200, 380, 0);
stamper.Close();
}
應用簽名后需要更新簽名外觀,這表明簽名解決方案的體系結構很差。
在當前情況下,這種不良體系結構似乎是由於要求而導致的(“外觀必須包含證書信息”與“證書在簽名前不可用”結合使用)。 但是,這是一個糟糕的體系結構,應在審查和修訂要求之后進行改進。
但是確實可以在良好的環境下更新簽名外觀:如果現有簽名允許“表單填寫和注解更改”並且沒有完全鎖定相應的簽名字段,則可以在不進行任何增量更新的情況下更新簽名外觀使簽名無效(不過,驗證者可能會警告更改)。
PDF規范沒有具體定義簽名字段外觀的結構,通用解決方案僅需用新的替換每個簽名字段窗口小部件注釋的外觀流。 可以使用.Net的iText 5.5.x來做到這一點:
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);
}
}
如您所見,該代碼從簽署者證書中提取主題的通用名稱,並將其(以"Signed by:"
行作為前綴)寫入新外觀。 如果替換外觀中需要其他數據,只需更改添加到columnText
和/或appearance
數據即可。
此外,該代碼用尺寸為100×100的新外觀替換了所有外觀。 您當然也可以使它適應您的要求。
從本質上講,這是從此答案到C#的代碼端口。
Adobe Acrobat Reader使用特定的方案來構建其簽名外觀,甚至為根據該方案的較舊版本構建的簽名添加某些功能。 如上所述,PDF規范沒有規定任何這樣的方案。 實際上,它甚至禁止這種功能,請參見。 這個答案 。
但是,很多堆棧溢出問題(尤其是來自印度的堆棧溢出問題)似乎都表明,客戶通常經常需要遵循該過時方案的簽名。
如果遵循該方案,則外觀本身被構造為形式XObjects的層次結構,特別是一組所謂的“層” n0到n4 ,其中n2是希望簽名者在其上應用其標識的層。
上面的通用解決方案可以進行如下調整以符合此方案:
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);
}
}
}
利用以下輔助方法:
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;
}
(請注意:此代碼假定簽名遵循Adobe方案;如果不確定輸入內容是否正確,請添加一些健全性檢查並將其默認設置為上述通用解決方案。)
由於外觀在計算要簽名的哈希時是文檔的一部分,因此更改外觀將更改哈希並使已完成的簽名無效。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.