Deferred Signing
prepare_signature, PreparedSignature, and SigningCertificate — split PDF preparation from an external hash-then-sign key operation
Deferred (two-step, "hash-then-sign") signing splits sign_pdf into a
prepare step (builds the PDF, no private key) and a finalize step (embeds an
externally-produced signature). See the Deferred Signing guide for
the full walkthrough and the v1 scope table.
prepare_signature
pub fn prepare_signature(
input: impl Into<PdfReader>,
identity: &SigningCertificate,
cfg: &SignConfig,
) -> Result<PreparedSignature>Builds the PDF — placeholders, the AcroForm field, and any DocMDP / DSS — and returns a
PreparedSignature exposing the bytes to sign. No private key is used here.
cfg is the same SignConfig as in-process signing, minus the key;
out-of-scope configurations (encrypted input/output, Method::MultiChained, PAdES B-LT/B-LTA,
already-signed input) return Error::InvalidInput.
use mudrit_pdfsign::{prepare_signature, SigningCertificate, SignConfig};
# fn my_remote_signer(_: &[u8]) -> mudrit_pdfsign::Result<Vec<u8>> { unimplemented!() }
# let pdf: Vec<u8> = Vec::new();
# let leaf_cert_der: Vec<u8> = Vec::new();
# let chain_ders: Vec<Vec<u8>> = Vec::new();
// STEP 1 (server): cert + chain are known up front; the key is remote.
let identity = SigningCertificate::new(leaf_cert_der).chain(chain_ders);
let prepared = prepare_signature(&pdf, &identity, &SignConfig::builder().build())?;
let to_sign = prepared.signed_attributes().to_vec(); // or prepared.digest()
// STEP 2 (cloud HSM / browser / mobile): produce the signature out-of-process.
let signature = my_remote_signer(&to_sign)?; // RSA PKCS#1 / PSS, or ECDSA DER {r,s}
// STEP 3 (server): embed it.
let signed_pdf = prepared.finalize(&signature)?;
# Ok::<(), mudrit_pdfsign::Error>(())SigningCertificate
Everything prepare_signature needs except the private key: the leaf certificate, its chain, and
the scheme the remote key signs with. Clone + Debug.
Prop
Type
Constructors and methods
Prop
Type
The algorithm must match the remote key
algorithm selects the digest and how the SignedData is labelled. If the remote key produces
ECDSA P-256 but the identity says RSA, the finalized signature is mislabelled and rejected by
validators.
PreparedSignature
A PDF prepared for signing, awaiting an externally-produced signature. Returned by
prepare_signature; Clone, with a Debug that hides the PDF bytes and signed attributes.
Prop
Type
What the external signer must do
Apply the same operation a local Signer would to
signed_attributes(): hash with algorithm()'s digest and produce the signature — a raw RSA PKCS#1
v1.5 / PSS signature, or an ECDSA SEQUENCE { r, s } DER. If the API signs a pre-computed hash
instead, feed it digest() — that is exactly algorithm.digest(signed_attributes()).
// request A — prepare, then stash the blob (DB / cache / queue)
let blob = prepared.to_bytes()?;
// request B — restore, embed the externally-produced signature
let prepared = PreparedSignature::from_bytes(&blob)?;
let signed = prepared.finalize(&signature)?;Timestamps are not serializable
to_bytes errors if a timestamp was configured. For a timestamped (B-T) deferred flow, either
finalize in the same process, or prepare_signature with Timestamp::None and pass the TSA to
finalize_with_timestamp after from_bytes.
v1 scope
Fresh (unsigned), unencrypted input; Method::Single / Method::MultiShared; optional
certification, LTV, and PAdES B-B / B-T. Encrypted output, Method::MultiChained, appending to an
already-signed PDF, and PAdES B-LT / B-LTA are rejected with Error::InvalidInput — use the
in-process sign_pdf for those.