簡體   English   中英

Pdf 簽名使 Acrobat Reader 中的現有簽名無效

[英]Pdf signature invalidates existing signature in Acrobat Reader

我正在使用 iText 7.1.15 和 SignDeferred 將簽名應用於 pdf 文檔。 SignDeferred 是必需的,因為簽名是創建 PKCS11 硬件令牌(usb 密鑰)。

當我簽署“常規”pdf(例如通過 word 創建)時,我可以應用多個簽名,並且所有簽名在 adobe acrobat 閱讀器中均顯示為有效。

如果 pdf 是通過將多個 pdf 文檔與 adobe DC 組合創建的,則第一個簽名有效,但應用秒簽名后立即失效。

應用第一個簽名后在 Adobe Reader 中的文檔: 在此處輸入圖像描述

應用第二個簽名后在 Adobe Reader 中的文檔: 在此處輸入圖像描述

同一文檔的簽名在福昕閱讀器中顯示為有效。

我在 stackoverflow 上發現了一個類似的問題( 多個簽名使 iTextSharp pdf 簽名中的第一個簽名無效),但它使用的是 iText 5,我不確定這是同一個問題。

問題:如何才能使兩個簽名在 Acrobat Reader 中保持有效?

第一個簽名失效的未簽名Pdf文檔: https://github.com/suntsu42/iTextDemoInvalidSecondSignature/blob/master/test.Z437175BA41913710EE09ZE1D

兩次簽名的無效文檔: https://github.com/suntsu42/iTextDemoInvalidSecondSignature/blob/master/InvalidDocumentSignedTwice.pdf

用於簽名的代碼

  //Step #1 >> prepare pdf for signing (Allocate space for the signature and calculate hash)
            using (MemoryStream input = new MemoryStream(pdfToSign))
            {
                using (var reader = new PdfReader(input))
                {
                    StampingProperties sp = new StampingProperties();
                    sp.UseAppendMode();

                    using (MemoryStream baos = new MemoryStream())
                    {
                        var signer = new PdfSigner(reader, baos, sp);
             

                        //Has to be NOT_CERTIFIED since otherwiese a pdf cannot be signed multiple times
                        signer.SetCertificationLevel(PdfSigner.NOT_CERTIFIED);

                        if (visualRepresentation != null)
                        {
                            try
                            {
                                PdfSignatureAppearance appearance = signer.GetSignatureAppearance();
                                base.SetPdfSignatureAppearance(appearance, visualRepresentation);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Unable to set provided signature image", ex);
                            }
                        }

                        //Make the SignatureAttributeName unique
                        SignatureAttributeName = $"SignatureAttributeName_{DateTime.Now:yyyyMMddTHHmmss}";
                        signer.SetFieldName(SignatureAttributeName);
                        DigestCalcBlankSigner external = new DigestCalcBlankSigner(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);

                        signer.SignExternalContainer(external, EstimateSize);
                        hash = external.GetDocBytesHash();
                        tmpPdf = baos.ToArray();
                    }
                }

                //Step #2 >> Create the signature based on the document hash
                // This is the part which accesses the HSM via PCKS11
                byte[] signature = null;
                if (LocalSigningCertificate == null)
                {
                    signature = CreatePKCS7SignatureViaPKCS11(hash, pin);
                }
                else
                {
                    signature = CreatePKCS7SignatureViaX509Certificate(hash);
                }

                //Step #3 >> Apply the signature to the document
                ReadySignatureSigner extSigContainer = new ReadySignatureSigner(signature);
                using (MemoryStream preparedPdfStream = new MemoryStream(tmpPdf))
                {
                    using (var pdfReader = new PdfReader(preparedPdfStream))
                    {
                        using (PdfDocument docToSign = new PdfDocument(pdfReader))
                        {
                            using (MemoryStream outStream = new MemoryStream())
                            {
                                PdfSigner.SignDeferred(docToSign, SignatureAttributeName, outStream, extSigContainer);
                                return outStream.ToArray();
                            }
                        }
                    }
                }

            }

示例項目

我創建了一個使用本地證書進行簽名的工作示例項目。 我也確實將 iText 更新到版本 7.2,但結果相同。 它還包含無法兩次簽名的文檔(test.pdf) https://github.com/suntsu42/iTextDemoInvalidSecondSignature/tree/master

正如評論中已經提到的,示例文檔“InvalidDocumentSignedTwice.pdf”的簽名未在增量更新中應用,因此很明顯以前的簽名會損壞。 但這不是 OP 示例項目的問題。 因此,問題的處理着眼於示例項目的實際輸出。

分析問題

