How to Read the Signer's Certificate from Verified PDF Signatures in C# (X.509, eIDAS, Chain)

A contract lands in your audit pipeline. It is digitally signed, the signature is valid, and your compliance team needs answers: who signed it, was their certificate valid that morning, and does the issuing CA match your trusted list? You could pull the raw signature bytes, parse the DER, and walk the certificate chain by hand. Or you could ask the VerifiedSignature object for its SignerCertificate and read the answers off as properties.

That is what the SignerCertificate and CertificateChain properties on VerifiedSignature are built for. Returned from PdfDocument.GetVerifiedSignatures(), they expose the signer's full X.509 identity, the issuing CA, validity dates, the SHA-256 thumbprint, the raw DER bytes, and every intermediate certificate up to the root. That is the API surface you need for eIDAS compliance, certificate pinning, audit trails, and chain-of-trust verification.

This guide walks through three families of certificate inspection:

  • Standard X.509 details: identity properties, full Distinguished Names, validity windows, and raw DER bytes for X509Certificate2 interop.
  • eIDAS and regulated-industry fields: pulling the signer's national ID from SubjectSerialNumber and reading numeric OIDs like 2.5.4.97 for the eIDAS organizationIdentifier.
  • Trust-chain validation: SHA-256 thumbprint pinning and walking the full chain from signer to root CA.

Start a free 30-day trial to test certificate inspection in your verification pipeline.

NuGet Install with NuGet

PM >  Install-Package IronPdf

Check out IronPDF on NuGet for quick installation. With over 10 million downloads, it’s transforming PDF development with C#. You can also download the DLL or Windows installer.

Quickstart: Read the Signer's Certificate from a Signed PDF

Call GetVerifiedSignatures on a loaded PDF and read the signer's certificate properties directly off the result.

  1. Install IronPDF with NuGet Package Manager

    PM > Install-Package IronPdf
  2. Copy and run this code snippet.

    var sig = PdfDocument.FromFile("signed.pdf").GetVerifiedSignatures().First();
    var cert = sig.SignerCertificate;
    Console.WriteLine($"{cert?.CommonName} ({cert?.Organization}) valid until {cert?.NotAfter}");
  3. Deploy to test on your live environment

    Start using IronPDF in your project today with a free trial

    arrow pointer


How Do You Extract the Signer's National or Personal ID?

The SubjectSerialNumber property returns the value of the SERIALNUMBER OID from the certificate's Subject DN. In regulated industries (eIDAS qualified certificates, banking, government-issued digital IDs), this field carries the signer's national ID, tax ID, or personal identification number. It is not the certificate's own serial number; that is CertificateSerialNumber.

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-serial-number.cs
using IronPdf;
using IronPdf.Signing.Inspection;

var pdf = PdfDocument.FromFile("eidas-signed-invoice.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    // Subject DN SERIALNUMBER OID (national ID, tax ID, etc.)
    Console.WriteLine($"Signer Identity (SERIALNUMBER): {cert.SubjectSerialNumber ?? "not present"}");

    // Certificate's own serial from the issuing CA
    Console.WriteLine($"Certificate Serial (CA-issued):  {cert.CertificateSerialNumber}");
}
Imports IronPdf
Imports IronPdf.Signing.Inspection

