0
votes

I have been working with itextpdf-5.5.5, and the Integrity check return false.

PdfPKCS7 pkcs7 = fields.verifySignature(name);

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

But Adobe Acrobat Reader say: Signed and all signatures are valid. :(

This is the document: Test.pdf.

Could someone explain what is happening?.

Thanks in advance

1

1 Answers

3
votes

The CMS signature container embedded in your PDF has some questionable properties. In particular it has an encapsulated content, even though it merely is a byte array of length 0. This makes iText include this value in a test which then fails. I would propose creating cleaner signature containers.

In detail

The CMS object type EncapsulatedContentInfo is specified as:

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.

(RFC 5652 section 5.2)

So for signature containers embedded into a PDF, eContent should be omitted to indicate that not this eContent is signed but separate data, the surrounding PDF data.

The signature container of the OP's signed document contains this EncapsulatedContentInfo object:

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

Thus, this signature container does not have omitted the eContent and so makes iText include this array into its checks to eventually fail.

This actually is a bug, though

I formulated the above in a fairly restrained manner for a simple reason: Even though that use of the EncapsulatedContentInfo type is misleading, it is not outright invalid in the case at hand!

For the original ISO 32000-1 integrated signatures, that norm specified

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)

So for such signatures there either was no encapsulated data (not even a zero-length byte array) or the encapsulated data were a digest value to check.

This is why iText's verification code in one place assumes it is in the latter case: As there is some encapsulated content, it must be a hash to check.


The signature at hand, though, is not of one of those original types but it uses SubFilter ETSI.CAdES.detached. These signatures are originally specified by 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

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

So for signatures as in the case at hand, the section from ISO 32000-1 partially quoted before does not apply, and the hash in the signature (the message-digest attribute) has to match the document digest (calculated as defined by ISO 32000-1, i.e. only for the defined byte ranges) without consideration of the encapsulated content.

A workaround

If you slightly patch the PdfPKCS7 object before calling PdfPKCS7.verify(), you can positively verify the document with iText, cf. the test VerifySignature.java:

While the original verification

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 a failure

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

the patched verification

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 a success:

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

(The patch attempts to be gentle and only patches zero-length byte arrays to null.)