1

この前の質問で与えられた回答に従ってください: In Itext 7, how to get range stream to sign a pdf? 、Itext 5 で機能する 2 段階の署名方法を再実装しようとしましたが、最初の段階の文書結果を (PdfReader または pdf リーダーで) 再オープンしようとすると問題が発生します。(無効な文書)

これは、certification という名前の空の署名フィールドがすでに含まれているドキュメントの署名部分です ... このステップの結果が無効なのはなぜですか?

PdfReader reader = new PdfReader(fis);
Path signfile = Files.createTempFile("sign", ".pdf");
FileOutputStream os = new FileOutputStream(signfile.toFile());
PdfSigner signer = new PdfSigner(reader, os, false);
signer.setFieldName("certification"); // this field already exists
signer.setCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING);
PdfSignatureAppearance sap = signer.getSignatureAppearance();
sap.setReason("Certification of the document");
sap.setLocation("On server");
sap.setCertificate(maincertificate);
BouncyCastleDigest digest = new BouncyCastleDigest();
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, null, digest,false);
 //IExternalSignatureContainer like BlankContainer
PreSignatureContainer external = new    PreSignatureContainer(PdfName.Adobe_PPKLite,PdfName.Adbe_pkcs7_detached);
signer.signExternalContainer(external, 8192);
byte[] hash=external.getHash();
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null,PdfSigner.CryptoStandard.CMS);// sh will be sent for signature

PreSignatureContainer クラスは次のとおりです。

public class PreSignatureContainer implements IExternalSignatureContainer {

private PdfDictionary sigDic;
private byte hash[];


public PreSignatureContainer(PdfName filter, PdfName subFilter) {

    sigDic = new PdfDictionary();
    sigDic.put(PdfName.Filter, filter);
    sigDic.put(PdfName.SubFilter, subFilter);
}

@Override
public byte[] sign(InputStream data) throws GeneralSecurityException {
    String hashAlgorithm = "SHA256";
    BouncyCastleDigest digest = new BouncyCastleDigest();

    try {
    this.hash= DigestAlgorithms.digest(data, digest.getMessageDigest(hashAlgorithm));
    } catch (IOException e) {
        throw new GeneralSecurityException("PreSignatureContainer signing exception",e);
    }

    return new byte[0];
}

@Override
public void modifySigningDictionary(PdfDictionary signDic) {
    signDic.putAll(sigDic);

}

public byte[] getHash() {
    return hash;
}

public void setHash(byte hash[]) {
    this.hash = hash;
}

}

4

1 に答える 1

1

このステップの結果が無効なのはなぜですか

基本的にバグを発見したため... ;)

サンプル入力ファイルには、バグを引き起こす機能が 1 つあります。それは、オブジェクト ストリームを使用して圧縮されていることです。

iText がそのようなファイルを操作するとき、できるだけ多くのオブジェクトをオブジェクト ストリームに入れようとします。残念ながら、署名辞書でもそうです。これは残念なことです。ファイル全体を書き込んだ後、このディクショナリに (以前は利用できなかった) いくつかの情報を入力しようとするため、圧縮されたオブジェクト ストリームが破損します。


何ができる...

次のいずれかを実行できます

  • iText の開発がこの問題を修正するのを待ちます。これにはあまり時間がかからないと思いますが、おそらく待つ時間がないでしょう。また
  • オブジェクト ストリームを使用しない形式に署名するようにファイルを変換します。これは iText 自体を使用して行うことができますが、これが意味するファイルの増加を受け入れることができないか、おそらくファイルが既に署名されているため、そのような変換が禁止されています。また
  • iText 7 にパッチを適用して、シグネチャ ディクショナリがオブジェクト ストリームに追加されないように強制します。これは些細なパッチですが、おそらくパッチを適用したライブラリを使用したくないでしょう。

上記のパッチは実に些細なもので、メソッドには次のPdfSigner.preClose(Map<PdfName, Integer>)コードが含まれています。

if (certificationLevel > 0) {
    // add DocMDP entry to root
    PdfDictionary docmdp = new PdfDictionary();
    docmdp.put(PdfName.DocMDP, cryptoDictionary.getPdfObject());
    document.getCatalog().put(PdfName.Perms, docmdp); // TODO: setModified?
}

document.close();

上記cryptoDictionary.getPdfObject())の署名辞書です。document.close()以前に出力に書き込まれていない限り、オブジェクト ストリームに追加されます。したがって、その呼び出しの直前にそのオブジェクトをフラッシュするための呼び出しを追加し、closeパラメーターによってオブジェクト ストリームに追加されないことを明確にする必要があります。

cryptoDictionary.getPdfObject().flush(false);

そのパッチを適用すると、コードが返す PDF は上記のように破損しなくなります。


余談ですが、iText 5 には、上のブロックに対応するブロックPdfSignatureAppearance.preClose(HashMap<PdfName, Integer>)のすぐ上の対応する行に同様の行が含まれています。iText 7 へのリファクタリング中に失われたようです。ifif

于 2016-08-26T10:18:31.477 に答える