Avoiding the PDF Digital Signature Vulnerabilities with iText

Some details about digital signatures and document verification in iText.

21st May 2019
ian.morris

Blog_PDFdigtialsignature_1140x300.png

In February 2019, a team of security researchers from the Ruhr-University Bochum in Germany published details of vulnerabilities in the digital signing system of many PDF viewers and online PDF digital signing services. After investigating these vulnerabilities, we found that recent updates to iText introduced in version 7.1.5 mean we are not vulnerable to the described attacks. In addition (and although iText 5 is now EOL (except for security releases)), we have released a maintenance update (version 5.5.13.1) to support these users as well. If you would like to confirm your installation by using the PDF documents we utilized in our tests, you can access our test suite on GitHub.

However, it was determined that the current names of the methods for checking and verifying signatures could be improved to better reflect their functionality. Therefore we have decided to deprecate the SignatureUtil#verifySignature and PdfPKCS7#verify methods, and replace them with SignatureUtil#readSignatureData  and PdfPKCS7#verifySignatureIntegrityAndAuthenticity which have been introduced in iText 7.1.6.

For those who would like to know more about the three types of attacks described in the report and how iText document verification works, see below.

The first one is Universal Signature Forgery (USF), and is based on simply breaking the required signature data, in the hope that a PDF processor will perform some kind of fall-back mechanism but will still mark the signature as valid. This attack is very basic, and iText simply refuses to handle these types of signatures even at the first stage of the signature verification process, which is the signature integrity and authenticity check. Either an exception caused by an invalid document will be thrown, or one of the following method calls will yield null or false.

For iText 7.1.6

SignatureUtil.readSignatureData(signatureFieldName) and signature1.verifySignatureIntegrityAndAuthenticity().

For iText 7.1.5

SignatureUtil.verifySignature(signatureFieldName) and signature1.verify().

Below are code examples showing how to use the signature integrity and authenticity checks for each of these versions of iText.

iText 7.1.6:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PdfDocument pdfDocument = new PdfDocument(new PdfReader(input));
 
// Checks that signature is genuine and the document was not modified.
boolean genuineAndWasNotModified = false;
 
String signatureFieldName = "Signature1";
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
try {
    PdfPKCS7 signature1 = signatureUtil.readSignatureData(signatureFieldName);
    if (signature1 != null) {
        genuineAndWasNotModified = signature1.verifySignatureIntegrityAndAuthenticity();
    }
} catch (Exception ignored) {
    // ignoring exceptions,
    // we are only interested in signatures that are passing the check successfully
}
 
pdfDocument.close();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PdfDocument pdfDocument = new PdfDocument(new PdfReader(inputFile));
 
// Checks that signature is genuine and the document was not modified.
Boolean genuineAndWasNotModified = false;
 
String signatureFieldName = "Signature1";
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
try {
    PdfPKCS7 signature1 = signatureUtil.ReadSignatureData(signatureFieldName);
    if (signature1 != null) {
        genuineAndWasNotModified = signature1.VerifySignatureIntegrityAndAuthenticity();
    }
} catch (Exception ignored) {
    // ignoring exceptions,
    // we are only interested in signatures that are passing the check successfully
}
 
pdfDocument.Close();
    }

 

iText 7.1.5:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PdfDocument pdfDocument = new PdfDocument(new PdfReader(input));
 
// Checks that signature is genuine and the document was not modified.
boolean genuineAndWasNotModified = false;
 
String signatureFieldName = "Signature1";
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
try {
    PdfPKCS7 signature1 = signatureUtil.verifySignature(signatureFieldName);
    if (signature1 != null) {
        genuineAndWasNotModified = signature1.verify();
    }
} catch (Exception ignored) {
    // ignoring exceptions,
    // we are only interested in signatures that are passing the check successfully
}
 
pdfDocument.close();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PdfDocument pdfDocument = new PdfDocument(new PdfReader(input));
 
// Checks that signature is genuine and the document was not modified.
bool genuineAndWasNotModified = false;
 
String signatureFieldName = "Signature1";
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
try {
    PdfPKCS7 signature1 = signatureUtil.VerifySignature(signatureFieldName);
    if (signature1 != null) {
        genuineAndWasNotModified = signature1.Verify();
    }
} catch (Exception ignored) {
    // ignoring exceptions,
    // we are only interested in signatures that are passing the check successfully
}
 
pdfDocument.Close();
    }

 

The second and third types of attack, Incremental Saving Attack (ISA) and Signature Wrapping (SWA), are based on attempts to insert into the PDF file some malicious data to override signed data.

In order to validate every signature, it is necessary to check if it covers the entire file, otherwise iText cannot be sure that signature in question indeed signs the data that constitutes the current PdfDocument and all its contents. Even though the signature is authentic and signed data integrity is intact, iText will always check that signed data is not only a part of the PDF content but is also a valid PDF file.

iText implements this check in the SignatureUtil.signatureCoversWholeDocument(String fieldName) method. For both ISA and SWA attacks, this method will return false, because some unsigned data was inserted into the file:

We actually addressed this specific issue back in November 2018 (before we were aware of the reported vulnerabilities) with a rewrite of the signatureCoversWholeDocument() method. Providing you use iText 7.1.5 (or newer) the following code should correctly validate the PDF:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PdfDocument pdfDocument = new PdfDocument(new PdfReader(input));
 
String signatureFieldName = "Signature1";
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
 
Boolean completeDocumentIsSigned = signatureUtil.signatureCoversWholeDocument(signatureFieldName)}
if (!completeDocumentIsSigned) 
{
    // handle PDF file which contains NOT signed data
}
 
pdfDocument.close();
 
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
PdfDocument pdfDocument = new PdfDocument(new PdfReader(inputFile));
 
String signatureFieldName = "Signature1";
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
 
Boolean completeDocumentIsSigned = signatureUtil.SignatureCoversWholeDocument(signatureFieldName);
if (!completeDocumentIsSigned)
{
    // handle PDF file which contains NOT signed data
}
 
pdfDocument.Close();
  
    }

 

Note: Signatures that do not cover the entire document cannot be considered as verifying the PDF file, because content that is not covered by the signature might have been modified since the signature was created.

For iText 5 users, the following code example shows the equivalent integrity, authenticity and document coverage logic:

iText 5.5.13.1

1
2
3
4
5
6
7
8
9
PdfReader pdfReader = new PdfReader(input);
    AcroFields fields = pdfReader.getAcroFields();
 
    String fieldName = "Signature1";
    PdfPKCS7 signature1 = fields.verifySignature(fieldName);
 
    boolean genuineAndWasNotModified  =  signature1.verify();
    boolean completeDocumentIsSigned = fields.signatureCoversWholeDocument(fieldName);
    }
1
2
3
4
5
6
7
8
9
PdfReader pdfReader = new PdfReader(input);
    AcroFields fields = pdfReader.AcroFields;
 
    String fieldName = "Signature1";
    PdfPKCS7 signature1 = fields.VerifySignature(fieldName);
 
    bool genuineAndWasNotModified = signature1.Verify();
    bool completeDocumentIsSigned = fields.SignatureCoversWholeDocument(fieldName);
    }

 

Please make sure you use the latest maintenance release of iText 5 (version 5.5.13.1 at the time of writing) or consider upgrading to iText 7 for more comprehensive digital signature security and more features.



Contact

Still have questions? 

We're happy to answer your questions. Reach out to us and we'll get back to you shortly.

Contact us
Stay updated

Join 11,000+ subscribers and become an iText PDF expert by staying up to date with our new products, updates, tips, technical solutions and happenings.

Subscribe Now