Digital signatures
Sign PDFs programmatically, in any language
Last updated: 2026-06-29
A digital signature seals a PDF so anyone can prove who signed it and that not a single byte changed afterward. rust-pdf adds that seal from code, the same way in every language it supports, using one shared Rust core. This hub explains how signing works and links to a step-by-step guide for each language.
What signing a PDF actually does
A scanned image of a handwritten signature is just a picture. It can be copied onto any file and proves nothing. A real digital signature is cryptographic: your private key produces a mathematical seal over the exact bytes of the document, and anyone can verify that seal with the matching public certificate. If a single byte changes after signing, verification fails. That is what gives you both proof of identity and proof of integrity in one step.
rust-pdf builds the seal as a PKCS#7 detached CMS structure, the format that PDF readers and signature validators expect. The digest is computed over a ByteRange that covers the whole file except the slot reserved for the signature itself, so the signature certifies everything around it. When you enable PAdES, the same machinery switches to the ETSI baseline profile that the EU eIDAS regulation references, adding the signed attributes a PAdES validator looks for.
For a plain-language explanation of PAdES, the wax-seal analogy, and the meaning of each conformance level, read the deep dive at what is PAdES. This page stays focused on the practical task: getting a signed file out of your code.
What you get
The same signing surface is exposed in every language binding.
PKCS#7 and PAdES
A standard detached CMS signature by default, or a PAdES baseline signature with the ESS signing-certificate attribute when you ask for it.
Bytes preserved
Signing appends an incremental update. The original file is left untouched, so existing signatures stay valid and the new seal certifies the exact bytes it covers.
Your keys, your control
You bring the private key and certificate in DER form. rust-pdf never creates or stores keys, so the cryptographic material stays entirely yours.
Offline long-term validation
Extend a signature to B-LT with a Document Security Store, then to B-LTA with a real RFC 3161 timestamp, all without contacting an external service.
Visible and multiple signatures
Stamp a visible signature appearance on a page, and let several people sign the same document in sequence without invalidating earlier signatures.
Verifiable everywhere
The output validates in Adobe Reader, pdfsig and openssl cms. rust-pdf can also verify a signed file by recomputing the ByteRange digest and checking the embedded CMS.
Why incremental updates matter
Most PDF tools rewrite a file when they change it. That is fine for a draft, but fatal for a signature, because a signature is a promise about a precise sequence of bytes. Rewrite the file and that promise no longer matches anything.
rust-pdf never rewrites when signing. It appends only the new objects, the signature dictionary, the signature widget and the form fields, then a fresh cross-reference section chained to the old one. Everything that came before is preserved verbatim. This is the property that lets two or three parties countersign a contract, each new signature covering all the bytes up to its own point, while every earlier signature continues to verify against the bytes that existed when it was made.
From a basic seal to long-term proof
The same three calls build up the PAdES baseline levels, entirely offline.
Basic signature
The PKCS#7 or PAdES seal plus the signing certificate. Proves who signed and that nothing changed.
Add validation material
add_dss writes a Document Security Store with the certificates and revocation data, so the signature still checks years later.
Add an archive timestamp
timestamp appends a real RFC 3161 timestamp token over the whole package, protecting it for the very long term.
rust-pdf supplies the timestamp from a self-issued authority for offline use. You supply the certificates and revocation data, since those come from your own trust chain.
Sign a PDF in your language
Each guide walks through loading the file, supplying the key and certificate, and writing a verified, signed PDF.
How to sign a PDF with rust-pdf
Pass the PDF bytes, your private key and certificate (DER), and a flag for PAdES. The signature is appended as an incremental update, then optionally extended.
# pip install rustpdf
import rustpdf
pdf = open("contract.pdf", "rb").read()
key = open("signing-key.pkcs8.der", "rb").read()
cert = open("signing-cert.der", "rb").read()
signed = rustpdf.sign(pdf, key, cert, reason="Approved", pades=True) # PAdES-B-B
lt = rustpdf.add_dss(signed, certs=[cert], crls=[crl]) # to B-LT
lta = rustpdf.timestamp(lt, tsa_key, tsa_cert) # to B-LTA
open("contract.signed.pdf", "wb").write(lta)
# Verify in a shell: pdfsig contract.signed.pdf -> "Signature is Valid."
// dotnet add package RustPdf
using RustPdf;
byte[] signed = Pdf.Sign(pdf, keyDer, certDer, reason: "Approved", pades: true); // B-B
byte[] lt = Pdf.AddDss(signed, certs: new[]{ certDer }, crls: new[]{ crl }); // B-LT
byte[] lta = Pdf.Timestamp(lt, tsaKeyDer, tsaCertDer); // B-LTA
// go get github.com/rustpdf/rustpdf-go@latest
signed, _ := rustpdf.Sign(pdf, key, cert, rustpdf.SignOptions{Reason: "Approved", Pades: true})
lt, _ := rustpdf.AddDss(signed, [][]byte{cert}, [][]byte{crl}) // B-LT
lta, _ := rustpdf.Timestamp(lt, tsaKey, tsaCert) // B-LTA
// npm install rustpdf
const { sign, addDss, timestamp } = require("rustpdf");
const signed = sign(pdf, key, cert, { reason: "Approved", pades: true }); // B-B
const lt = addDss(signed, { certs: [cert], crls: [crl] }); // B-LT
const lta = timestamp(lt, tsaKey, tsaCert); // B-LTA
Six language bindings share the same Rust core for signing: Go, Python, C#, PHP, Ruby and Node.js. Full details in the documentation.
Signing is a licensed corporate feature
Generating PDFs with rust-pdf is free. Digital signatures, long-term validation and timestamps are part of the corporate license, gated in the core so the same rule applies in every language. You can build and test signing with the bundled developer token, then activate a production license when you ship. See the plans on the pricing page.
Sign PDF FAQ
How do I digitally sign a PDF in code?
Pass the PDF bytes, your private key and your certificate to the sign function, and the library builds a PKCS#7 or PAdES signature and appends it as an incremental update. The same call exists in Go, Python, C#, PHP, Ruby and Node, all backed by one Rust core, so the output is identical regardless of language.
What signature formats does rust-pdf support?
It produces a standard PKCS#7 detached CMS signature by default, and switches to a PAdES baseline signature when you enable it. PAdES levels B-B, B-LT and B-LTA are supported offline, where B-LT adds a Document Security Store with certificates and revocation data, and B-LTA adds an RFC 3161 archive timestamp.
Does signing change the original PDF?
No. rust-pdf signs by appending an incremental update, so every original byte is preserved verbatim. This is what keeps earlier signatures valid when a second person signs the same file, and it is how the signature stays verifiable against the exact bytes that were sealed.
Do I bring my own key and certificate?
Yes. You supply the private key and the certificate in DER form, plus any chain certificates and revocation data. rust-pdf never generates or stores keys for you, so the cryptographic material stays entirely under your control, which is what makes the resulting signature legally meaningful.