在Itext 7中,如何使用2个步骤签署pdf? [英] In Itext 7, how to sign a pdf with 2 steps?

查看:401
本文介绍了在Itext 7中,如何使用2个步骤签署pdf?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

按照上一个问题给出的答案:在Itext 7中,如何让范围流签署pdf?,我试图重新实现在Itext 5中工作的两步签名方法,但是在尝试重新打开时我遇到了一个问题第一步的文档结果(使用PdfReader或pdf阅读器)。(无效文档)

Following the answers given in this previous question : In Itext 7, how to get the range stream to sign a pdf?, i've tried to reimplement the two steps signing method working in Itext 5 but i encounter an issue when trying to reopen the document result of the first step (with the PdfReader or a pdf reader).(invalid document)

这是已经包含空签名字段的文档的预设部分命名认证...为什么这一步的结果无效?

Here is the presigning part for a document already containing an empty signature field named certification ... why is the result of this step invalid ?

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类:

And here is the PreSignatureContainer class :

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;
}

}

推荐答案


为什么这一步的结果无效

why is the result of this step invalid

因为你基本上发现了一个错误...;)

Because you essentially discovered a bug... ;)

您的示例输入文件有一个触发错误的功能:使用对象流进行压缩。

Your sample input file has one feature which triggers the bug: It is compressed using object streams.

当iText操作这样的文件时,它还会尝试将尽可能多的对象放入对象流中。不幸的是,它也使用签名字典。这很不幸,因为在写完整个文件后,它会尝试在这个字典中输入一些信息(之前没有这些信息),这会损坏压缩的对象流。

When iText manipulates such a file, it also tries to put as many objects as possible into object streams. Unfortunately it also does so with the signature dictionary. This is unfortunate because after writing the whole file it tries to enter some information (which are not available before) into this dictionary which damages the compressed object stream.

你可以


  • 等待iText开发来解决这个问题 - 我认为这不会花太长时间,但可能你没有时间等待;或者

  • 转换文件以签署一个不使用对象流的表单 - 这可以使用iText本身完成,但可能你不能接受文件增长这意味着,或者可能已经文件已经签字禁止任何此类转变;或

  • 修补iText 7以强制不将签名词典添加到对象流中 - 这是一个简单的补丁,但您可能不想使用修补的库。

上面提到的补丁确实很简单,方法 PdfSigner.preClose(Map< PdfName,Integer>)包含以下代码:

The patch mentioned above indeed is trivial, the method PdfSigner.preClose(Map<PdfName, Integer>) contains this code:

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()期间,它被添加到对象流中,除非之前已将其写入输出。因此,您只需在关闭调用之前添加一个刷新该对象的调用,并通过参数明确表示不应将其添加到对象流中:

The cryptoDictionary.getPdfObject()) is the signature dictionary I mentioned above. During document.close() it is added to an object stream unless it has been written to the output before. Thus, you simply have to add a call to flush that object right before that close call and by parameter make clear that it shall not be added to an object stream:

cryptoDictionary.getPdfObject().flush(false);

使用该补丁后,您的代码返回的PDF不会再被破坏。

With that patch in place, the PDFs your code returns are not damaged as above anymore.

另外,iText 5确实在相应的 PdfSignatureAppearance.preClose(HashMap< PdfName)中包含类似的行,整数>)正好在 if 块之上,对应于上面的 if 块。在重构到iText 7期间似乎丢失了。

As an aside, iText 5 does contain a similar line in the corresponding PdfSignatureAppearance.preClose(HashMap<PdfName, Integer>) right above the if block corresponding to the if block above. It seems to have been lost during refactoring to iText 7.

这篇关于在Itext 7中,如何使用2个步骤签署pdf?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