Skip to content

Extracting Signatories and Signing Dates From a Signed PDF Contract

10 min read · updated August 11, 2026

The signature block at the end of a contract is a picture. It can say anything. If the PDF was signed cryptographically there is a second, machine-readable record of who signed and when, and it lives in the file structure rather than on the page — but it answers a narrower question than most people assume.

Three different things called a signature

Before you write any extraction, decide which of these you are looking at, because they support completely different claims.

  • A typed or drawn mark on the page. A cursive font rendering of a name, a scanned wet signature, or a finger-drawn scribble pasted as an image. This is content. It has exactly the evidential weight of any other text in the document, which is to say the document says somebody signed it.
  • A digital signature over a byte range. A PKCS#7 structure embedded in the file, covering a specified span of the file’s bytes, made with a private key whose certificate names a subject. This is checkable offline.
  • A platform audit record. The certificate of completion an e-signature service appends, listing each signer, their email, timestamps, and often an IP address and an envelope identifier. This is a business record produced by a third party.

A single delivered PDF often has all three, and they can disagree. The extraction schema should carry all three as separate sourced fields rather than collapsing them into one signers array, because the question a downstream reviewer asks is not “who signed” but “on what basis do we believe it”.

What the signature dictionary contains

In the PDF file format, published as ISO 32000 and available from Adobe as PDF 32000-1, a signature is a form field of type /Sig whose value is a signature dictionary. The entries you care about:

<< /Type /Sig
   /Filter    /Adobe.PPKLite
   /SubFilter /ETSI.CAdES.detached
   /ByteRange [0 8412 42130 3271]
   /Contents  <308206...>          % the PKCS#7 / CMS blob
   /Name      (A. Signer)
   /M         (D:20260410153012+02'00')
   /Reason    (I am the author of this document)
   /Location  (Rotterdam)
>>

/ByteRange is two offset-and-length pairs. It says: bytes 0 to 8412 and bytes 42130 to 45400 are covered by this signature. The gap between them is the hex string in /Contents itself, which obviously cannot sign itself. /SubFilter tells you which signature profile was used — adbe.pkcs7.detached for the older Adobe profile, ETSI.CAdES.detached for a PAdES signature, and ETSI.RFC3161 for a document timestamp rather than a signature by a person.

/Name and /M are the two fields an extractor is tempted to treat as the answer. /Name is a string somebody put in the dictionary. /M is the signing time as claimed by the signing software, taken from the clock of the machine that made the signature. Neither is verified by anything. The signer’s real identity is the subject of the certificate inside /Contents, and a trusted time is an RFC 3161 timestamp token in the signature’s unsigned attributes, issued by a timestamp authority. If your pipeline reports /M as “the signing date” without saying that it is self-asserted, you have laundered a claim into a fact.

What it proves, and what it does not

A validated digital signature supports one narrow statement: the bytes in the covered ranges have not changed since a key corresponding to this certificate signed them. Everything else is inference, and the inferences that fail most often are these.

  • The covered range may not be the whole file. PDF allows incremental updates — new objects appended after the signed region. Annotations, form fills, even added pages can appear after the last covered byte. The signature is still valid and the document you are reading is not the document that was signed. Compute the file length and compare it against the end of the last /ByteRange pair; if there is content beyond it, that is a flag, not an error. Whether a change was permitted is governed by a /DocMDP certification signature, whose /P value states what modifications were allowed.
  • The certificate subject may not be the signer. This is the big one for e-signature platforms. When a service seals a completed envelope, the certificate subject is often the platform or its signing service, not the individual human. The signature then proves the platform vouches for the file’s integrity, and the identity of the humans comes entirely from the platform’s own audit record. Extracting the certificate subject as signatory_name produces a contract apparently signed by a software company.
  • Trust is a policy decision, not a property of the file. A self-signed certificate produces a cryptographically valid signature. Chain validation against a trust list, revocation checking, and whether the certificate was valid at signing time are separate questions your extractor should record answers to rather than assume.

The platform certificate page

For envelopes routed through an e-signature service, the appended certificate of completion is usually the best available source for the human-level facts. It is machine-generated from the service’s own event log, so it is internally consistent in a way a hand-typed signature block is not, and it carries fields the page body does not: each signer’s email address, the sequence and timestamps of sent, viewed and signed events, an envelope identifier, and the timezone those timestamps are expressed in.

Two practical points. The timezone is stated once, near the top, and then omitted from the individual rows — extract it as a document-level field and apply it, or you will store a set of naive local times and lose an hour twice a year. And the certificate page is laid out as a table with the signer’s name and email in one cell and their event timeline in another, so a text-order read of the page interleaves two signers’ timestamps. That is a reading-order problem of the kind covered in PDF parsing and in two-column reading order, and the fix is to work from coordinates rather than from the text stream.

The failure modes worth handling

The document was flattened. Printing to PDF, or passing the file through an imaging step, rasterises the pages and discards the signature dictionaries entirely. What arrives is a picture of a signed contract with no cryptographic content at all. Your extractor should distinguish “no signature dictionary present” from “signature present and invalid” and never report the first as the second.

Empty signature fields look like signed ones. A prepared contract contains /FT /Sig widget annotations with no value. Counting signature fields tells you how many signatures were asked for; counting fields with a populated /V tells you how many arrived. A partially executed contract is a common and consequential case, and it reads on the page as a document with a blank line.

The visible block and the dictionary disagree. The printed name says one person, the certificate says another, because somebody signed on behalf of somebody else or used a shared account. Record both and let a human decide. This is the case where a side-by-side of extracted values, rather than a single resolved answer, is the correct output — the same reasoning as the notary and witness fields on a notarised document, where the seal and the typed certificate can also contradict each other.

Multiple signatures, one file. Each signer adds an incremental update with its own /ByteRange, and the ranges nest: the first signature covers less of the file than the last. The order of the signature dictionaries in the file is the signing order, which is information the page itself does not carry.

Signature profiles and platform certificate-page layouts both move. PAdES profiles are published by ETSI and the PDF format itself by ISO; treat the specific field names above as current at the time of writing and confirm against the specification before building a validator on them.