Mudrit
Guides

Encryption

Sign password-protected PDFs and password-protect the signed output — AES-128 or AES-256, with owner-enforced permissions

Part of mudrit-pdfsign

Encryption touches signing on two independent sides, and Mudrit keeps them separate:

  • Encrypted input — the PDF you're signing is already password-protected. The open password is a property of the document, so it rides on the input via PdfReader, not on the config.
  • Encrypted output — you want the signed result to stay (or become) password-protected. That is a signing decision, so it lives on SignConfig via .encrypt(…).

By default the two are unrelated: signing an encrypted input produces an unencrypted output unless you also ask for output encryption.

Encrypted input

A password belongs to the input document, not to how you sign it — so it travels with the input through PdfReader, and SignConfig stays purely about the signature. Mudrit decrypts the input in memory before signing.

use mudrit_pdfsign::prelude::*;

// from a file…
let signed = sign_pdf(PdfReader::open("locked.pdf")?.password("asd"), &signer, &cfg)?;

// …or from bytes already in memory
let signed = sign_pdf(PdfReader::from(bytes).password("asd"), &signer, &cfg)?;

PdfReader also offers .maybe_password(Option<…>) when the password is already wrapped in an Option.

Wrong or missing password fails loud

Signing an encrypted PDF without a password, or with the wrong one, returns a clear Error::Encryption instead of producing a broken signature. Mudrit never silently signs a document it couldn't fully decrypt.

The bundled samples/test_protected.pdf has open password asd — use it to exercise the encrypted paths (see the sign_protected example).

Encrypted output

Where the input open password is a PdfReader concern, output encryption is a signing concern, so it lives on the builder. The document is re-encrypted before signing, so the output is both signed and password-protected — the signature is computed over the encrypted bytes (its /Contents stays exempt, per the PDF spec).

// a new user (open) password; AES-128 by default
let cfg = SignConfig::builder()
    .place("F", [350, 60, 560, 160])?
    .encrypt("newpw")
    .build();

.encrypt(_) accepts either a password string (a shorthand for OutputEncryption::password(…), AES-128) or a full OutputEncryption. The OutputEncryption fields:

Prop

Type

Choosing the cipher

Prop

Type

Output defaults to AES-128 (AESV2). Choose AES-256 (AESV3 / V5, revision R6 per ISO 32000-2 §7.6.4.3.4) with Cipher::Aes256:

.encrypt(OutputEncryption::password("pw").cipher(Cipher::Aes256))   // AES-256 (R6)

Both open correctly in Adobe Acrobat / Reader — the R6 /Encrypt dictionary is emitted with the /Length and crypt-filter hints Acrobat requires (see the aes256_demo example).

Permissions

Permissions controls what a viewer that opened with the user password may do. The restrictions are enforced by the owner password — the owner password lifts them, so set a distinct owner password when you rely on the flags. Start from a preset and toggle:

let p = Permissions::all().print(true).copy(false).modify(false); // view + print only
Preset / toggleEffect
Permissions::all()All permissions granted (the default — unrestricted)
Permissions::none()View only with the user password
.print(bool)Print (low resolution unless print_high_quality)
.modify(bool)Modify contents
.copy(bool)Copy / extract text and graphics
.annotate(bool)Add / modify annotations and fill forms
.fill_forms(bool)Fill form fields (even if annotate is off)
.accessibility(bool)Extract text/graphics for accessibility
.assemble(bool)Insert / rotate / delete pages
.print_high_quality(bool)Print at high resolution

Permissions need an owner password

Restrictions set under the user password are trivially lifted by the owner password — and when no owner password is given it defaults to the user password. Set OutputEncryption::password(u).owner_password(o).permissions(…) so the restrictions can't be bypassed by the same password that opens the file.

Works with every method — and with certification

Encryption works with every Method, including MultiChained: each chained revision is encrypted with the same file key, so the whole multi-signature document stays protected. Combining certification with encryption keeps the signature DocMDP-valid — a certified, encrypted output is both.

Protected → protected round-trip

The common real-world case is a document that arrives protected and must leave protected. Pass the open password on the input and .keep_password() on the config:

let cfg = SignConfig::builder()
    .place("F", [350, 60, 560, 160])?
    .keep_password()                     // same password in, same password out
    .build();

let signed = sign_pdf(PdfReader::open("locked.pdf")?.password("asd"), &signer, &cfg)?;

Re-signing an already-signed and encrypted document works the same way — the appended revision is encrypted with the document's own key, so the output stays protected with the same password and cipher (any cipher, including AES-256):

let resigned = sign_pdf(PdfReader::from(signed_protected).password("asd"), &signer, &cfg)?;

Output encryption cannot be combined with PDF/A — the standard forbids encryption, so .pdfa(…) and .encrypt(…) together are rejected.

Next

On this page