从 pdf 中检索 pkcs7 文件并共同签名

retrieve the pkcs7 file from pdf and co-sign

我有一个签名的 PDF 文件和一个 .p7s 文件。我需要从 PDF 中检索 .p7s 并共同签署 PDF(生成其他 .p7s)。 在我需要再次将 p7s 放入 PDF 文件之后。 当我尝试从 PDF 获取 p7s 时出现此错误: ExceptionConverter:java.security.SignatureException:对象未初始化以进行签名 我的代码:

BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);

PdfReader pdfReader = new 
PdfReader(Files.readAllBytes(inputFile.toPath()));

AcroFields acroFields = pdfReader.getAcroFields();          
List<String> signatures = acroFields.getSignatureNames();

for (String name : signatures) {
   PdfPKCS7 pdfPkcs7 = acroFields.verifySignature(name, "BC");
   Files.write(Paths.get("~/TEST/itext/"+ name +".p7s"), 
   pdfPkcs7.getEncodedPKCS7());//ERROR HERE!
}

理论上

您想通过从 PDF 中提取嵌入式 CMS 签名容器并向其添加您的签名来[=44​​=]共同签署 PDF(会签?平行签名?),并重新嵌入扩展容器。

这是错误的方法,PDF 签名被设计为仅包含每个 PDF 签名字段的单个签名,而我们讨论的 CMS 签名容器本质上就是这样一个字段的值,因此每个嵌入的签名容器都应准确包含一个签名。

严格来说 PDF 规范允许您使用任意自定义签名方案,只有“可互操作的签名”是有限的。因此,您的具有多个签名的签名容器可以被认为是有效的 PDF,只是不是“可互操作签名”,即您不应该期望任何其他软件像您一样解释签名。特别是 Adob​​e Acrobat Reader 将仅识别并显示其中一个签名。

(事实上,一些大型签名解决方案提供商也在 PDF 中嵌入了多重签名 CMS 容器。例如,目前我必须处理由嵌入任意 CAdES-A 签名的此类提供商生成的签名容器(特别是一些有多个签名者)转换为 PDF 并调用此签名格式 PDF/CAdES-A,现在我必须向他们的客户解释他们的签名是 PDF 签名,但不是可互操作的签名,即使那个名字 [=54] =] 听起来像是一些很棒的标准...)

为了实现互操作性,PDF 中的多个签名必须按顺序应用,如本草图所示:

有关一些背景和更多信息的链接,请参见。 this answer.

在实践中

您的代码似乎是 iText 5.5.x 代码,特别是还不是 iText 7.x 代码。在这种情况下,您可以按原样使用 Digital Signatures for PDF Documents white book by Bruno Lowagie 中的代码。

一个非常简单的例子:

public void sign(String src, String dest, Certificate[] chain, PrivateKey pk, String digestAlgorithm,
        String provider, CryptoStandard subfilter, String reason, String location)
        throws GeneralSecurityException, IOException, DocumentException {
    // Creating the reader and the stamper
    PdfReader reader = new PdfReader(src);
    FileOutputStream os = new FileOutputStream(dest);
    PdfStamper stamper = PdfStamper.createSignature(reader, os, '[=10=]');
    // Creating the appearance
    PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
    appearance.setReason(reason);
    appearance.setLocation(location);
    appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
    // Creating the signature
    ExternalDigest digest = new BouncyCastleDigest();
    ExternalSignature signature = new PrivateKeySignature(pk, digestAlgorithm, provider);
    MakeSignature.signDetached(appearance, digest, signature, chain, null, null, null, 0, subfilter);
}

(代码示例 2.1:使用 iText 签名的“Hello World”)

由于您的用例涉及签署已签署的文档,因此您只需将 PdfStamper.createSignature 行更改为

PdfStamper stamper = PdfStamper.createSignature(reader, os, '[=11=]', true);

(注意末尾的新布尔参数)。此更改导致签名以 附加模式 创建,这使以前的修订保留原始签名,不会使该签名无效。