【问题标题】:Is it possible to change the appearance of the signature within the document after signing it?签名后是否可以更改文档中签名的外观?
【发布时间】:2019-05-03 06:07:54
【问题描述】:

在计算签名文档的哈希值之前,我将使用以下代码在我的文档中添加 TextField。因为我正在关注这个链接 Changing signature appearance after signing pdf file with iTextSharp 这是在所有页面上添加签名并在第一页上添加文本字段的代码。 文本字段的目的是从证书中提取“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();
            }

【问题讨论】:

  • 如果您的问题是Is it possible to change the appearance of the signature within the document after signing it?,那么您已经发布了自己的副本
  • “我正在添加 TextField” - 哪个文本字段?链接的问题不涉及任何文本字段,并且在签名过程中也没有规范的文本字段。 (或者你是指我对布鲁诺的回答的评论吗?)
  • 参考您的评论。在签名之前添加字段,当我填写这些字段时,它显示签名无效。
  • 您的在文档上盖章签名的代码os 用于PdfStamperMakeSignature.SignDeferred。这会在流中生成两个 pdf 的大杂烩。即使pdf阅读器可以修复它,其中的任何签名都会由于修复更改而损坏。
  • PDF 所有页面上的签名都是不必要的...签名的是完整的 pdf 哈希,单个签名证明了完整 pdf 文档的不可否认性。

标签: c# pdf itext digital-signature x509certificate


【解决方案1】:

首先,正如 cmets 中讨论的问题和Bharat's answer

在应用签名后需要更新签名外观表明签名解决方案的架构不好。

在目前的情况下,这种糟糕的架构似乎是要求的结果(“外观必须包含证书信息”与“证书在签名前不可用”相结合)。尽管如此,这是一个糟糕的架构,应该在审查和修改需求后加以改进。

但在良性情况下确实可以更新签名外观:如果现有签名允许“表单填写和注释更改”并且不完全锁定相应的签名字段,外观的签名可以在增量更新中更新,而不会使签名失效(但验证器可能会警告更改)。

更新通用 PDF 签名

PDF 规范没有具体定义签名字段的外观结构,通用解决方案只需将每个签名字段小部件注释的外观流替换为新的。这可以使用 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);
    }
}

如您所见,代码从签名者证书中提取主题的公用名并将其(以"Signed by:" 行作为前缀)写入新外观。如果您在替换外观中需要其他数据,只需相应地更改添加到columnText 和/或appearance 的数据即可。

此外,代码将所有外观替换为 100×100 大小的新外观。当然,您也可以根据自己的要求进行调整。

这实质上是从this answer 到 C# 的代码移植。

使用 Adob​​e 特定层更新 PDF 签名

Adobe Acrobat Reader 使用特定的方案来构建其签名外观,甚至为根据该方案的旧版本构建的签名添加了某些功能。如上所述,PDF 规范没有规定任何此类方案;实际上它甚至禁止这样的功能,cf。 this answer.

尽管如此,尤其是来自印度的许多堆栈溢出问题似乎表明,客户通常需要遵循该过时方案的签名。

如果遵循此方案,则外观本身被构造为表单 XObjects 的层次结构,特别是一组所谓的“层”n0n4,其中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;
}

(请注意:此代码假定签名遵循 Adob​​e 方案;如果您不确定您的输入是否符合,请添加一些健全性检查并默认使用上述通用解决方案。)

【讨论】:

    【解决方案2】:

    由于在计算要签名的哈希时外观是文档的一部分,因此更改外观将更改哈希并使已经完成的签名无效。

    【讨论】:

    • “改变外观会改变哈希” - 如果外观在增量更新中改变,则不会。
    • @mkl 你能详细说明什么是增量更新吗?
    • @Bharat 要求在签名外观部分打印“isssueTo”(即客户名称)以及签名日期。并且在调用签名者服务之前“IssuedTo”不可用。
    • 签名服务返回证书。包含“issueTo”。我如何在外观部分的 pdf 上打印它
    • @Pravin "什么是增量更新?"​​i> - 如果您更改 PDF 不是通过保存 PDF 对象的全新转储而是仅将更改附加到原始 PDF .在 iText 术语中也称为“附加模式”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-27
    • 1970-01-01
    相关资源
    最近更新 更多