C# 验证PDF签名 C#如何检查PDF文件中的数字签名是否有效

来源:这里教程网 时间:2026-02-21 17:41:54 作者:

PDF签名验证失败但没报错?先确认是否在验证签名本身

很多开发者调用

SignatureCollection
AcroForm.Signatures
后发现
IsValid
返回
false
,却没意识到:PDF里可能根本没嵌入可验证的数字签名——它只是带了个签名域(
SignatureField
),而域里空着或只存了占位图像。真正的签名数据必须是符合 PKCS#7(或 CMS)格式的二进制对象,并绑定到某个字段的
Value
中。

实操建议:

pdfiumsharp
iTextSharp.LGPLv2.Core
打开文件后,先遍历所有
SignatureField
,检查其
FieldValue
是否非空且类型为
ByteString
若用
System.Security.Cryptography.Pkcs.SignedCms
解析,需先提取原始字节流,再调用
Decode()
;直接传入 PDF 字段的 Base64 编码字符串会抛
CryptographicException
注意 Adobe Reader 允许“视觉签名”(仅图片+文字),这类签名
SignatureCollection
根本不会收录——它只认 ISO 32000-1 定义的
Sig
字典项

证书链不可信?别硬写
Verify()
就完事

调用

signature.Verify()
返回
false
,常见原因是系统信任根证书库(Windows Certificate Store)里缺中间 CA 或 OCSP 响应不可达,而不是签名本身损坏。.NET 默认启用吊销检查(
X509RevocationMode.Online
),一旦网络不通或 CA 服务器返回 5xx,验证就静默失败。

实操建议:

改用
Verify(X509RevocationMode.NoCheck)
快速排除网络干扰;但上线前必须切回
Online
Offline
并配好
X509RevocationFlag.ExcludeRoot
手动加载证书链时,确保按“叶子→中间→根”顺序添加到
X509Chain.ChainPolicy.ExtraStore
,顺序反了会导致
PartialChain
错误
如果证书含 AIA(Authority Information Access)扩展,.NET 会自动尝试下载 CRL/OCSP;某些企业内网屏蔽外部 HTTPS,得提前缓存 CRL 到本地并用
ChainPolicy.CustomTrustStore
注入

用 iTextSharp 验证时提示
BadPaddingException
?大概率是签名字节被截断

iTextSharp.text.pdf.AcroFields.GetSignatureNames()
能列出签名名,但
GetSignatureDictionary()
返回的
PdfDictionary
里,
Contents
的值可能是不完整的十六进制字符串(如
 形式),直接转 
byte[]
会丢字节。iText 对 PDF 签名解析依赖底层
PdfReader
的流解码逻辑,跳过对象流或交叉引用表偏移错误都会导致内容截断。

实操建议:

不用
PdfString.ToUnicodeString()
处理
Contents
,改用
PdfString.GetOriginalBytes()
获取原始字节
验证前先调用
reader.ConsolidateNamedDestinations()
reader.RemoveUnusedObjects()
,避免因 PDF 结构损坏导致签名字节读取错位
若用
iText7.kernel.pdf.PdfDocument
,必须打开时传
new PdfReader(src, new ReaderProperties().SetUnethicalReading(true))
,否则加密/增量更新的 PDF 可能拒绝访问签名数据

时间戳失效或签名时间早于证书有效期?检查 PDF 文档时间戳(DSS)和签署时间字段

PDF 签名有效 ≠ 文档可信。签名时间由

M
字段(如
D:20230101120000+08'00'
)声明,但它可被任意设置;而真实可信的时间依据是签名中嵌入的 RFC 3161 时间戳令牌(TSA)。若 PDF 没包含 DSS(Document Security Store)或 TSA 证书不在信任链里,即使签名验签通过,法律效力也存疑。

实操建议:

pdfiumsharp
GetSignatureTimestamp()
提取 TSA 时间,对比系统时间与证书有效期,而非只信
M
字段
检查 PDF 是否含
DSS
字典(路径:
Root/DSS
),里面应有
Certificates
Revocations
Timestamps
子项;缺失则说明未启用长期验证(LTV)
生成签名时若用 iText7,务必调用
signer.SetCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING_AND_ANNOTATION)
并启用
EnableLtvForCollection()
,否则 DSS 不会自动注入

真正麻烦的是混合签名场景:一个 PDF 里多个签名、部分带 TSA、部分不带,还跨不同时间戳服务。这时候不能只看单个

IsValid
,得逐签名解析
SignerInfo
,比对
SigningTime
属性和各自嵌入的 TSA 证书链——这些细节,调试器里点开
signature.CryptoValue
才看得见。

相关推荐