簡體   English   中英

iTextPdf:為什么在驗證簽名時誤導一些修訂?

[英]iTextPdf: why misleading number of revisions when verifiying signature?

我使用iTextPdf對Alfresco提供支持的PDF進行簽名和完整性檢查

這是簽名的代碼:

public void signItem(NodeRef itemToSign, String signer) {

       try{
        // retrieving user's public and private key
        Certificate chain[] = getCertificate(signer);
        PrivateKey pk = getPrivateKey(signer);

        String digestAlgorithm = DigestAlgorithms.SHA512;
        BouncyCastleProvider provider = new BouncyCastleProvider();
        Security.addProvider(provider);

        // Getting content of item to sign
        InputStream originalInputStream = getNodeRefInputStream(itemToSign);
        PdfReader pdfReader = new PdfReader(originalInputStream);

        // get an outputStream on the item to sign nodeRef and give to the
        // pdfStamper
        ByteArrayOutputStream outputStream = getNodeRefOutputStream(itemToSign);
        // logger.info("Before" + outputStream);

        PdfStamper pdfStamper = PdfStamper.createSignature(pdfReader, outputStream, '\0', new File("temp"), true);

        // Creating the appearance
        PdfSignatureAppearance appearance = pdfStamper.getSignatureAppearance();
        appearance.setReason("freeze");
        appearance.setLocation("koosserydesk");
        appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "signature space");

        // the sign document is subject to future approval signatures
        appearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_FORM_FILLING);

        // Creating the signature
        ExternalDigest digest = new BouncyCastleDigest();
        ExternalSignature signature = new PrivateKeySignature(pk, digestAlgorithm, provider.getName());
        // signing...
        MakeSignature.signDetached(appearance, digest, signature, chain, null, null, null, 0, CryptoStandard.CMS);


        // get the signed input stream
        InputStream signedInputStream = new ByteArrayInputStream(outputStream.toByteArray());

        // replace the itemToSign content with the signed content
        ContentWriter writer = getWriter(itemToSign);
        writer.putContent(signedInputStream);
} catch (Exception e) {

        // do something

    }

}

這是完整性檢查的代碼

public void checkDocIntegrity(NodeRef itemToSign) throws KoosseryDeskServerException {
    /** check the integrity of the document **/

    ArrayList<String> signatureNames;
    PdfPKCS7 pkcs7;
    boolean result = false;
    try {
        InputStream is = getNodeRefInputStream(itemToSign);
        PdfReader reader = new PdfReader(is);
        AcroFields fields = reader.getAcroFields();

        signatureNames = fields.getSignatureNames();
        String name = signatureNames.get(0);
        System.out.println("Siganture names = " + signatureNames);
        System.out.println("Document revision: " + fields.getRevision(name) + " of " + fields.getTotalRevisions());
        pkcs7 = fields.verifySignature(name);

        result = pkcs7.verify();
        System.out.println("Is the document integrity check OK? : "+result);
    } catch (Exception e) {

        // do something

    }

}

當我對使用上述signItem函數簽名的文檔進行完整性檢查時, 總是得到以下輸出:

Siganture names = [signature space] 
Document revision: 1 of 2
Is the document integrity check OK? : false

我猜完整性檢查始終是錯誤的,因為在粘貼了簽名之后添加了第二個修訂,但是:我不知道為什么我要得到兩個文檔修訂,但是我沒有添加任何注釋或其他批准簽名。

請告訴我我做錯了什么? 謝謝!

簡而言之

看起來您的方法getNodeRefOutputStream返回了一個ByteArrayOutputStream ,該ByteArrayOutputStream 已經包含要開始的原始文檔的副本,或者您的方法getWriter返回了一個ContentWriter ,它追加到現有內容上而不是替換它。

結果是最終結果文檔是(A)原始文檔和(B)原始文檔加簽名的串聯。

要解決此問題,請更改或替換錯誤的方法,以返回一個對象,該對象可以有效地壓模輸出替換原始內容。

詳細

通過分析您的PDF,它很快就變得很破損,因為它實際上不是由預期的兩個修訂(首先是原始PDF,然后是為簽名而創建的附加內容)組成,而是由三個部分(首先是原始PDF,然后是原始PDF)組成。 ,然后創建了一些附加內容,以便使用交叉引用對其進行簽名,就好像原始部分在前面一樣,但是一次。

效果是

  • 聚合的交叉引用是錯誤的,它們指向原始文檔的第二個副本中的位置不足,而不是實際添加的對象,並且startxref指針也指向原始PDF的第二個副本中的某個位置; 因此,在關閉文檔時,Adobe Reader會“修復” PDF,從而引發“您要保存更改”對話框嗎?
  • 簽名字節范圍內的間隙位於原始文檔的第二個副本中,特別是不包含編碼的簽名字節; 因此,Adobe Reader-期望在空白處簽名-表示該簽名中包含格式或信息的錯誤
  • 帶符號的字節范圍在某種程度上到達原始文件的第二個副本,從而使iText報告兩個修訂,首先是直到帶符號的字節范圍末尾的數據,然后是超出范圍的數據;
  • 簽名哈希值被破壞,因為簽名字節范圍不包含原始文檔和簽名附加內容,但實際簽名字節除外,而是原始文檔加上原始文檔第二個副本的一些怪異部分; 這會導致iText驗證失敗。

(您可能想在Information Security Stack Exchange上閱讀此答案以了解詳細信息。)

對於iText類,這種行為是聞所未聞的。 因此,它似乎是由您的代碼引起的。

查看您發布的代碼,該原始文檔的重復很可能是由於您的代碼

  • 標記到由getNodeRefOutputStream返回的ByteArrayOutputStream (如果該流是使用原始文檔的副本初始化的),或者
  • 將結果PDF寫入getWriter返回的ContentWriter (如果putContent方法實際上追加到現有內容之后)。

因此,我建議不要將outputStream設置為getNodeRefOutputStream返回的ByteArrayOutputStream ,以將outputStream設置為空的new ByteArrayOutputStream() 如果這樣做沒有幫助,我建議尋找getWriterContentWriter.putContent替代方法。

暫無
暫無

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

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