Signature Timestamp
A signature timestamp provides evidence that a digital signature existed at the TSA's asserted time. Unlike a Document Timestamp, it timestamps the signature value, not the PDF's byte ranges. The token is stored in the signature's CMS unsigned attributes, as described in RFC 3161, Appendix A.
This example creates a new PDF signature and adds its timestamp before delivering the finished PDF.
1. Set up access and tools
Configure your client ID and outbound IP using the timestamping access instructions. You need Python 3.10+, OpenSSL 3+, curl 7.76+, and an unencrypted PDF.
Download timestamping-examples.zip and extract it. It contains one script, timestamp_pdf.py, for both examples and a sample original.pdf. In the extracted directory, install the library:
python3 -m venv .venv
. .venv/bin/activate
python -m pip install 'pyhanko==0.35.1'
Demo mode generates a fresh self-signed signing certificate locally, so you do not need a signer.p12 file. This identity is for testing only; its private key stays in memory.
2. Create the signature and timestamp request
python timestamp_pdf.py signature prepare original.pdf signature-work --demo
Use a new directory for signature-work. The command creates:
signed.pdf: a new PDF signature with space reserved for its CMS timestamp attribute.signature.json: metadata binding the request to that signature.request.tsq: a binary RFC 3161 request containingSHA256(signatureValue).demo-signer.pem: the public demo signing certificate, used for validation.
Keep these files unchanged. The timestamp digest is calculated from the raw CMS SignerInfo.signature bytes—not the PDF, the complete CMS object, or Base64 text. No network request has been made yet.
3. Request the timestamp
export CLIENT_ID='YOUR_CLIENT_ID'
curl --silent --show-error --fail-with-body \
"https://id.eideasy.com/api/timestamp/${CLIENT_ID}" \
--header 'Content-Type: application/octet-stream' \
--data-binary @signature-work/request.tsq \
--output signature-work/response.tsr
Stop if curl fails. Inspect the saved error message and Case ID before continuing.
4. Embed the signature timestamp
python timestamp_pdf.py signature complete signature-work
The script checks the response status, signature digest and TSA chain, then extracts the CMS token and adds it as the signature's signature-time-stamp unsigned attribute. It includes the public SK TSA chain; no separate TSA certificate files are needed.
The output is signature-work/timestamped.pdf, with the extracted token in timestamp-token.der. The PDF's covered bytes, signed attributes and original signature value remain unchanged. The intermediate signed.pdf is retained.
5. Validate the result
python timestamp_pdf.py signature validate signature-work/timestamped.pdf --demo
Expect signature_intact=True, signature_valid=True, signer_trusted=True, timestamp_valid=True, and whole-file coverage. Signer trust and TSA trust are checked separately; validation may fetch revocation information.
--demo explicitly trusts the locally generated demo certificate for this validation only. Your PDF viewer may still report that signer as unknown. A timestamp does not make a test signer a trusted production identity.
To use your own signing identity instead, replace --demo during preparation with --pfx signer.p12; the script prompts for its password. Validate without --demo, adding --signer-ca-file if your signing chain needs custom trust configuration.
If the CMS reservation is too small, start again in a new directory with prepare --cms-capacity 65536 and obtain a fresh timestamp for the new signature. For HTTP and TSA trust errors, see troubleshooting.