3.28.  Signed PDF

Overview

If contractual information is sent as a PDF document, you must check that the data really was sent by the person they claim to be. Certificates allow this. A certificate confirms the authenticity of personal or corporate data. It confirms your signature when you sign the content of a document in a special formular field.

PDFUnit provides these test methods for signatures:

// Simple methods for signatures:
.isSigned()
.isSignedBy(..)
.hasNumberOfSignatures(..) 
.hasSignatureField(..)
.hasSignatureFields()

// Detailed tests for one signature:
.hasSignatureField(..).withSignature(..).coveringWholeDocument()
.hasSignatureField(..).withSignature(..).signedBy(name)
.hasSignatureField(..).withSignature(..).signedOn(date)
.hasSignatureField(..).withSignature(..).withReason(..)
.hasSignatureField(..).withoutSignature(..)

// Tests covering all signature fields:
.hasSignatureFields().allSigned()
.hasSignatureFields().allUnSigned()

// Other tests with signatures:
.hasField(..).ofType(SIGNATURE)
.hasField(..).withProperty().signed()

A signed PDF must not be confused with a certified PDF. A certified PDF guarantees the compliance with certain properties which are needed to process a document in further workflow steps. Tests for certified PDF documents are described in chapter 3.6: “Certified PDF”.

Existence of Signatures

The simplest test is to check whether a document is actually signed:

@Test
public void isSigned() throws Exception {
  String filename = "sampleSignedPDFDocument.pdf";

  AssertThat.document(filename)
            .isSigned()
  ;
}

Multiple signature fields can be verified together:

@Test
public void allFieldsSigned() throws Exception {
  String filename = "documentUnderTest.pdf";
  AssertThat.document(filename)
            .hasSignatureFields()
            .allSigned()
  ;
}

The method .allUnsigned() validates the absence of all signatures in all fields.

PDFUnit can check, whether a particular signature field contains a signature:

@Test
public void hasField_Signed() throws Exception {
  String filename = "sampleSignedPDFDocument.pdf";
  String fieldnameSignedField = "Signature2"; 
  
  AssertThat.document(filename)
            .hasField(fieldnameSignedField) 
            .withProperty()
            .signed()
  ;
}

The next example shows how to check whether there are any specific signature fields:

@Test
public void hasSignatureFields() throws Exception {
  String filename = "sampleSignedPDFDocument.pdf";

  AssertThat.document(filename)
            .hasSignatureField("Signature of Seller")
            .hasSignatureField("Signature of Buyer")
  ;
}

The previous example can be extended to check whether specific signature fields are signed:

@Test
public void hasSignatureFieldsWithSignature() throws Exception {
  String filename = "sampleSignedPDFDocument.pdf";

  AssertThat.document(filename)
            .hasSignatureField("Signature of Seller").withSignature()
            .hasSignatureField("Signature of Buyer").withSignature()
  ;
}

The function .hasSignatureField("name").withoutSignature() checks that a signature field is unsigned.

Number of Signatures

Because a document may contain multiple signatures, a test exists to check the number of the signatures:

@Test
public void hasNumberOfSignatures() throws Exception {
  String filename = "sampleSignedPDFDocument.pdf";

  AssertThat.document(filename)
            .hasNumberOfSignatures(1)
  ;
}

Validity Date

Sometimes, you need to know when a PDF document was signed:

@Test 
public void hasSignature_WithSigningDate() throws Exception {
  String filename = "sampleSignedPDFDocument.pdf";
  Calendar signingDate = DateHelper.getCalendar("2009-07-16", "yyyy-MM-dd");

  AssertThat.document(filename)
            .hasSignatureField("Signature2")
            .signedOn(signingDate)  1
  ;
}

1

The comparison is allways made on the base of year-month-day.

Reason for a Signature

Maybe, it's not interesting enough to validate the signature-reason. But if it turns out to be, it can be checked:

@Test
public void hasSignature_WithReason() throws Exception {
  String filename = "sampleSignedPDFDocument.pdf";
  
  AssertThat.document(filename)
            .hasSignatureField("Signature2")
            .withSignature()
            .withReason("I am the author of this document")
  ;
}

Whitespaces will be normalized before string comparison.

Name of the Signatory

The name of the person who signed a PDF document can be checked as well, even if such a test is more interesting for validation in a productive work flow than in a testing scenario.

@Test
public void hasSignature_WithSigningName() throws Exception {
  String filename = "sampleSignedPDFDocument.pdf";

  AssertThat.document(filename)
            .hasSignatureField("Signature2")
            .withSigningName("John B Harris")
  ;
}

The following test checks that a document is signed by the expected person. This test is unrelated to a signature field.

@Test
public void isSignedBy() throws Exception {
  String filename = "sampleSignedPDFDocument.pdf";

  AssertThat.document(filename)
            .isSignedBy("John B Harris")
  ;
}

Scope of the Signature

The PDF standard allows a signature to cover only a part of a document. Thus, one may need to test whether a signature covers the complete document:

@Test
public void hasSignature_CoveringWholeDocument() throws Exception {
  String filename = "sampleSignedPDFDocument.pdf";

  AssertThat.document(filename)
            .hasSignatureField("Signature2")
            .coveringWholeDocument()
  ;
}

Multiple Invocation

Of course, test methods can be chained when they are related to one signature field:

@Test
public void differentAspectsAroundSignature() throws Exception {
  String filename = "helloWorld_signed.pdf";
  Calendar signingDate = DateHelper.getCalendar("2007-10-14", "yyyy-MM-dd");
  
  AssertThat.document(filename)
            .hasSignatureField("sign_rbl")
            .signedBy("Raymond Berthou")
            .signedOn(signingDate)
            .coveringWholeDocument()
  ;
}

But think of a better name for this test. It would be better to split it into several tests with specific names.