Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 签名后是否可以更改文档中签名的外观?_C#_Pdf_Itext_Digital Signature_X509certificate - Fatal编程技术网

C# 签名后是否可以更改文档中签名的外观?

C# 签名后是否可以更改文档中签名的外观?,c#,pdf,itext,digital-signature,x509certificate,C#,Pdf,Itext,Digital Signature,X509certificate,在计算用于签名的文档哈希之前,我使用以下代码在文档中添加TextField。因为我正在关注这个链接 这是一个在所有页面上添加签名并在第一页上添加文本字段的代码。 文本字段的目的是从证书中提取“IssuedTo”,并将其显示在签名外观上 在更新模式下设计打开pdf之前: XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Signatures"); string signature = nodeList

在计算用于签名的文档哈希之前,我使用以下代码在文档中添加TextField。因为我正在关注这个链接 这是一个在所有页面上添加签名并在第一页上添加文本字段的代码。 文本字段的目的是从证书中提取“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签名 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);
    }
}
使用以下帮助器方法:

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

首先,正如在对问题的评论中所讨论的,并:

应用签名后需要更新签名外观,这表明签名解决方案的体系结构不好

在本案例中,这种糟糕的体系结构似乎是需求的结果(“外观必须包括证书信息”以及“签名前证书不可用”)。尽管如此,这是一种糟糕的体系结构,应在审查和修订需求后进行改进

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

更新通用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);
    }
}
使用以下帮助器方法:

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方案;如果您不确定您的输入是否遵循Adobe方案,请添加一些健全性检查,并默认为上述通用解决方案。)

如果您的问题是
是否可以在签名后更改文档中签名的外观?
那么您已经发布了自己的副本“我正在添加文本字段”-哪个文本字段?链接的问题不涉及任何文本字段,签名过程中也没有规范的文本字段。(或者你是否参考我对布鲁诺回答的评论?)审阅了您的评论。在签名之前添加字段,当我填写这些字段时,它显示签名无效。您在文档上标记签名的代码对
PdfStamper
MakeSignature.SignDeferred
都使用
os
。这会在流中产生两个pdf的大杂烩。即使pdf阅读器可以修复t、 由于修复更改,其中的任何签名都将被破坏。PDF的所有页面上的签名都是不必要的…所签名的是完整的PDF哈希,单个签名证明完整的PDF文档是不可否认的。如果您的问题是
,签名后是否可以更改文档中签名的外观?
您已经发布了自己的副本“我正在添加文本字段”-哪个文本字段?链接的问题不涉及任何文本字段,并且在签名过程中也没有规范的文本字段。(或者您是否参考我对Bruno答案的评论?)审阅了您的评论。在签名之前添加字段,当我填写这些字段时,它显示签名无效。您在文档上标记签名的代码对
PdfStamper
MakeSignature.SignDeferred
都使用
os
。这会在流中产生两个pdf的大杂烩。即使pdf阅读器可以修复t、 由于修复更改,其中的任何签名都将被破坏。PDF所有页面上的签名都是不必要的…签名的是完整的PDF哈希,单个签名证明完整PDF文档的不可否认性。“更改外观将更改哈希”-如果在增量更新中外观发生更改,则不会发生任何更改。@mkl您能详细说明什么是增量更新吗?@Bharat其要求是在签名表上打印“IsSueTo”(即客户名称)。@mkl