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才看得见。
