PdfPKCS7 .verify() return 错误
PdfPKCS7 .verify() return false
我一直在使用 itextpdf-5.5.5,完整性检查 return 错误。
PdfPKCS7 pkcs7 = fields.verifySignature(name);
pkcs7.verify();<------ HERE return false
但是 Adobe Acrobat Reader 说:已签名且所有签名均有效。 :(
这是文档:Test.pdf。
有人可以解释一下发生了什么吗?。
提前致谢
您的 PDF 中嵌入的 CMS 签名容器具有一些可疑的属性。特别是它有一个封装的内容,即使它只是一个长度为 0 的字节数组。这使得 iText 在测试中包含这个值然后失败。我建议创建更干净的签名容器。
详细
CMS 对象类型 EncapsulatedContentInfo
指定为:
5.2. EncapsulatedContentInfo Type
The content is represented in the type EncapsulatedContentInfo:
EncapsulatedContentInfo ::= SEQUENCE {
eContentType ContentType,
eContent [0] EXPLICIT OCTET STRING OPTIONAL }
ContentType ::= OBJECT IDENTIFIER
The fields of type EncapsulatedContentInfo have the following
meanings:
eContentType is an object identifier. The object identifier
uniquely specifies the content type.
eContent is the content itself, carried as an octet string. The
eContent need not be DER encoded.
The optional omission of the eContent within the
EncapsulatedContentInfo field makes it possible to construct
"external signatures". In the case of external signatures, the
content being signed is absent from the EncapsulatedContentInfo value
included in the signed-data content type. If the eContent value
within EncapsulatedContentInfo is absent, then the signatureValue is
calculated and the eContentType is assigned as though the eContent
value was present.
因此对于嵌入到 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 集成签名,该规范指定
When PKCS#7 signatures are used, the value of Contents shall be a DER-encoded PKCS#7 binary data
object containing the signature. The PKCS#7 object shall conform to RFC3852 Cryptographic Message
Syntax. Different subfilters may be used and shall be registered in accordance with Annex E. SubFilter shall
take one of the following values:
- adbe.pkcs7.detached: The original signed message digest over the document’s byte range shall be
incorporated as the normal PKCS#7 SignedData field. No data shall be encapsulated in the PKCS#7
SignedData field.
- adbe.pkcs7.sha1: The SHA1 digest of the document’s byte range shall be encapsulated in the PKCS#7
SignedData field with ContentInfo of type Data. The digest of that SignedData shall be incorporated as the
normal PKCS#7 digest.
(ISO 32000-1 section 12.8.3.3 PKCS#7 Signatures as used in ISO 32000)
因此对于此类签名,要么没有封装数据(甚至没有零长度字节数组),要么封装数据是要检查的摘要值。
这就是为什么iText的验证码有一处假设是后一种情况:因为有一些封装的内容,所以一定是hash来校验。
虽然手头的签名不是那些原始类型之一,但它使用了 SubFilter ETSI.CAdES.detached。这些签名最初由 ETSI 指定:
4.2 General Requirements
For all profiles covered in the present document:
b) A DER-encoded SignedData object as specified in CMS (RFC 3852) shall be included as the PDF
signature in the entry with the key Content of the signature dictionary as described in ISO 32000-1,
clause 12.8.1. This CMS object forms a CAdES signature described in TS 101 733 as it may contain
several attributes required by the rules given in the following clauses.
d) Requirements specified in ISO 32000-1, clauses 12.8.3.2 (PKCS#1) and 12.8.3.3 (PKCS#7) signatures as
used in ISO 32000-1 do not apply.
4.6.2 Document Digest
The verifier shall check that the document digest matches that in the signature as specified in ISO 32000-1,
clause 12.8.1
因此对于手头案例中的签名,之前部分引用的 ISO 32000-1 部分不适用,并且签名中的散列(消息摘要属性)必须匹配文档摘要(计算由 ISO 32000-1 定义,即仅针对定义的字节范围)而不考虑封装的内容。
解决方法
如果在 调用 PdfPKCS7.verify()
之前稍微修补 PdfPKCS7
对象 ,则可以使用 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());
returns失败
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());
returns成功:
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
。)
我一直在使用 itextpdf-5.5.5,完整性检查 return 错误。
PdfPKCS7 pkcs7 = fields.verifySignature(name);
pkcs7.verify();<------ HERE return false
但是 Adobe Acrobat Reader 说:已签名且所有签名均有效。 :(
这是文档:Test.pdf。
有人可以解释一下发生了什么吗?。
提前致谢
您的 PDF 中嵌入的 CMS 签名容器具有一些可疑的属性。特别是它有一个封装的内容,即使它只是一个长度为 0 的字节数组。这使得 iText 在测试中包含这个值然后失败。我建议创建更干净的签名容器。
详细
CMS 对象类型 EncapsulatedContentInfo
指定为:
5.2. EncapsulatedContentInfo Type
The content is represented in the type EncapsulatedContentInfo:
EncapsulatedContentInfo ::= SEQUENCE {
eContentType ContentType,
eContent [0] EXPLICIT OCTET STRING OPTIONAL }
ContentType ::= OBJECT IDENTIFIER
The fields of type EncapsulatedContentInfo have the following meanings:
eContentType is an object identifier. The object identifier uniquely specifies the content type.
eContent is the content itself, carried as an octet string. The eContent need not be DER encoded.
The optional omission of the eContent within the EncapsulatedContentInfo field makes it possible to construct "external signatures". In the case of external signatures, the content being signed is absent from the EncapsulatedContentInfo value included in the signed-data content type. If the eContent value within EncapsulatedContentInfo is absent, then the signatureValue is calculated and the eContentType is assigned as though the eContent value was present.
因此对于嵌入到 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 集成签名,该规范指定
When PKCS#7 signatures are used, the value of Contents shall be a DER-encoded PKCS#7 binary data object containing the signature. The PKCS#7 object shall conform to RFC3852 Cryptographic Message Syntax. Different subfilters may be used and shall be registered in accordance with Annex E. SubFilter shall take one of the following values:
- adbe.pkcs7.detached: The original signed message digest over the document’s byte range shall be incorporated as the normal PKCS#7 SignedData field. No data shall be encapsulated in the PKCS#7 SignedData field.
- adbe.pkcs7.sha1: The SHA1 digest of the document’s byte range shall be encapsulated in the PKCS#7 SignedData field with ContentInfo of type Data. The digest of that SignedData shall be incorporated as the normal PKCS#7 digest.
(ISO 32000-1 section 12.8.3.3 PKCS#7 Signatures as used in ISO 32000)
因此对于此类签名,要么没有封装数据(甚至没有零长度字节数组),要么封装数据是要检查的摘要值。
这就是为什么iText的验证码有一处假设是后一种情况:因为有一些封装的内容,所以一定是hash来校验。
虽然手头的签名不是那些原始类型之一,但它使用了 SubFilter ETSI.CAdES.detached。这些签名最初由 ETSI 指定:
4.2 General Requirements
For all profiles covered in the present document:
b) A DER-encoded SignedData object as specified in CMS (RFC 3852) shall be included as the PDF signature in the entry with the key Content of the signature dictionary as described in ISO 32000-1, clause 12.8.1. This CMS object forms a CAdES signature described in TS 101 733 as it may contain several attributes required by the rules given in the following clauses.
d) Requirements specified in ISO 32000-1, clauses 12.8.3.2 (PKCS#1) and 12.8.3.3 (PKCS#7) signatures as used in ISO 32000-1 do not apply.
4.6.2 Document Digest
The verifier shall check that the document digest matches that in the signature as specified in ISO 32000-1, clause 12.8.1
因此对于手头案例中的签名,之前部分引用的 ISO 32000-1 部分不适用,并且签名中的散列(消息摘要属性)必须匹配文档摘要(计算由 ISO 32000-1 定义,即仅针对定义的字节范围)而不考虑封装的内容。
解决方法
如果在 调用 PdfPKCS7.verify()
之前稍微修补 PdfPKCS7
对象 ,则可以使用 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());
returns失败
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());
returns成功:
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
。)