pdf签名验证失败但无报错,主因是仅存在空签名域而非有效PKCS#7签名;证书链不可信常因吊销检查网络失败;iTextSharp解析易截断Contents字节;可信时间须依赖DSS中TSA而非M字段。

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