Dim pdf = PdfDocument.FromFile("eidas-signed-invoice.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    ' Subject DN SERIALNUMBER OID (national ID, tax ID, etc.)
    Console.WriteLine($"Signer Identity (SERIALNUMBER): {If(cert.SubjectSerialNumber, "not present")}")

    ' Certificate's own serial from the issuing CA
    Console.WriteLine($"Certificate Serial (CA-issued):  {cert.CertificateSerialNumber}")
Next
$vbLabelText   $csharpLabel

TipsQuick reminder on the difference between these two: SubjectSerialNumber tells you who signed the document (a person or organization ID, like a national ID or tax number embedded in the Subject DN). CertificateSerialNumber tells you which certificate was used; it is the unique serial the issuing Certificate Authority assigns for revocation lists and audit logs.

How Do You Access Convenience Identity Properties?

SignerCertificateInfo exposes the most commonly needed Subject DN fields as direct properties, so you do not need to parse the full DN string yourself.

Reading the Signer's Name and Affiliation

The CommonName, Organization, Country, and Email properties cover what most identity workflows reach for first.

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-identity-common.cs
using IronPdf;
using IronPdf.Signing.Inspection;

var pdf = PdfDocument.FromFile("signed-agreement.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    // Common Subject DN fields exposed directly as properties
    Console.WriteLine($"CommonName (CN):  {cert.CommonName}");
    Console.WriteLine($"Organization (O): {cert.Organization}");
    Console.WriteLine($"Country (C):      {cert.Country}");
    Console.WriteLine($"Email:            {cert.Email}");
}
Imports IronPdf
Imports IronPdf.Signing.Inspection

Dim pdf = PdfDocument.FromFile("signed-agreement.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    ' Common Subject DN fields exposed directly as properties
    Console.WriteLine($"CommonName (CN):  {cert.CommonName}")
    Console.WriteLine($"Organization (O): {cert.Organization}")
    Console.WriteLine($"Country (C):      {cert.Country}")
    Console.WriteLine($"Email:            {cert.Email}")
Next
$vbLabelText   $csharpLabel

The Email property first checks the Subject DN for the emailAddress OID. If not present, it falls back to the Subject Alternative Name (SAN) rfc822Name extension. The Country property returns the two-letter ISO 3166-1 alpha-2 code (for example, "DE", "US", "FR"). All convenience properties return null if the corresponding field is not present in the certificate.

Reading the Full DN When You Need Everything

For audit logging, downstream parsing, or compliance reports, SubjectDN and IssuerDN return the complete Distinguished Name strings.

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-identity-full-dn.cs
using IronPdf;
using IronPdf.Signing.Inspection;

var pdf = PdfDocument.FromFile("signed-agreement.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    // Full DN strings for audit logging or downstream parsing
    Console.WriteLine($"Full Subject DN: {cert.SubjectDN}");
    Console.WriteLine($"Full Issuer DN:  {cert.IssuerDN}");
}
Imports IronPdf
Imports IronPdf.Signing.Inspection

Dim pdf = PdfDocument.FromFile("signed-agreement.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    ' Full DN strings for audit logging or downstream parsing
    Console.WriteLine($"Full Subject DN: {cert.SubjectDN}")
    Console.WriteLine($"Full Issuer DN:  {cert.IssuerDN}")
Next
$vbLabelText   $csharpLabel

How Do You Extract Arbitrary Subject and Issuer Fields?

The GetSubjectField(string) and GetIssuerField(string) methods accept either short names or numeric OIDs. They are case-insensitive and return null if the field is not present.

Supported short names: CN, O, OU, C, L, ST, E, SERIALNUMBER, SN/SURNAME, T, GIVENNAME, UID, ORGANIZATIONIDENTIFIER.

Reading Any Subject Field (Including eIDAS OIDs)

Pass a short name like "OU" or a numeric OID like "2.5.4.97" (the eIDAS organizationIdentifier) to pull any field from the Subject DN.

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-field-subject.cs
using IronPdf;
using IronPdf.Signing.Inspection;

var pdf = PdfDocument.FromFile("eidas-contract.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    // Short-name lookups (case-insensitive)
    string? orgUnit = cert.GetSubjectField("OU");
    string? surname = cert.GetSubjectField("SURNAME");
    string? givenName = cert.GetSubjectField("GIVENNAME");
    string? title = cert.GetSubjectField("T");

    // Numeric OID lookup (eIDAS organizationIdentifier)
    string? orgId = cert.GetSubjectField("2.5.4.97");

    Console.WriteLine($"Name: {givenName} {surname} ({title}), OU: {orgUnit}");
    Console.WriteLine($"eIDAS Org ID (2.5.4.97): {orgId ?? "not present"}");
}
Imports IronPdf
Imports IronPdf.Signing.Inspection

Dim pdf = PdfDocument.FromFile("eidas-contract.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    ' Short-name lookups (case-insensitive)
    Dim orgUnit As String = cert.GetSubjectField("OU")
    Dim surname As String = cert.GetSubjectField("SURNAME")
    Dim givenName As String = cert.GetSubjectField("GIVENNAME")
    Dim title As String = cert.GetSubjectField("T")

    ' Numeric OID lookup (eIDAS organizationIdentifier)
    Dim orgId As String = cert.GetSubjectField("2.5.4.97")

    Console.WriteLine($"Name: {givenName} {surname} ({title}), OU: {orgUnit}")
    Console.WriteLine($"eIDAS Org ID (2.5.4.97): {If(orgId, "not present")}")
Next
$vbLabelText   $csharpLabel

The numeric OID lookup is essential for eIDAS and regulated-industry certificates that embed organization identifiers, professional qualifications, or jurisdiction-specific fields not covered by standard short names.

Reading the Issuer's Fields

GetIssuerField works the same way against the issuing CA's Distinguished Name. This is useful for trust validation, CA reporting, and grouping signatures by their issuing authority.

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-field-issuer.cs
using IronPdf;
using IronPdf.Signing.Inspection;

var pdf = PdfDocument.FromFile("eidas-contract.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    // GetIssuerField works the same way as GetSubjectField
    string? issuerCN = cert.GetIssuerField("CN");
    string? issuerO = cert.GetIssuerField("O");
    string? issuerCountry = cert.GetIssuerField("C");

    Console.WriteLine($"Issued by: {issuerCN} ({issuerO}, {issuerCountry})");
}
Imports IronPdf
Imports IronPdf.Signing.Inspection

Dim pdf = PdfDocument.FromFile("eidas-contract.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    ' GetIssuerField works the same way as GetSubjectField
    Dim issuerCN As String = cert.GetIssuerField("CN")
    Dim issuerO As String = cert.GetIssuerField("O")
    Dim issuerCountry As String = cert.GetIssuerField("C")

    Console.WriteLine($"Issued by: {issuerCN} ({issuerO}, {issuerCountry})")
Next
$vbLabelText   $csharpLabel

How Do You Check Certificate Validity?

SignerCertificateInfo provides two validity helpers:

  • IsExpired returns true if DateTime.UtcNow > NotAfter.
  • IsValidAt(DateTime) returns true if the given timestamp falls within the NotBefore/NotAfter window. The typical use is confirming the certificate was valid at the time of signing.
:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-validity.cs
using IronPdf;
using IronPdf.Signing.Inspection;

var pdf = PdfDocument.FromFile("archived-contract.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    Console.WriteLine($"Valid from:  {cert.NotBefore}");
    Console.WriteLine($"Valid until: {cert.NotAfter}");
    Console.WriteLine($"Expired now: {cert.IsExpired}");

    // Confirm certificate was valid when the document was signed
    if (sig.SigningDate.HasValue)
    {
        bool validAtSigning = cert.IsValidAt(sig.SigningDate.Value);
        Console.WriteLine($"Valid at signing time ({sig.SigningDate.Value}): {validAtSigning}");
    }
}
Imports IronPdf
Imports IronPdf.Signing.Inspection

Dim pdf = PdfDocument.FromFile("archived-contract.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    Console.WriteLine($"Valid from:  {cert.NotBefore}")
    Console.WriteLine($"Valid until: {cert.NotAfter}")
    Console.WriteLine($"Expired now: {cert.IsExpired}")

    ' Confirm certificate was valid when the document was signed
    If sig.SigningDate.HasValue Then
        Dim validAtSigning As Boolean = cert.IsValidAt(sig.SigningDate.Value)
        Console.WriteLine($"Valid at signing time ({sig.SigningDate.Value}): {validAtSigning}")
    End If
Next
$vbLabelText   $csharpLabel

TipsIsExpired returns false (not true) when validity dates cannot be extracted from the certificate. The helper is intentionally conservative; it does not assume expiration just because metadata is missing. For zero-trust workflows, treat missing validity dates as a failure case explicitly.

For audit trails, log both the IsExpired check (current validity) and the IsValidAt(signingDate) check (validity at signing time). A certificate that has since expired may still have been valid when the document was signed; both data points matter for compliance.

How Do You Use the SHA-256 Thumbprint for Certificate Pinning?

The Sha256Thumbprint property returns a 64-character uppercase hex string, the SHA-256 hash of the DER-encoded certificate. This is the value you pin against a known-good list of accepted signer certificates.

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-thumbprint-pinning.cs
using IronPdf;
using IronPdf.Signing.Inspection;
using System.Collections.Generic;

// Known-good signer thumbprints from a compliance database
var pinnedThumbprints = new HashSet<string>
{
    "A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2",
    "F6E5D4C3B2A1F6E5D4C3B2A1F6E5D4C3B2A1F6E5D4C3B2A1F6E5D4C3B2A1F6E5"
};

var pdf = PdfDocument.FromFile("submitted-form.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    // Pin against trusted thumbprint list
    bool trusted = pinnedThumbprints.Contains(cert.Sha256Thumbprint);
    Console.WriteLine($"Signer: {cert.CommonName}, Thumbprint: {cert.Sha256Thumbprint}");
    Console.WriteLine($"Pinned: {trusted}");
}
Imports IronPdf
Imports IronPdf.Signing.Inspection
Imports System.Collections.Generic

' Known-good signer thumbprints from a compliance database
Dim pinnedThumbprints As New HashSet(Of String) From {
    "A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2",
    "F6E5D4C3B2A1F6E5D4C3B2A1F6E5D4C3B2A1F6E5D4C3B2A1F6E5D4C3B2A1F6E5"
}

Dim pdf = PdfDocument.FromFile("submitted-form.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    ' Pin against trusted thumbprint list
    Dim trusted As Boolean = pinnedThumbprints.Contains(cert.Sha256Thumbprint)
    Console.WriteLine($"Signer: {cert.CommonName}, Thumbprint: {cert.Sha256Thumbprint}")
    Console.WriteLine($"Pinned: {trusted}")
Next
$vbLabelText   $csharpLabel

How Do You Construct an X509Certificate2 from Raw Certificate Data?

The RawData property returns the DER-encoded certificate as a byte[]. Each call returns a defensive copy, so the internal data is never exposed by reference. You can use this to construct a System.Security.Cryptography.X509Certificates.X509Certificate2 for interop with .NET's native cryptographic APIs (chain building, policy validation, CRL checking).

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-x509-interop.cs
using IronPdf;
using IronPdf.Signing.Inspection;
using System.Security.Cryptography.X509Certificates;

var pdf = PdfDocument.FromFile("signed-report.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    // Construct X509Certificate2 from defensive DER copy
    byte[] derBytes = cert.RawData;
    var x509 = new X509Certificate2(derBytes);

    Console.WriteLine($"X509 Subject:    {x509.Subject}");
    Console.WriteLine($"X509 Issuer:     {x509.Issuer}");
    Console.WriteLine($"X509 Serial:     {x509.SerialNumber}");
    Console.WriteLine($"X509 Algorithm:  {x509.SignatureAlgorithm.FriendlyName}");
    Console.WriteLine($"X509 Key Size:   {x509.PublicKey.Key.KeySize} bits");
}
Imports IronPdf
Imports IronPdf.Signing.Inspection
Imports System.Security.Cryptography.X509Certificates

Dim pdf = PdfDocument.FromFile("signed-report.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    ' Construct X509Certificate2 from defensive DER copy
    Dim derBytes As Byte() = cert.RawData
    Dim x509 = New X509Certificate2(derBytes)

    Console.WriteLine($"X509 Subject:    {x509.Subject}")
    Console.WriteLine($"X509 Issuer:     {x509.Issuer}")
    Console.WriteLine($"X509 Serial:     {x509.SerialNumber}")
    Console.WriteLine($"X509 Algorithm:  {x509.SignatureAlgorithm.FriendlyName}")
    Console.WriteLine($"X509 Key Size:   {x509.PublicKey.Key.KeySize} bits")
Next
$vbLabelText   $csharpLabel

Please noteEach access to RawData returns a fresh defensive copy. The internal byte array is never exposed by reference, so mutating the returned bytes will not affect later calls.

This interop path lets you plug IronPDF's signature verification into existing .NET certificate validation pipelines, such as X509Chain.Build(), revocation checking via CRL/OCSP, or custom X509CertificateValidator implementations.

How Do You Walk the Certificate Chain?

The CertificateChain property on VerifiedSignature returns an IReadOnlyList<SignerCertificateInfo> ordered from signer → intermediate CA(s) → root CA. Index 0 is always the signer certificate (the same object as SignerCertificate). The list is never null; if the chain cannot be extracted, it contains only the signer certificate.

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-chain-walking.cs
using IronPdf;
using IronPdf.Signing.Inspection;

var pdf = PdfDocument.FromFile("enterprise-signed.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    Console.WriteLine($"--- Signature: {sig.SignatureName} ---");
    Console.WriteLine($"Chain length: {sig.CertificateChain.Count}");

    // Walk from signer (index 0) through intermediates to root CA
    for (int i = 0; i < sig.CertificateChain.Count; i++)
    {
        var cert = sig.CertificateChain[i];
        string role = i == 0 ? "Signer"
                    : i == sig.CertificateChain.Count - 1 ? "Root CA"
                    : $"Intermediate CA ({i})";

        Console.WriteLine($"  [{role}]");
        Console.WriteLine($"    Subject:     {cert.SubjectDN}");
        Console.WriteLine($"    Issuer:      {cert.IssuerDN}");
        Console.WriteLine($"    Serial:      {cert.CertificateSerialNumber}");
        Console.WriteLine($"    Valid:       {cert.NotBefore} to {cert.NotAfter}");
        Console.WriteLine($"    Expired:     {cert.IsExpired}");
        Console.WriteLine($"    Thumbprint:  {cert.Sha256Thumbprint}");
    }
}
Imports IronPdf
Imports IronPdf.Signing.Inspection

Dim pdf = PdfDocument.FromFile("enterprise-signed.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Console.WriteLine($"--- Signature: {sig.SignatureName} ---")
    Console.WriteLine($"Chain length: {sig.CertificateChain.Count}")

    ' Walk from signer (index 0) through intermediates to root CA
    For i As Integer = 0 To sig.CertificateChain.Count - 1
        Dim cert = sig.CertificateChain(i)
        Dim role As String = If(i = 0, "Signer", If(i = sig.CertificateChain.Count - 1, "Root CA", $"Intermediate CA ({i})"))

        Console.WriteLine($"  [{role}]")
        Console.WriteLine($"    Subject:     {cert.SubjectDN}")
        Console.WriteLine($"    Issuer:      {cert.IssuerDN}")
        Console.WriteLine($"    Serial:      {cert.CertificateSerialNumber}")
        Console.WriteLine($"    Valid:       {cert.NotBefore} to {cert.NotAfter}")
        Console.WriteLine($"    Expired:     {cert.IsExpired}")
        Console.WriteLine($"    Thumbprint:  {cert.Sha256Thumbprint}")
    Next
Next
$vbLabelText   $csharpLabel

Please noteEach SignerCertificateInfo in the chain exposes the same properties as SignerCertificate: identity fields, validity, thumbprint, RawData, GetSubjectField(), and GetIssuerField(). You can validate every intermediate CA's expiration, check the root's thumbprint against your trusted root store, and log the full chain for compliance audits.

Next Steps

SignerCertificate and CertificateChain give you full X.509 inspection without leaving the IronPDF API surface. From here, the natural next moves are deeper into the parts of IronPDF these properties plug into.

For the signing side of the workflow, the digital signatures how-to covers certificate creation, metadata, and incremental signing. For HSM-based signing in high-security environments, the HSM signing guide covers PKCS#11 integration. Need ready-to-run snippets? The digital signatures code example provides them.

Ready to try it on your own signed PDFs? Start your free 30-day trial or view licensing options.

Curtis Chau
Technical Writer

Curtis Chau holds a Bachelor’s degree in Computer Science (Carleton University) and specializes in front-end development with expertise in Node.js, TypeScript, JavaScript, and React. Passionate about crafting intuitive and aesthetically pleasing user interfaces, Curtis enjoys working with modern frameworks and creating well-structured, visually appealing manuals.

...

Read More
Ready to Get Started?
Nuget Downloads 18,831,934 | Version: 2026.5 just released
Still Scrolling Icon

Still Scrolling?

Want proof fast? PM > Install-Package IronPdf
run a sample watch your HTML become a PDF.