如何在 C# 中读取 Verified PDF 签名中的签名者证书(X.509、eIDAS、证书链)
一份合同进入您的审计流程。该合同已进行数字签名,签名有效,而您的合规团队需要确认:谁签署了该合同?签署人当天的证书是否有效?签发证书的 CA 是否在您的受信任列表中?您可以手动提取原始签名字节、解析 DER 格式,并逐级追踪证书链。 或者,您可以请求SignerCertificate,并将答案作为属性读取。
这就是 VerifiedSignature 上的SignerCertificate 和 CertificateChain属性的构建目的。 从PdfDocument.GetVerifiedSignatures()返回,它们公开签署人的完整X.509身份、颁发CA、有效期、SHA-256指纹、原始DER字节以及所有到根的中间证书。 这就是您实现 eIDAS 合规性、证书固定、审计追踪以及信任链验证所需的 API 接口。
本指南将详细介绍三类证书检查:
- **标准X.509详细信息:**身份属性、完整的专有名称、有效期窗口以及用于
X509Certificate2互操作的原始DER字节。 - **eIDAS和受监管行业字段:**从
2.5.4.97的数字OID以获取eIDAS组织标识符。 - **信任链验证:**SHA-256 指纹固定,并从签名者追溯至根 CA 完整验证整个信任链。
立即开始 30天试用,在您的验证流程中测试证书检查功能。
PM > Install-Package IronPdf
在加载的PDF上调用GetVerifiedSignatures,并直接从结果中读取签署人的证书属性。
-
1Install IronPDF with NuGet Package Manager
PM > Install-Package IronPdf
-
2Copy 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}");C# -
3Deploy to test on your live environment
Start using IronPDF in your project today with a free trial
最小化工作流程(三步)
- 从 NuGet 下载 IronPDF
- 加载一个已签名的 PDF 文件,并调用
GetVerifiedSignatures()方法以获取经过验证的签名对象列表 - 读取
sig.SignerCertificate以验证身份和有效性,或遍历sig.CertificateChain以获取完整的信任链
如何提取签名人的国民身份证号或个人身份证号?
SubjectSerialNumber属性从证书的主体DN中返回SERIALNUMBER OID的值。 在受监管的行业(如 eIDAS 合格证书、银行业、政府颁发的数字身份)中,该字段包含签署人的国民身份证号、税务识别号或个人识别码。 这不是证书本身的序列号; 那就是CertificateSerialNumber。
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}")
NextSubjectSerialNumber告诉您谁签署了文档(一个人或组织ID,如国家ID或嵌入在主体DN中的税号)。 CertificateSerialNumber告诉您使用了哪个证书; (这是签发证书的证书颁发机构为撤销列表和审计日志分配的唯一序列号。如何访问便捷标识属性?
SignerCertificateInfo将最常用的主体DN字段公开为直接属性,因此您不需要自己解析完整的DN字符串。
读取签名人的姓名和所属机构
Email属性涵盖了大多数身份工作流程首先需要的内容。
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}")
NextemailAddress OID。 如果不存在,则回退到主体备用名称(SAN)rfc822Name扩展。 Country属性返回两位字母的ISO 3166-1 alpha-2代码(例如,"DE","US","FR")。 如果证书中不存在相应字段,所有便利属性都返回null。
当您需要全面了解时,请阅读完整的DN
对于审核记录、下游解析或合规报告,IssuerDN返回完整的专有名称字符串。
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如何提取任意"主题"和"发件人"字段?
GetIssuerField(string)方法接受简短名称或数字OID。 它们不区分大小写,如果字段不存在,则返回null。
支持的简短名称:ORGANIZATIONIDENTIFIER。
读取任意主题字段(包括 eIDAS OID)
传递一个像 "OU" 的简短名称或像 "2.5.4.97" (eIDAS 组织标识符) 的数字OID以从主体DN中提取任何字段。
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数字 OID 查询对于 eIDAS 证书以及受监管行业的证书至关重要,这些证书嵌入了组织标识符、专业资质或标准短名称未涵盖的特定管辖区域字段。
读取发行人的字段
GetIssuerField以相同的方式处理颁发CA的专有名称。 这对于信任验证、CA 报告以及按签发机构对签名进行分组非常有用。
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如何验证证书的有效性?
SignerCertificateInfo提供两个有效性帮助器:
- 如果
true。 - 如果给定的时间戳落在
true。 典型用法是确认证书在签名时是否有效。
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
bool validAtSigning = cert.IsValidAt(sig.SigningDate);
Console.WriteLine($"Valid at signing time ({sig.SigningDate}): {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
Dim validAtSigning As Boolean = cert.IsValidAt(sig.SigningDate)
Console.WriteLine($"Valid at signing time ({sig.SigningDate}): {validAtSigning}")
Nexttrue)。 该辅助程序采用保守策略;它不会仅因元数据缺失就推定其已过期。 对于零信任工作流,应将缺少有效期明确视为失败情况。对于审计轨迹,同时记录IsValidAt(signingDate)检查(签署时间的有效性)。 一个证书即使已过期,签署文档时可能仍然有效; 这两个数据点对于合规而言都很重要。
如何使用SHA-256指纹进行证书固定?
Sha256Thumbprint属性返回一个64字符的大写十六进制字符串,即DER编码证书的SHA-256哈希。 这是您用来与已知良好的接受签署人证书列表进行固定的值。
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如何从原始证书数据构造X509Certificate2?
byte[]。 每次调用返回一个防御性副本,因此内部数据永远不会通过引用暴露。 您可以使用它来构造一个System.Security.Cryptography.X509Certificates.X509Certificate2以与.NET的本地加密API进行互操作(链构建、策略验证、CRL检查)。
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")
NextRawData都会返回一个新的防御性副本。 内部字节数组永远不会通过引用暴露,因此修改返回的字节不会影响后续调用。此互操作路径允许您将IronPDF的签名验证插入现有的.NET证书验证管道中,例如通过X509CertificateValidator实现。
如何遍历证书链?
IReadOnlyList<SignerCertificateInfo>,按签署人→中间CA→根CA顺序排列。 索引0始终为签署人证书(与SignerCertificate相同的对象)。 列表永远不为null; 如果无法提取链,它仅包含签署人证书。
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
NextGetIssuerField()。 您可以验证每个中间CA的过期情况,将根的指纹与您信任的根存储进行对比,并记录完整的链以供合规审计。下一步
CertificateChain可让您在不离开IronPDF API表面的情况下进行完整的X.509检查。 从这里开始,自然的下一步是深入到这些属性插入的IronPDF部分。
对于工作流程的签署方,数字签名操作指南涵盖了证书创建、元数据和增量签名。 对于高安全性环境中的基于HSM的签名,HSM签名指南涵盖了PKCS#11集成。 需要现成可运行的代码片段? 数字签名代码示例提供了它们。
准备在您自己的签名PDF上试试吗? 开始您的免费30天试用或查看许可选项。
Frequently Asked Questions
从PDF签名读取X.509证书详细信息有什么目的?
从PDF签名读取X.509证书详细信息允许您验证签名者的身份,检查证书的有效性,并确保符合eIDAS等标准。此过程对于维护文档的真实性和完整性至关重要。
IronPDF如何帮助从PDF签名中提取签名者身份?
IronPDF提供了从PDF签名中提取签名者身份的工具,通过访问X.509证书详细信息,使文件源头的身份认证和签名者的凭证验证更容易。
IronPDF可以从PDF签名中提取eIDAS字段吗?
是的,IronPDF可以从PDF签名中提取eIDAS字段,允许开发人员确保文件符合欧盟关于电子签名的法规。
使用IronPDF可以从PDF签名中检索到什么信息?
使用IronPDF,您可以从PDF签名中检索诸如签名者身份、证书有效性、指纹和证书链等信息,提供对签名真实性的全面的了解。
使用IronPDF验证PDF签名的有效性是否可能?
是的,IronPDF允许您通过检查X.509证书详细信息来验证PDF签名的有效性,这可以包括检查到期日期和签名证书的完整性。
使用IronPDF进行PDF签名验证有哪些好处?
使用IronPDF进行PDF签名验证提供了诸如自动提取详细证书信息、与C#应用程序兼容以及遵循eIDAS等国际标准的好处。
IronPDF如何从PDF签名中提取指纹?
IronPDF通过访问PDF签名中的X.509证书详细信息来处理指纹提取,使您可以通过其独特的指纹验证证书的真实性和完整性。
IronPDF可以从PDF签名中提供证书链吗?
IronPDF可以从PDF签名中提供证书链,帮助您跟踪从签名者证书到受信任根证书的信任路径,确保签名的有效性。
IronPDF支持哪种编程语言用于读取PDF签名?
IronPDF支持C#用于读取和验证PDF签名,使其成为.NET生态系统中开发人员的理想解决方案。
IronPDF是否符合电子签名的国际标准?
是的,IronPDF符合国际标准如eIDAS,确保您的PDF签名验证过程符合电子签名的监管要求。

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.