You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用iText验证PDF签名的文档哈希、证书有效性及篡改情况?

PDF签名验证完整实现(基于iText)

核心逻辑梳理

PDF签名验证本质是三个核心环节:证书有效性校验、签名哈希与文档完整性匹配、签名合法性验证。你找不到待验证哈希的原因是:哈希并非直接存储在/Sig字典中,而是通过/ByteRange指定的文档片段计算得出,再与PKCS#7签名中的摘要做比对。


一、证书有效性验证(PKCS#7方式)

用iText的PdfPKCS7类直接解析签名内容,完成证书链有效性校验:

PdfReader reader = new PdfReader("signed.pdf");
AcroFields acroFields = reader.getAcroFields();
List<String> signNames = acroFields.getSignatureNames();

for (String signName : signNames) {
    // 获取签名对应的PKCS7对象
    PdfPKCS7 pkcs7 = acroFields.verifySignature(signName);
    
    // 验证证书链(需传入信任证书库,这里示例用自定义信任存储)
    boolean certValid = pkcs7.verifyCertificates(new TrustedCertStore().getKeystore(), null, null);
    
    // 输出证书状态与详情
    System.out.println("证书有效性:" + certValid);
    X509Certificate signCert = pkcs7.getSigningCertificate();
    System.out.println("签名证书主体:" + signCert.getSubjectDN());
}

二、签名哈希与文档完整性校验

关键原理:/ByteRange的作用

/Sig字典中的/ByteRange是四元素数组(如[0, 1000, 2000, 500]),表示签名时仅对0到1000字节和2000到2500字节的内容做哈希,中间1000-2000字节是签名内容(/Contents),签名时会被跳过。我们需要计算这两段内容的哈希,再与PKCS7中的签名摘要比对。

代码实现

for (String signName : signNames) {
    PdfPKCS7 pkcs7 = acroFields.verifySignature(signName);
    PdfDictionary sigDict = acroFields.getSignatureDictionary(signName);
    
    // 获取ByteRange数组
    PdfArray byteRange = sigDict.getAsArray(PdfName.BYTERANGE);
    long[] ranges = byteRange.toLongArray();
    
    // 初始化哈希算法(与签名算法一致)
    MessageDigest digest = MessageDigest.getInstance(pkcs7.getDigestAlgorithm());
    RandomAccessFile raf = new RandomAccessFile("signed.pdf", "r");
    
    // 计算第一段内容的哈希
    raf.seek(ranges[0]);
    byte[] buffer = new byte[8192];
    long remaining = ranges[1];
    while (remaining > 0) {
        int read = raf.read(buffer, 0, (int) Math.min(remaining, buffer.length));
        digest.update(buffer, 0, read);
        remaining -= read;
    }
    
    // 计算第二段内容的哈希
    raf.seek(ranges[2]);
    remaining = ranges[3];
    while (remaining > 0) {
        int read = raf.read(buffer, 0, (int) Math.min(remaining, buffer.length));
        digest.update(buffer, 0, read);
        remaining -= read;
    }
    raf.close();
    
    // 比对计算哈希与签名摘要
    byte[] computedHash = digest.digest();
    byte[] signedHash = pkcs7.getEncodedPKCS1();
    boolean isTampered = !Arrays.equals(computedHash, signedHash);
    
    System.out.println("文档是否被篡改:" + isTampered);
}

三、正确获取/Sig字典的方式

直接通过AcroFields的签名字段获取,避免无效操作:

PdfDictionary sigDict = acroFields.getSignatureDictionary(signName);
// 读取字典中指定字段
PdfName filter = sigDict.getAsName(PdfName.FILTER);
PdfDate signDate = sigDict.getAsDate(PdfName.M);
// ...其他字段读取逻辑

注意事项

  1. 哈希算法必须与签名时使用的一致,可通过pkcs7.getDigestAlgorithm()获取
  2. 证书验证需配置信任证书库,否则无法判断证书是否来自可信机构
  3. 若PDF含时间戳签名,需额外验证时间戳的有效性

内容的提问来源于stack exchange,提问作者tulak.hord

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 02:58:39