๐๏ธ Digital Signatures
Digitally sign PDFs with X.509 certificates using PKCS#7/CMS detached signatures (ISO 32000 ยง12.8).
using System.Security.Cryptography.X509Certificates;
using ObviousPDF;
// Load a certificate with private key (.pfx / .p12)
var cert = new X509Certificate2("certificate.pfx", "password",
X509KeyStorageFlags.Exportable);
var sig = new PdfDigitalSignature(cert)
{
Reason = "I approve this document",
Location = "New York, NY",
ContactInfo = "alice@example.com",
SignerName = "Alice Signer",
PageIndex = 0, // Sign the first page
X = 72, Y = 50, // Signature field position
Width = 250, Height = 60
};
var doc = new PdfDocument();
doc.Info.Title = "Digitally Signed Document";
var page = doc.AddPage();
page.AddText("This document is digitally signed.", 72, 720);
// Sign() writes the PDF with a PKCS#7 detached signature
doc.Sign("signed_document.pdf", sig);
Imports System.Security.Cryptography.X509Certificates
Imports ObviousPDF
' Load a certificate with private key (.pfx / .p12)
Dim cert As New X509Certificate2("certificate.pfx", "password",
X509KeyStorageFlags.Exportable)
Dim sig As New PdfDigitalSignature(cert) With {
.Reason = "I approve this document",
.Location = "New York, NY",
.ContactInfo = "alice@example.com",
.SignerName = "Alice Signer",
.PageIndex = 0,
.X = 72, .Y = 50,
.Width = 250, .Height = 60
}
Dim doc As New PdfDocument()
doc.Info.Title = "Digitally Signed Document"
Dim page = doc.AddPage()
page.AddText("This document is digitally signed.", 72, 720)
doc.Sign("signed_document.pdf", sig)
open System.Security.Cryptography.X509Certificates
open ObviousPDF
// Load a certificate with private key (.pfx / .p12)
let cert = new X509Certificate2("certificate.pfx", "password",
X509KeyStorageFlags.Exportable)
let sig = PdfDigitalSignature(cert,
Reason = "I approve this document",
Location = "New York, NY",
ContactInfo = "alice@example.com",
SignerName = "Alice Signer",
PageIndex = 0,
X = 72.0, Y = 50.0,
Width = 250.0, Height = 60.0)
let doc = PdfDocument()
doc.Info.Title <- "Digitally Signed Document"
let page = doc.AddPage()
page.AddText("This document is digitally signed.", 72.0, 720.0)
doc.Sign("signed_document.pdf", sig)
Add-Type -Path "ObviousPDF.dll"
Add-Type -AssemblyName "System.Security.Cryptography.X509Certificates"
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new(
"certificate.pfx", "password",
[System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
$sig = [ObviousPDF.PdfDigitalSignature]::new($cert)
$sig.Reason = "I approve this document"
$sig.Location = "New York, NY"
$sig.ContactInfo = "alice@example.com"
$sig.SignerName = "Alice Signer"
$sig.X = 72; $sig.Y = 50
$sig.Width = 250; $sig.Height = 60
$doc = [ObviousPDF.PdfDocument]::new()
$doc.Info.Title = "Digitally Signed Document"
$page = $doc.AddPage()
$page.AddText("This document is digitally signed.", 72, 720)
$doc.Sign("c:\temp\signed_document.pdf", $sig)
What you should see: When opened in Adobe Acrobat Reader, the document displays a "Signed and all signatures are valid" or "At least one signature has problems" banner at the top with a Signatures panel on the right. The banner message depends on whether the signing certificate is trusted (see Certificate Setup below).
Certificate Setup Guide
ObviousPDF produces standard PKCS#7 detached signatures (/SubFilter /adbe.pkcs7.detached).
Adobe Acrobat Reader will always detect the signature, but its validity
depends on whether the signing certificate is trusted by the viewer.
Certificate Trust Levels
| Certificate Type | Adobe Reader Status | Use Case |
|---|---|---|
| Self-signed (test/development) | โ ๏ธ "Signature validity is unknown" | Internal testing, development |
| Organisation CA | โ ๏ธ "Signer’s identity is unknown" (unless CA is installed) | Internal documents within an organisation |
| Trusted CA (e.g. GlobalSign, DigiCert, Sectigo) | โ "Signed and all signatures are valid" | Public-facing documents, contracts, invoices |
| AATL Certificate (Adobe Approved Trust List) | โ Fully trusted with blue ribbon icon | Legal documents, government, regulated industries |
Option 1: Self-Signed Certificate (Development & Testing)
Create a self-signed certificate using .NET or openssl. The signature will be detected by Adobe Reader but shown as "validity unknown" until you manually trust it.
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using var rsa = RSA.Create(2048);
var req = new CertificateRequest(
"CN=My Signer, O=My Company",
rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
req.CertificateExtensions.Add(
new X509KeyUsageExtension(
X509KeyUsageFlags.DigitalSignature, critical: true));
var cert = req.CreateSelfSigned(
DateTimeOffset.UtcNow.AddDays(-1),
DateTimeOffset.UtcNow.AddYears(3));
// Export as .pfx for later use
byte[] pfxBytes = cert.Export(X509ContentType.Pfx, "mypassword");
File.WriteAllBytes("my_signing_cert.pfx", pfxBytes);
# Generate private key and certificate in one step
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem \
-days 1095 -nodes -subj "/CN=My Signer/O=My Company"
# Convert to .pfx (PKCS#12) for use with .NET
openssl pkcs12 -export -out my_signing_cert.pfx \
-inkey key.pem -in cert.pem -passout pass:mypassword
Option 2: Trusting a Self-Signed Certificate in Adobe Reader
To make Adobe Reader trust your self-signed certificate:
- Open the signed PDF in Adobe Acrobat Reader.
- Click the "Signature Panel" button in the warning banner.
- Expand the signature and click "Certificate Details".
- In the Certificate Viewer, go to the "Trust" tab.
- Click "Add to Trusted Certificates..." and confirm.
- Check "Use this certificate as a trusted root".
- Click OK and re-validate โ the signature should now show as valid.
Option 3: Purchasing a Trusted Certificate (Production)
For production use, purchase a document signing certificate from a Certificate Authority (CA) trusted by Adobe. Certificates on the Adobe Approved Trust List (AATL) are automatically trusted by all Adobe Reader installations worldwide.
Popular providers of document signing certificates:
| Provider | Notes |
|---|---|
| GlobalSign | AATL member, individual and organisation certificates |
| DigiCert | AATL member, hardware token options |
| Sectigo (Comodo) | AATL member, affordable options |
| Entrust | AATL member, enterprise-focused |
Most vendors deliver the certificate as a .pfx / .p12 file or on a hardware
token (USB). Load it in your .NET code with:
// From a .pfx file
var cert = new X509Certificate2("purchased_cert.pfx", "password",
X509KeyStorageFlags.Exportable);
// From the Windows certificate store
using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates
.Find(X509FindType.FindBySubjectName, "My Signer", validOnly: true)
.OfType<X509Certificate2>()
.FirstOrDefault(c => c.HasPrivateKey)
?? throw new InvalidOperationException("Certificate not found");
Signature Properties
| Property | Type | Default | Description |
|---|---|---|---|
Certificate | X509Certificate2 | (required) | Signing certificate โ must include a private key. |
Reason | string? | null | Reason for signing (e.g. "I approve this document"). |
Location | string? | null | Physical or logical location of signing. |
ContactInfo | string? | null | Contact information for the signer. |
SignerName | string? | null | Signer name. Defaults to the certificate’s CN. |
PageIndex | int | 0 | Zero-based page index for the signature field. |
X, Y | double | 0 | Bottom-left corner of the signature field (points). |
Width | double | 200 | Signature field width in points. |
Height | double | 50 | Signature field height in points. |
FieldName | string | "Signature1" | Unique form field name for the signature. |
Technical Details
| Aspect | Value |
|---|---|
| Standard | ISO 32000-2 ยง12.8 (PDF 2.0) |
| Filter | /Adobe.PPKLite |
| SubFilter | /adbe.pkcs7.detached |
| Signature Format | PKCS#7 / CMS (RFC 5652), detached |
| Digest Algorithm | SHA-256 |
| Byte Range | Covers the entire file except the /Contents hex value |
| Certificate Inclusion | Whole chain (X509IncludeOption.WholeChain) |
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| "Signature validity is unknown" | Certificate is not in Adobe’s trust store | Add the certificate to Trusted Certificates in Adobe Reader (see above), or use a CA-issued certificate. |
| "Document has been altered" | File was modified after signing | Do not edit or re-save the PDF after calling Sign(). |
ArgumentException: "Certificate must contain a private key" |
Certificate loaded without private key | Load from a .pfx/.p12 file (not .cer/.crt). Include X509KeyStorageFlags.Exportable. |
| Signature not visible on the page | Signature field is outside the visible page area | Adjust X, Y, Width, Height to fit within the page’s MediaBox (default: 612 ร 792 points). |