Adding Digital Signatures to PDF Documents via C#

Published: (December 23, 2025 at 10:14 PM EST)
6 min read
Source: Dev.to

Source: Dev.to

Digital signatures are indispensable for up‑to‑date document compliance and data security.
For enterprise‑level .NET development, implementing PDF digital‑signature functionality using C# is a high‑priority requirement for scenarios such as contract management, official document issuance, and confidential file distribution.

Free Spire.PDF for .NET is a lightweight, free PDF‑processing library that enables seamless PDF digital‑signature integration—no need for third‑party software such as Adobe Acrobat.

1️⃣ Installation

Install-Package FreeSpire.PDF

The NuGet command automatically resolves dependencies and integrates the library into your .NET project.

2️⃣ Certificate Requirements

EnvironmentHow to obtain a .pfx certificate
TestingGenerate a self‑signed certificate with OpenSSL or the Windows Certificate Manager (suitable for functional testing only).
ProductionUse a certificate issued by a trusted Certificate Authority (CA) that complies with local regulations.

The certificate must contain both public and private keys.

Loading the Certificate

The core class is System.Security.Cryptography.X509Certificates.X509Certificate2.

3️⃣ Signature Workflow

PdfOrdinarySignatureMaker (Free Spire.PDF) handles standard digital signatures.
The linear workflow is:

  1. Load PDF Document
  2. Parse X.509 Certificate
  3. Initialize Signature Maker
  4. Configure Signature Settings
  5. Execute Signature
  6. Save & Release Resources

We will first implement a basic invisible signature, then extend it to a customizable visible signature with text and image elements.

4️⃣ Basic Invisible Signature (Production‑grade)

using Spire.Pdf;
using Spire.Pdf.Interactive.DigitalSignatures;
using System.Security.Cryptography.X509Certificates;
using System.IO;

namespace DigitallySignPdf
{
    class Program
    {
        static void Main(string[] args)
        {
            // Define file paths (use relative paths for better portability)
            string inputPdfPath = "Input.pdf";
            string outputPdfPath = "Signed_Invisible.pdf";
            string certPath = "certificate.pfx";
            string certPassword = "abc123";

            try
            {
                // Validate input files exist to avoid runtime exceptions
                if (!File.Exists(inputPdfPath) || !File.Exists(certPath))
                {
                    Console.WriteLine("Error: Input PDF or certificate file not found.");
                    return;
                }

                // 1. Initialize PdfDocument and load the target PDF
                using (PdfDocument doc = new PdfDocument())
                {
                    doc.LoadFromFile(inputPdfPath);

                    // 2. Load X.509 certificate with secure key storage flags
                    // Key flags explanation:
                    // - MachineKeySet: Suitable for server‑side deployment (shared key store)
                    // - EphemeralKeySet: Prevents key persistence, enhances security for temporary signing tasks
                    X509Certificate2 cert = new X509Certificate2(
                        certPath,
                        certPassword,
                        X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.EphemeralKeySet
                    );

                    // 3. Initialize signature maker and bind the PDF document
                    PdfOrdinarySignatureMaker signatureMaker = new PdfOrdinarySignatureMaker(doc, cert);

                    // 4. Execute signature with a unique identifier (for later verification)
                    signatureMaker.MakeSignature("Enterprise_Signature_001");

                    // 5. Save the signed PDF (using 'using' statement auto‑releases resources)
                    doc.SaveToFile(outputPdfPath);
                    Console.WriteLine($"Success! Invisible signature added. Output file: {outputPdfPath}");
                }
            }
            catch (CryptographicException ex)
            {
                Console.WriteLine($"Certificate error: {ex.Message} (Check password or certificate validity)");
            }
            catch (IOException ex)
            {
                Console.WriteLine($"File operation error: {ex.Message} (Ensure the PDF is not open in another program)");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Signature failed: {ex.Message}");
            }
        }
    }
}

Note: Invisible signatures lack visual confirmation for end‑users.

5️⃣ Advanced Visible Signature (Customizable)

The example below adds a visible signature field with customizable text, images, and layout—ideal for contracts and official documents.

using Spire.Pdf;
using Spire.Pdf.Interactive.DigitalSignatures;
using System;
using System.Drawing;
using System.Security.Cryptography.X509Certificates;
using System.IO;

namespace PdfDigitalSignatureDemo
{
    class AdvancedVisibleSignatureDemo
    {
        static void Main(string[] args)
        {
            string inputPdf = "Input.pdf";
            string outputPdf = "Signed_Visible.pdf";
            string certPath = "certificate.pfx";
            string certPwd = "your_secure_password";
            string signatureImagePath = "signature_stamp.png";

            try
            {
                using (PdfDocument doc = new PdfDocument())
                {
                    doc.LoadFromFile(inputPdf);
                    X509Certificate2
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }
        }
    }
}

The code snippet is intentionally left incomplete to match the original content.

6️⃣ Recap & Tips

TopicTips
Certificate handlingPrefer MachineKeySet + EphemeralKeySet for server environments; never hard‑code passwords in production.
Error handlingCatch CryptographicException, IOException, and generic Exception to surface clear diagnostics.
File pathsUse relative paths or configuration settings to improve portability across environments.
Visible signaturesAdjust the signature rectangle, add text (PdfSignatureAppearance), and embed an image for a professional look.
TestingVerify signatures with Adobe Acrobat Reader or any PDF viewer that supports digital signatures.

7️⃣ Next Steps