在驗證簽名的 PDF 時,Adobe Acrobat 執行兩種類型的檢查:

  • 它檢查簽名本身,以及它所涵蓋的 PDF 的修訂版是否未被觸及。
  • (如果在簽名涵蓋的修訂之后對 PDF 進行了添加:)它檢查在增量更新中應用的更改是否僅包含允許的更改。

前一個檢查非常穩定和標准,但第二個檢查非常異想天開,容易出現錯誤的否定驗證結果。 就像你的情況...

在您的示例文檔的情況下,您可以簡單地確定第一次檢查必須肯定地驗證第一個簽名:只有一個(有效)簽名的文件構成了具有兩個簽名的文件的字節起始部分。 所以這里沒有任何東西被破壞。

因此,第二種檢查類型,變化無常的類型,在手頭的情況下一定是錯誤的。

要找出哪些更改必須分析簽名期間所做的更改。 一個有用的事實是,使用 iText 5 執行相同操作不會產生問題; 因此,觸發檢查的更改必須是 iText 7 與 iText 5 所做的不同之處。 在此上下文中的主要區別在於 iText 7 具有比 iText 5 更全面的標記支持,因此,還向文檔結構樹添加了對新簽名字段的引用。

這本身還沒有觸發異想天開的檢查,但是,它只是在這里這樣做,因為一個大綱元素將更改的父結構樹元素引用為其結構元素SE )。 顯然,Adobe Acrobat 將關聯結構元素的更改視為大綱鏈接的更改,因此,將其視為由第一個簽名簽名的文檔修訂行為的(不允許的)更改。

那么這是 iText 錯誤(將條目添加到結構樹)還是 Adobe Acrobat 錯誤(抱怨添加)? 好吧,在標記的 PDF (並且您的 PDF 的相應Marked條目設置為true )中,包括注釋和表單字段的內容預計將被標記。 因此,為新添加的簽名字段及其外觀添加結構樹條目不僅應該被允許,而且實際上被推薦甚至是必需的。 所以這似乎是 Adobe Acrobat 的錯誤。

解決方法

知道這似乎是一個 Adobe Acrobat 錯誤,這一切都很好,但歸根結底,現在可能需要一種方法來多次簽署此類文檔,而當前的 Adobe Acrobat 不會將其稱為無效。

可以讓 iText 相信沒有結構樹,也不需要更新結構樹。 這可以通過使文檔標記結構的初始化失敗來完成。 為此,我們重寫了PdfDocument方法TryInitTagStructure 由於 iText PdfSigner在內部創建其文檔 object,因此我們在PdfSigner方法InitDocument的覆蓋中執行此操作。

即,我們使用 class MySigner而不是PdfSigner ,定義如下:

public class MySigner : PdfSigner
{
    public MySigner(PdfReader reader, Stream outputStream, StampingProperties properties) : base(reader, outputStream, properties)
    {
    }

    override protected PdfDocument InitDocument(PdfReader reader, PdfWriter writer, StampingProperties properties)
    {
        return new MyDocument(reader, writer, properties);
    }
}

public class MyDocument : PdfDocument
{
    public MyDocument(PdfReader reader, PdfWriter writer, StampingProperties properties) : base(reader, writer, properties)
    {

    }

    override protected void TryInitTagStructure(PdfDictionary str)
    {
        structTreeRoot = null;
        structParentIndex = -1;
    }
}

使用MySigner簽署文檔 iText 將不再添加標記,因此不會讓 Adobe Acrobat 抱怨結構樹中的新條目。

Java 中的相同解決方法

由於我在 Java 中工作感覺更舒服,我對此進行了分析並測試了 Java 中的變通方法。

這里可以把它變成一個更封閉的形式(也許C#也可以,我不知道),而不是像這樣初始化簽名者

PdfSigner pdfSigner = new PdfSigner(pdfReader, os, new StampingProperties().useAppendMode());

我們這樣做:

PdfSigner pdfSigner = new PdfSigner(pdfReader, os, new StampingProperties().useAppendMode()) {
    @Override
    protected PdfDocument initDocument(PdfReader reader, PdfWriter writer, StampingProperties properties) {
        return new PdfDocument(reader, writer, properties) {
            @Override
            protected void tryInitTagStructure(PdfDictionary str) {
                structTreeRoot = null;
                structParentIndex = -1;
            }
        };
    }
};

MultipleSignaturesAndTagging測試testSignTestManuelTwiceNoTag

TL;博士

iText 7 在簽名期間將新簽名字段的結構元素添加到文檔結構樹中。 但是,如果此新節點的父節點被引用為大綱元素的關聯結構元素,Adobe Acrobat 會錯誤地認為這是不允許的更改。 作為一種解決方法,可以調整 iText 簽名以不添加結構元素。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM