Можно ли изменить внешний вид подписи в документе после его подписания? - PullRequest
0 голосов
/ 03 мая 2019

Прежде чем вычислять хэш документа для подписи, я добавляю TextField в свой документ, используя приведенный ниже код.поскольку я перехожу по этой ссылке Изменение внешнего вида подписи после подписания pdf-файла с помощью iTextSharp Вот код, который добавляет подпись на все страницы и добавляет текстовое поле на первой странице.Цель текстового поля состоит в том, чтобы извлечь «IssuedTo» из сертификата и отобразить его в виде подписи.

Перед записью в PDF откройте 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();
            }

Ответы [ 2 ]

2 голосов
/ 07 мая 2019

Прежде всего, как обсуждалось в комментариях к вопросу и к ответу Бхарата :

Необходимость обновления внешнего вида подписи после применения подписи указывает на плохую архитектуру решения для подписи.

В данном случае эта плохая архитектура, кажется, является результатом требований («внешний вид должен включать информацию о сертификате» в сочетании с «сертификат недоступен до подписания»). Тем не менее, это плохая архитектура, и ее следует улучшить после рассмотрения и пересмотра требований.

Но при благоприятных обстоятельствах действительно возможно обновить внешний вид подписи: если существующие подписи допускают «заполнение форм и изменения аннотаций» и не полностью блокируют соответствующие поля подписи, появления подписи могут обновляться в инкрементном обновлении без аннулирования подписей (хотя валидаторы могут предупреждать об изменении).

Обновление общих подписей PDF

Спецификация PDF специально не определяет структуру для появления поля подписи, общее решение просто должно заменить поток появления каждой аннотации виджета поля подписи новой. Это можно сделать так, используя iText 5.5.x для .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. Вы, конечно, можете адаптировать это и к вашим требованиям.

По сути это порт кода от этого ответа до C #.

Обновление подписей PDF с использованием специальных слоев Adobe

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; если вы не уверены, что ваш ввод делает, добавьте некоторые проверки работоспособности и по умолчанию примените к общему решению, приведенному выше.)

0 голосов
/ 04 мая 2019

Поскольку внешний вид является частью документа при вычислении хэша для подписи, изменение внешнего вида изменит хэш и сделает недействительной уже выполненную подпись.

...