  1. Complete the visible‑signature demo – add rectangle positioning, appearance settings, and image loading.
  2. Integrate with your CI/CD pipeline – automate signing of generated PDFs before distribution.
  3. Implement verification logic – use PdfSignatureValidator (or a third‑party validator) to confirm signature integrity on the receiving side.

Happy signing! 🚀

Code Example – Adding a Visible PDF Signature

try
{
    // Load the certificate (use secure storage flags)
    var cert = new X509Certificate2(
        certPath,
        certPwd,
        X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.EphemeralKeySet);

    // Initialise the signer and obtain the signature object
    PdfOrdinarySignatureMaker signer = new PdfOrdinarySignatureMaker(doc, cert);
    PdfSignature signature = signer.Signature;

    // Configure signature metadata (visible in PDF signature properties)
    signature.Name        = "ACME Corporation Document Signing";
    signature.ContactInfo = "compliance@acme.com";
    signature.Location    = "USA";
    signature.Reason      = "Confirm document content compliance and authenticity";
    signature.Date        = DateTime.Now;   // Auto‑set signature timestamp

    // Create and customise the signature appearance
    PdfSignatureAppearance appearance = new PdfSignatureAppearance(signature)
    {
        NameLabel        = "Signer: ",
        ContactInfoLabel = "Contact: ",
        LocationLabel    = "Location: ",
        ReasonLabel      = "Reason: "
    };

    // Add a signature image (PNG/JPG). Resize if needed.
    if (File.Exists(signatureImagePath))
    {
        PdfImage signatureImage = PdfImage.FromFile(signatureImagePath);
        appearance.SignatureImage = signatureImage;
        // Layout mode: Image + Text details
        // Alternatives: SignImageOnly / SignDetailOnly
        appearance.GraphicMode = GraphicMode.SignImageAndSignDetail;
    }
    else
    {
        Console.WriteLine("Signature image not found; using text‑only appearance.");
        appearance.GraphicMode = GraphicMode.SignDetailOnly;
    }

    // Attach the appearance to the signer
    signer.SignatureAppearance = appearance;

    // Choose the target page (e.g., the last page of the PDF)
    PdfPageBase targetPage = doc.Pages[doc.Pages.Count - 1];

    // Set signature position (x, y, width, height) – adjust for your layout
    const float x      = 54f;   // Left margin (72 pt = 1 in)
    const float y      = 330f;  // Bottom margin
    const float width  = 280f;
    const float height = 90f;

    // Execute the visible signature
    signer.MakeSignature(
        "ACME_Visible_Signature_001",
        targetPage,
        x, y, width, height,
        appearance);

    // Save the signed PDF
    doc.SaveToFile(outputPdf);
    Console.WriteLine($"Visible signature added successfully! Output: {outputPdf}");
}
catch (Exception ex)
{
    Console.WriteLine($"Advanced signature failed: {ex.Message}");
}

Glossary of Core Components & Practical Tips

Class / ParameterCore FunctionUsage Notes
PdfOrdinarySignatureMakerCore signature engine; binds PDF and certificate, then executes signingUse using statements or call Dispose() to release resources promptly.
X509Certificate2Parses .pfx certificates; extracts public/private key pairsAlways supply secure key‑storage flags; avoid DefaultKeySet on servers.
X509KeyStorageFlagsControls where the certificate is stored and the key’s lifecycleMachineKeySet – for server‑side deployment
EphemeralKeySet – temporary, in‑memory keys
PdfSignatureAppearanceConfigures the visible signature style (labels, image, layout)Adjust GraphicMode to switch between image‑only, text‑only, or combined layouts.
GraphicModeDefines the visible signature layoutOptions: SignImageOnly, SignDetailOnly, SignImageAndSignDetail
MakeSignature()Triggers the signing processPass a unique signature name to aid post‑signing verification.

Security Best Practices

  • Never hard‑code passwords. Store certificate passwords in a secure vault (Azure Key Vault, AWS Secrets Manager) or an encrypted configuration file.
  • Restrict certificate access. Grant only the minimal file‑system permissions required for the .pfx file.
  • App‑pool identity. In IIS, give the application‑pool identity (e.g., IIS AppPool\YourApp) read access to the certificate store.
  • Avoid UserKeySet on servers. It leads to permission issues in non‑interactive environments.

Common Error Scenarios & Remedies

Error TypeRoot CauseSolution
CryptographicExceptionInvalid certificate password or insufficient permissionsVerify the password, double‑check X509KeyStorageFlags, and ensure the app pool can read the certificate.
IOExceptionPDF file is locked by another processClose any readers/editors that have the PDF open before signing.
Invisible Signature Not DetectedSignature name conflict or incomplete PDF savingUse a unique signature name each time and confirm SaveToFile() is called before disposing.

Validation Checklist

  • Desktop readers: Adobe Acrobat DC, Foxit Reader
  • Web browsers: Chrome PDF Viewer, Microsoft Edge PDF Reader
  • Mobile: Adobe Acrobat Mobile, WPS Mobile

Test the signed PDFs in the above environments to ensure the signature is recognized and displayed correctly.

Why Choose Spire.PDF for .NET?

  • Lightweight & easy‑to‑integrate API – no heavyweight third‑party dependencies.
  • Full support for invisible and visible signatures via PdfOrdinarySignatureMaker.
  • Comprehensive documentation & examples that address common enterprise pain points (security, permissions, deployment).

Whether you’re building a document‑management system, a contract‑signing platform, or a confidential file‑distribution tool, this approach delivers a cost‑effective, high‑performance, production‑ready PDF digital‑signature solution.

Back to Blog

Related posts

Read more »

Domina el uso de paquetes NuGet en .NET

¿Qué es realmente NuGet? Imagina que NuGet es el Amazon o Mercado Libre de .NET. Tú no fabricas cada tornillo de tu mueble; los pides a la tienda. - El Package...

Convert Excel to PDF in C# Applications

Overview Transforming Excel files into polished, share‑ready PDFs doesn’t have to be a slow or complicated process. With the GroupDocs.Conversion Cloud SDK for...