| Title: | Digitally Sign and Verify PDF Documents |
|---|---|
| Description: | Digitally sign PDF documents with a 'PKCS#12' keystore and verify their signatures. Signing produces a detached 'PKCS#7' / 'CMS' signature ('adbe.pkcs7.detached') over the document and is applied as an incremental update, so existing signatures remain valid. The cryptography and PDF manipulation are performed by a bundled, pure-'Rust' backend (the 'pdf_signer' crate); no Java runtime, 'OpenSSL', or external command-line tools are required. Visible signature appearances with custom text are supported. |
| Authors: | André Leite [aut, cre] (ORCID: <https://orcid.org/0000-0002-4718-9766>), Hugo Vasconcelos [aut] (ORCID: <https://orcid.org/0000-0001-6249-0920>), Diogo Bezerra [aut] (ORCID: <https://orcid.org/0000-0002-1216-8674>), Marcos Wasiliew [aut] (ORCID: <https://orcid.org/0009-0004-4694-3159>), Carlos Amorim [aut] (ORCID: <https://orcid.org/0000-0001-6315-8305>), Júlia Nascimento Barreto [aut] (ORCID: <https://orcid.org/0009-0004-2851-7770>), Authors of the vendored Rust crates [ctb, cph] (see inst/AUTHORS for the bundled crates and their licences) |
| Maintainer: | André Leite <[email protected]> |
| License: | GPL-3 |
| Version: | 0.3.0 |
| Built: | 2026-09-30 19:37:20 UTC |
| Source: | https://github.com/strategicprojects/pdfsigner |
Signs pdf_file using an RSA key + certificate stored in a PKCS#12
(.p12/.pfx) keystore, writing the signed document to output_file. The
signature is a detached adbe.pkcs7.detached CMS over the whole document and
is added as an incremental update, so any pre-existing signatures stay valid.
sign_pdf( pdf_file, output_file, keystore_path = Sys.getenv("KEYSTORE_PATH"), keystore_password = Sys.getenv("KEY_PASSWORD"), signtext = NULL, validate_link = NULL, reason = NULL, signer_name = NULL, page = 1, x = 36, y = 36, width = 320, height = 64, font_size = 8, font = NULL, image = NULL, border = TRUE, translate = FALSE, tsa_url = NULL, pades_level = c("bb", "bt", "blt", "blta") )sign_pdf( pdf_file, output_file, keystore_path = Sys.getenv("KEYSTORE_PATH"), keystore_password = Sys.getenv("KEY_PASSWORD"), signtext = NULL, validate_link = NULL, reason = NULL, signer_name = NULL, page = 1, x = 36, y = 36, width = 320, height = 64, font_size = 8, font = NULL, image = NULL, border = TRUE, translate = FALSE, tsa_url = NULL, pades_level = c("bb", "bt", "blt", "blta") )
pdf_file |
Path to the input PDF. |
output_file |
Path where the signed PDF is written. |
keystore_path |
Path to the |
keystore_password |
Password for the keystore. Defaults to the
|
signtext |
Optional text for a visible signature box. When |
validate_link |
Optional validation URL appended to the visible box. |
reason, signer_name
|
Optional |
page |
1-based page number for the visible box. |
x, y, width, height
|
Visible box geometry, in PDF points (origin at the page's bottom-left). |
font_size |
Font size for the visible box, in points. |
font |
Optional path to a TrueType/OpenType font file ( |
image |
Optional path to a PNG or JPEG logo drawn in the visible box. Ignored for invisible signatures. |
border |
Draw a border around the visible box. |
translate |
If |
tsa_url |
Optional RFC 3161 Time-Stamping Authority |
pades_level |
PAdES conformance level: |
Invisibly, the path to the signed PDF. Raises an error on failure.
## Not run: sign_pdf( pdf_file = "input.pdf", output_file = "signed.pdf", keystore_path = "keystore.p12", keystore_password = "password", signtext = "Document digitally signed by CastLab", validate_link = "https://castlab.org/validate", translate = TRUE ) ## End(Not run)## Not run: sign_pdf( pdf_file = "input.pdf", output_file = "signed.pdf", keystore_path = "keystore.p12", keystore_password = "password", signtext = "Document digitally signed by CastLab", validate_link = "https://castlab.org/validate", translate = TRUE ) ## End(Not run)
Cryptographically verifies every signature and document timestamp in
pdf_file using the bundled Rust backend. Each signature is checked by
re-deriving its signed byte range, validating the CMS profile
(content-type, message-digest, ESS certificate binding) and the signer's
signature over the signed attributes. The document is also judged as a
whole: it is only intact when the last valid signature or document
timestamp covers the entire file, or when everything appended after it is a
PAdES /DSS (long-term validation material). Content changed after signing
therefore fails document_intact even though the original signature still
verifies over its own bytes.
verify_pdf_signature(pdf_file, roots = NULL)verify_pdf_signature(pdf_file, roots = NULL)
pdf_file |
Path to the PDF to verify. |
roots |
Optional path to a PEM file of trusted root certificates (e.g.
the ICP-Brasil AC Raiz set). When supplied, each signer certificate chain
(and each timestamp authority's) is validated against these roots and
reported in |
A list with one entry per signature or document timestamp, in file
order. Each entry is a named list with valid (logical), is_timestamp
(logical, TRUE for a /DocTimeStamp), signer (subject DN),
chain_trusted (logical or NA when no roots given),
covers_whole_document (logical), trusted_time (seconds since the Unix
epoch of the trusted timestamp used, or NA), signed_len (bytes),
byte_range (numeric length-4) and detail. The list carries three
logical attributes with the document-level verdict: document_intact,
all_valid (every entry valid and the document intact) and all_trusted
(all_valid and every entry chains to one of roots; always FALSE
without roots). A length-zero list means no signatures were found.
## Not run: result <- verify_pdf_signature("signed.pdf", roots = "icp-brasil-roots.pem") vapply(result, function(s) s$valid, logical(1)) attr(result, "all_trusted") # the one-line verdict ## End(Not run)## Not run: result <- verify_pdf_signature("signed.pdf", roots = "icp-brasil-roots.pem") vapply(result, function(s) s$valid, logical(1)) attr(result, "all_trusted") # the one-line verdict ## End(Not run)