当前位置: 首页 > 知识库问答 >
问题:

在Itext 7中,如何用2个步骤签署pdf?

焦苏燕
2023-03-14

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

以下是已包含名为“认证”的空签名字段的文档的预签名部分...为什么此步骤的结果无效?

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

}

共有1个答案

董砚
2023-03-14

为什么这一步的结果无效

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

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

当iText处理这样的文件时,它还会尝试将尽可能多的对象放入对象流中。不幸的是,签名字典也是如此。这很不幸,因为在写入整个文件后,它试图将一些信息(以前不可用的)输入到这个字典中,这会损坏压缩对象流。

你可以选择

  • 等待iText开发来解决这个问题-我认为这不会花太长时间,但您可能没有时间等待;或
  • 将文件转换为不使用对象流的形式进行签名-这可以使用iText本身来完成,但您可能无法接受这意味着的文件增长,或者可能已经签名的文件禁止任何此类转换;或
  • 补丁iText7以强制签名字典不被添加到对象流中-这是一个微不足道的补丁,但您可能不想使用补丁库

上面提到的补丁确实是微不足道的,方法< code>PdfSigner.preClose(Map

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();

< code > crypto dictionary . getpdf object())就是我上面提到的签名字典。在< code>document.close()过程中,它被添加到对象流中,除非它以前已经被写入输出。因此,您只需在< code>close调用之前添加一个调用来刷新该对象,并通过参数表明它不应被添加到对象流中:

cryptoDictionary.getPdfObject().flush(false);

有了该补丁,您的代码返回的PDF就不会再像上面那样损坏了。

顺便说一句,iText 5确实在相应的签名中包含类似的行。

 类似资料:
  • 问题内容: 我正在尝试使用Rundeck构建,启动和链接一组Docker容器。简而言之(对于不熟悉docker的用户),启动映像时,它将返回容器ID。我想在启动后续作业时使用此容器ID。 从命令行运行时,它看起来像这样(仅示例!): (请注意在第二个命令行中使用第一个返回值) 此时,将有两个容器在运行。第二个将通过该选项链接到第一个,并且可使用第二个容器内部的主机名 host1 对其进行寻址。公平

  • 我对Spring批处理框架相当陌生。 我在一个作业中创建了两个步骤(我们称之为步骤1和步骤2)。我想把它们并行运行。不仅如此,step2的IteamReader还应该使用step1的itemwriter。 我的第一个问题是,在Spring批量中是否有可能做到这一点?如果是,怎么做? 其次,如果这不可能,还有什么工作可以做呢? 谢了。

  • null 如果生成错误(文件不一致,文件不存在...),则不能执行 仅供参考,我使用的是没有XML配置的Spring批处理!只有注释:下面是我的作业配置类的样子:

  • 我正在编写一个接收PKCS7数据(从签名的PDF文档中提取)的服务,并需要对其进行验证。 我使用iText7 PdfPKCS7进行验证,但签名验证总是失败。我可以从PKCS7读取所有其他信息(证书、时间戳等,我也用OpenSSL验证了这一点)。只有签名显示为无效。 下面是测试案例: 输出总是: 我想我在进口方面做错了什么,但就是找不到什么... 顺便说一句,验证其他pdf工具(Adobe DC、P

  • 我刚刚创建了DocuSign开发人员帐户。我必须使用签名对PDF进行签名,并用C#将其发送给收件人。我不希望收件人使用https://developers.docusign.com/esign-rest-api/code-examples/code-example-request-a-signature-via-email。我的系统中已经有PDF可用,我需要使用DocuSign API签名并将其发

  • 我需要多次调用webservice并传递使用来自多个表的数据创建的数据 UI->控制器->服务->(获取数据(表1,表2)并运行一些验证)1。如果验证失败-返回错误消息并停止。2.如果验证通过-调用JobLauncher并返回“任务启动”消息。 在异步作业中,我想到遵循2个步骤。 > bulkinsertstep > 我需要调用DB查询2个更多的表(Table3、table4)并创建一个大的数据集