签名后是否可以更改签名在文档中的外观? [英] Is it possible to change the appearance of the signature within the document after signing it?

查看:110
本文介绍了签名后是否可以更改签名在文档中的外观?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在计算文档的哈希值以进行签名之前,我正在使用以下代码在文档中添加TextField.当我关注此链接时 使用iTextSharp对pdf文件签名后更改签名外观 这是一个在所有页面上添加签名并在第一页上添加文本字段的代码. 文本字段的目的是从证书中提取"IssuedTo"并将其显示在签名外观上.

Before calculating the hash of the document for signing I am adding the TextField in my document using the below code. as I am following this link Changing signature appearance after signing pdf file with iTextSharp Here is a code that adds signature on all pages and adds a text field on the first page. the purpose of the text field is to extract the "IssuedTo" from the certificate and display it on the signature appearance.

在以更新方式分配pdf之前:

Before esign open pdf in update mode:

 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的代码

Code that add text to temp 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();
            }

推荐答案

首先,正如对该问题和

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).

PDF规范没有专门为签名字段的外观定义一种结构,一种通用解决方案仅需用新的替换每个签名字段窗口小部件注释的外观流.可以使用.Net的iText 5.5.x来做到这一点:

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);
    }
}

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

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.

此外,该代码用尺寸为100×100的新外观替换了所有外观.您当然也可以使它适应您的要求.

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.

这实质上是来自此答案对C#的代码端口.

This essentially is a port of code from this answer to C#.

Adob​​e Acrobat Reader使用特定的方案来构建其签名外观,甚至为根据该方案的较旧版本构建的签名添加某些功能.如上所述,PDF规范没有规定任何此类方案;实际上,它甚至禁止这样的功能,请参见. 此答案.

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.

如果遵循此方案,则外观本身将被构造为XObjects形式的层次结构,尤其是一组所谓的图层" n0 n4 n2 是希望签名者在其上应用其身份的层.

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);
        }
    }
}

使用以下辅助方法:

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方案;如果不确定输入是否正确,请添加一些健全性检查并将其默认设置为上面的通用解决方案.)

(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.)

这篇关于签名后是否可以更改签名在文档中的外观?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