Table of Contents

๐Ÿ‘‰ Try the demo: https://github.com/LM-Kit/lm-kit-net-samples/tree/main/console_net/document-intelligence/digital-signatures/pdf_digital_signatures

PDF Digital Signatures for C# .NET Applications


๐ŸŽฏ Purpose of the Demo

Show LM-Kit.NET's digital-signature engine end to end, fully offline: sign a PDF (PAdES detached CAdES), verify it on independent axes (integrity, identity, revocation, timestamp), countersign it without breaking the first signature, and watch a single tampered byte turn the verdict INVALID. Signing appends an incremental revision by construction, so LM-Kit never rewrites the document it signs.

๐Ÿ‘ฅ Who Should Use This Demo (Target Audience)

  • .NET developers adding legally meaningful signatures to contracts, invoices, approvals, and reports.
  • Teams replacing cloud e-signature round-trips with local, private signing.
  • Security engineers who need verifiable audit answers: what exactly is signed, by whom, at what proven time.

๐Ÿš€ What Problem It Solves

Most PDF tooling treats a signature as a boolean. Real questions are separate facts: do the bytes still match the signature, does the signer chain to an anchor you chose, is the certificate revoked, and is the time claimed or proven? The demo prints each axis for every signature, so the difference between "math checks out" and "signer is trusted" stays visible.

๐Ÿ’ป Demo Application Overview

The console app generates a sample contract from Markdown (or takes your PDF as an argument), creates a self-signed demo certificate, signs, validates, countersigns, validates again (signature 1 covers its revision, signature 2 covers the whole file, both valid), then flips one byte of signed content and validates a third time.

โœจ Key Features

  • PdfSigner.Sign: one call, any X509Certificate2 private key the BCL reaches (PFX, OS store, hardware-backed).
  • PdfSigner.BeginSign: a two-phase session (byte ranges, SHA-256 digest, Complete) for HSMs and signing services.
  • PdfSignatureValidator.Validate: verdicts on four axes plus an Adobe-compatible Valid / Indeterminate / Invalid status.
  • --tsa <url>: PAdES B-T, the signing time proven by an RFC 3161 authority.
  • PdfSigner.AddDocumentTimestamp and PdfSigner.ExtendLtv for document timestamps and offline long-term validation.

Example Output

== After countersigning ==
Overall: VALID

Signature #1 (ETSI.CAdES.detached)
  Status     : VALID
  Integrity  : Valid
  Identity   : Trusted (CN=LM-Kit Demo Signer)
  Covers all : False
  Detail     : ... The document was extended after this signature; only the signed revision is covered.

Signature #2 (ETSI.CAdES.detached)
  Status     : VALID
  Covers all : True

== After tampering with one byte ==
Overall: INVALID
  Integrity  : DocumentModified

๐Ÿ—๏ธ Architecture

 Markdown โ”€โ”€> sample PDF โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                                       v
 X509Certificate2 โ”€โ”€> PdfSigner.Sign โ”€โ”€> incremental revision
        (any BCL key)      โ”‚             (original bytes verbatim)
                           v
              PdfSignatureValidator โ”€โ”€> integrity | identity | revocation | timestamp
                           โ”‚
                           v
              countersign / tamper โ”€โ”€> re-validate

โš™๏ธ Getting Started

Prerequisites

  • .NET 8.0 SDK or newer. No model download and no GPU: this demo exercises the document engine only.

Download and Run

git clone https://github.com/LM-Kit/lm-kit-net-samples.git
cd lm-kit-net-samples/console_net/document-intelligence/digital-signatures/pdf_digital_signatures
dotnet run

Pass your own document and authority: dotnet run -- path/to/your.pdf --tsa http://timestamp.digicert.com

๐Ÿ”ง Troubleshooting

  • Identity shows Untrusted: trust is explicit. Add the signer's root to PdfSignatureValidationOptions.TrustedRoots, or enable TrustSystemRoots for the OS store.
  • Signing a signed document is refused elsewhere: full rewrites of signed documents require the operation's AllowSignatureInvalidation opt-in; signing itself is incremental and needs no consent.
  • The container does not fit: raise PdfSigningOptions.SignatureCapacity when embedding long chains.

๐Ÿš€ Extend the Demo

  • Certify instead of approve: set PdfSigningOptions.Certification to a DocMDP level.
  • Place a visible field with PdfSigningOptions.Bounds: it renders who signed, when, and why, localized through AppearanceCulture (fourteen languages built in). Appearance overrides the lines, size, and color, and Appearance.FontFile embeds a font for scripts beyond WinAnsi (Cyrillic, CJK, Arabic).
  • Sign through an HSM: BeginSign, ship GetDigest() out, Complete(cms).
  • Make validation self-contained for the archive: PdfSigner.ExtendLtv, then validate offline.

๐Ÿ“š Additional Resources

Share