PdfPKCS7 .verify()返回false

时间:2016-03-07 14:36:53

标签: pdf itext digital-signature pkcs#7

我一直在使用itextpdf-5.5.5,并且完整性检查返回false。

PdfPKCS7 pkcs7 = fields.verifySignature(name);

pkcs7.verify();<------ HERE return false

但Adobe Acrobat Reader说:已签名且所有签名均有效。 :(

这是文件:Test.pdf

有人可以解释发生了什么吗?

提前致谢

1 个答案:

答案 0 :(得分:3)

PDF中嵌入的CMS签名容器具有一些可疑属性。特别是它具有封装内容,即使它只是一个长度为0的字节数组。这使得iText在测试中包含该值然后失败。我建议创建更清洁的签名容器。

详细

CMS对象类型EncapsulatedContentInfo指定为:

  

5.2。 EncapsulatedContentInfo类型

     

内容以EncapsulatedContentInfo:

类型表示
 EncapsulatedContentInfo ::= SEQUENCE {
    eContentType ContentType,
    eContent [0] EXPLICIT OCTET STRING OPTIONAL }

 ContentType ::= OBJECT IDENTIFIER
     

EncapsulatedContentInfo类型的字段具有以下内容      含义:

     
    

eContentType是一个对象标识符。对象标识符           唯一指定内容类型。

         

eContent是内容本身,以八位字符串形式携带。该           电子内容无需进行DER编码。

  
     

可选的省略电子内容      EncapsulatedContentInfo字段使得构造成为可能      &#34;外部签名&#34;。在外部签名的情况下,      EncapsulatedContentInfo值中不存在正在签名的内容      包含在签名数据内容类型中。如果是eContent值      在EncapsulatedContentInfo中没有,那么signatureValue就是      计算并分配eContentType,就像电子内容一样      价值存在。

     

RFC 5652 section 5.2

因此,对于嵌入到PDF中的签名容器,应省略eContent以表示此eContent未签名但是单独的数据,即周围的PDF数据。

OP签名文档的签名容器包含此EncapsulatedContentInfo对象:

    <30 0F>
  43   15:       SEQUENCE {
    <06 09>
  45    9:         OBJECT IDENTIFIER data (1 2 840 113549 1 7 1)
         :           (PKCS #7)
    <A0 02>
  56    2:         [0] {
    <04 00>
  58    0:           OCTET STRING
         :           }
         :         }

因此,这个签名容器没有省略eContent,因此iText将此数组包含在其检查中以最终失败。

这实际上是一个错误,但是

我以一种相当克制的方式制定了上述内容,原因很简单:尽管EncapsulatedContentInfo类型的使用具有误导性,但在手边的情况下并非完全无效

对于原始ISO 32000-1集成签名,该规范指定

  

当使用PKCS#7签名时,内容的值应为DER编码的PKCS#7二进制数据   包含签名的对象。 PKCS#7对象应符合RFC3852加密消息   句法。可以使用不同的子过滤器,并应按照附录E进行注册。 SubFilter 应   采用以下值之一:

     
      
  • adbe.pkcs7.detached :文档字节范围内的原始签名消息摘要应为   作为正常的PKCS#7 SignedData字段合并。 PKCS#7中不应封装任何数据   SignedData字段。

  •   
  • adbe.pkcs7.sha1 :文档字节范围的SHA1摘要应封装在PKCS#7中   带有DataInfo的ContentInfo的SignedData字段。该SignedData的摘要应合并为   正常的PKCS#7消化。

  •   
     

ISO 32000-1部分12.8.3.3 PKCS#7 ISO 32000中使用的签名)

因此,对于这样的签名,要么没有封装数据(甚至不是零长度字节数组),要么封装数据是要检查的摘要值。

这就是为什么iText的验证码在一个地方假设它是在后一种情况下:因为有一些封装的内容,它必须是要检查的哈希。

但是,手头的签名不是原始类型之一,而是使用 SubFilter ETSI.CAdES.detached 。这些签名最初由ETSI指定:

  

4.2一般要求

     

对于本文件中涉及的所有概况:

     

b)CMS(RFC 3852)中指定的DER编码的SignedData对象应作为PDF包含在内   使用ISO 32000-1中描述的签名字典的内容键的条目中的签名,   第12.8.1条。此CMS对象形成TS 101 733中描述的CAdES签名,因为它可能包含   以下条款中给出的规则所要求的几个属性。

     

d)ISO 32000-1中规定的要求,条款12.8.3.2(PKCS#1)和12.8.3.3(PKCS#7)签名为   ISO 32000-1中使用的不适用。

     

4.6.2文件摘要

     

验证者应检查文件摘要是否与ISO 32000-1中规定的签名中的文摘摘要相匹配,   第12.8.1条

     

ETSI TS 102 778-3 V1.1.2 (2009-12)

因此,对于当前情况下的签名,以前部分引用的ISO 32000-1部分不适用,签名中的散列(消息摘要属性)必须与文档摘要匹配(按定义计算)通过ISO 32000-1,即仅针对定义的字节范围),而不考虑封装内容。

解决方法

如果您在调用PdfPKCS7之前稍微修补了PdfPKCS7.verify()对象,则可以使用iText正面验证该文档,参见测试VerifySignature.java

原始验证

System.out.println("Signature name: " + name);
System.out.println("Signature covers whole document: " + acroFields.signatureCoversWholeDocument(name));
PdfPKCS7 pk = acroFields.verifySignature(name);
System.out.println("Subject: " + CertificateInfo.getSubjectFields(pk.getSigningCertificate()));
System.out.println("Document verifies: " + pk.verify());

返回失败

Signature name: Signature1
Signature covers whole document: false
Subject: {SURNAME=[CHARPENTIER DIAZ], C=[CR], OU=[CIUDADANO], SN=[CPF-01-1093-0964], CN=[JOSE ALBERTO CHARPENTIER DIAZ (FIRMA)], GIVENNAME=[JOSE ALBERTO], O=[PERSONA FISICA]}
Document verifies: false

修补验证

System.out.println("Signature name: " + name);
System.out.println("Signature covers whole document: " + acroFields.signatureCoversWholeDocument(name));
PdfPKCS7 pk = acroFields.verifySignature(name);
System.out.println("Subject: " + CertificateInfo.getSubjectFields(pk.getSigningCertificate()));

Field rsaDataField = PdfPKCS7.class.getDeclaredField("RSAdata");
rsaDataField.setAccessible(true);
Object rsaDataFieldContent = rsaDataField.get(pk);
if (rsaDataFieldContent != null && ((byte[])rsaDataFieldContent).length == 0)
{
    System.out.println("Found zero-length encapsulated content: ignoring");
    rsaDataField.set(pk, null);
}
System.out.println("Document verifies: " + pk.verify());

返回成功:

Signature name: Signature1
Signature covers whole document: false
Subject: {SURNAME=[CHARPENTIER DIAZ], C=[CR], OU=[CIUDADANO], SN=[CPF-01-1093-0964], CN=[JOSE ALBERTO CHARPENTIER DIAZ (FIRMA)], GIVENNAME=[JOSE ALBERTO], O=[PERSONA FISICA]}
Found zero-length encapsulated content: ignoring
Document verifies: true

(补丁尝试轻柔,只将零长度字节数组补丁到null。)