Verify PDF Digital Signatures (cryptographic)
verify_signaturesVerifies digital signatures in a PDF: recomputes digests, checks CMS signature and timestamp, evaluates certificate chain, and checks revocation to detect tampering and confirm signer identity.
Instructions
Cryptographically verify the digital signatures in a PDF document.
For each signature this tool: recomputes the ByteRange digest and compares it with the CMS messageDigest attribute, verifies the CMS/PKCS#7 signature value against the signer certificate, verifies any RFC 3161 signature timestamp, evaluates the certificate chain against trust anchors, and checks revocation status.
Args:
file_path (string): Absolute path to a local PDF file
response_format ('markdown' | 'json'): Output format (default: 'markdown')
trust_anchors (string[], optional): Paths to trust anchor certificates (PEM/DER). Also reads the PDF_VERIFY_TRUST_ANCHORS env var (directory).
check_revocation ('none' | 'embedded' | 'online'): Revocation mode (default: 'embedded'; 'online' queries OCSP/CRL endpoints over HTTP)
revocation_freshness (integer seconds, default 86400): How long before the validation time a CRL / OCSP response may have been issued and still count as "good"
trusted_ocsp_responders (string[], optional): Certificates of locally trusted OCSP responders (RFC 6960 §4.2.2.2)
password (string, optional): Password for an encrypted PDF (permission-encrypted PDFs are decrypted automatically with the empty user password)
Returns: An object of the form { scope, signatures: [...] }. The top level changed from an array to an object in v0.21.0 - read .signatures for the list.
Size (v0.29.0): a JSON response is never cut by length. At most 32 signature fields are verified (file order); when the file has more, signaturesTruncated = { returned, total } is set and the remaining fields are NOT verified — evaluate_policy verifies every field. A markdown response is cut at 50,000 characters with a visible marker.
Every report begins with a "scope" object - how far the reading got, not a verdict: whether the cross-reference chain could be walked to the end (chainStop), whether this tool had to rebuild the cross-reference table itself (reconstructed - when true, the table is this tool's reconstruction and not the one the file carries), how many objects and sections were read, and whether an encrypted document could be opened. Read it before the verdict: "no violations" over a rebuilt table is not the same statement as "no violations" over the file's own table. For this tool it matters most: when scope.reconstructed is true, a signature the rebuild did not reach is absent from the list, so a short or empty list is not proof that the file carries no other signatures.
Per-signature verdict ('valid' / 'invalid' / 'indeterminate'), trust status ('trusted' / 'untrusted' / 'not_evaluated' with certificate path), revocation status ('good' / 'revoked' / 'revoked_after_validation_time' / 'unknown' / 'not_checked'; 'not_checked' when check_revocation is 'none') with source, origin ('dss' / 'cms_signed_data' / 'cms_revocation_info_archival'), revocationTime, thisUpdate and nextUpdate, per-intermediate-CA results in trust.chainRevocation, validationTime ({ time, source: 'signature_timestamp' | 'document_timestamp' | 'current_time' }), and signature timestamp verification.
Validation time: a verified timestamp (the signature's own, else the earliest document timestamp covering it) or, without one, the current time. The CMS signingTime attribute is written by the signer and is never used. A revoked signer certificate makes the verdict 'indeterminate' unless a timestamp proves the signature predates the revocation (then the status is 'revoked_after_validation_time' and the verdict is unchanged). CRLs and OCSP responses whose signatures cannot be verified, that expired before the validation time, or that were issued more than revocation_freshness seconds before it give 'unknown'.
Note: without trust_anchors (or the env var), trust is reported as not_evaluated — a 'valid' verdict then means cryptographic integrity, not signer identity assurance.
Complements pdf-reader-mcp's inspect_signatures, which inspects structure only.
Examples:
Verify a signed contract has not been altered since signing
Validate a signature against your organization's CA (trust_anchors)
Check whether the signer certificate has been revoked (check_revocation: "online")
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| password | No | Password for an encrypted PDF. Omit for permission-encrypted PDFs (an empty user password is tried automatically). | |
| file_path | Yes | Absolute path to a local PDF file (e.g., "/path/to/document.pdf") | |
| trust_anchors | No | Absolute paths to trust anchor certificates (PEM or DER). Merged with the PDF_VERIFY_TRUST_ANCHORS environment variable (a directory of *.pem/*.crt/*.cer/*.der files). When omitted and the env var is unset, trust is reported as not_evaluated. | |
| response_format | No | Output format: "markdown" for human-readable, "json" for structured data | markdown |
| check_revocation | No | Revocation checking: "none", "embedded" (OCSP/CRL data inside the PDF/CMS, default), or "online" (additionally query OCSP responders and CRL distribution points over HTTP). | embedded |
| revocation_freshness | No | Seconds before the validation time that a CRL / OCSP response may have been issued (thisUpdate) and still support "good". Default 86400 (24 h); 0 accepts only data issued at or after the validation time. Older data gives "unknown". | |
| trusted_ocsp_responders | No | Absolute paths to certificates (PEM or DER) of locally trusted OCSP responders (RFC 6960 §4.2.2.2). A response signed by one of them is accepted even when the responder is not the issuing CA or its delegate. |