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

PDF数字签名代码验证失败但Adobe Reader可正常验证问题求助

PDF数字签名校验失败问题排查

问题背景

有一份经过数字签名的PDF文件,使用Adobe Reader打开可正常通过签名校验,但自行开发的校验代码始终验证失败,两种实现方案的具体情况如下:

第一版原生JDK实现

代码如下:

public static void main(String[] args) throws Exception {

        HashMap<String, Object> keyPair = new HashMap<>();
        String password = "123";

        KeyStore keyStore = KeyStore.getInstance("pkcs12"); //, "BC");
        keyStore.load(new FileInputStream("/Users/shivam/Documents/Projects/label-processor/src/main/resources/DigitalSignatureFile.pfx"), password.toCharArray());

        Enumeration<String> keyStoreAliasEnum = keyStore.aliases();
        String alias = null;
        PublicKey publicKey = null;
        PrivateKey privateKey = null;
        byte[] signature = null;
        while ( keyStoreAliasEnum.hasMoreElements() ) {
            alias = keyStoreAliasEnum.nextElement();
            if (password != null) {
                privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray());

                X509Certificate x509Certificate = (X509Certificate) keyStore.getCertificate(alias);
                publicKey = x509Certificate.getPublicKey();
                signature = x509Certificate.getSignature();
                keyPair.put("Alias", alias);
                keyPair.put("PublicKey", publicKey);
                keyPair.put("PrivateKey", privateKey);
                keyPair.put("X509Certificate", x509Certificate);
            }
        }
        verify(publicKey, signature);
    }

    public static void verify(PublicKey publicKey, byte[] sigBytes) throws Exception {

        Signature sig = Signature.getInstance("SHA256withRSA");
        sig.initVerify(publicKey);
        FileInputStream datafis = new FileInputStream("/Users/shivam/Desktop/DigitallySignedFile.pdf");
        byte[] data = IOUtils.toByteArray(datafis);
        sig.update(data);
        boolean verifies = sig.verify(sigBytes);
        System.out.println("signature verifies: " + verifies);
        FileUtils.writeByteArrayToFile(
                new File("/Users/shivam/Documents/Projects/label-processor/src/main/resources/VerifiedFile.pdf"),
                data
        );
    }

运行结果:输出signature verifies: false,导出的文件打开后显示签名未验证。

第二版iText库实现

代码如下:

public static void main(String[] args) throws Exception {

    byte[] pdfBytes = Files.readAllBytes(
            Paths.get("/Users/shivam/Desktop/DigitallySignedFile.pdf")
    );
    PdfReader pdfReader = new PdfReader(pdfBytes);
    AcroFields acroFields = pdfReader.getAcroFields();
    List<String> signatures = acroFields.getSignatureNames();
    boolean valid = false;
    if (!signatures.isEmpty()) {
        for (String signature : signatures) {
            if (acroFields.signatureCoversWholeDocument(signature)) {
                PdfPKCS7 pkcs7 = acroFields.verifySignature(signature);
                valid = pkcs7.verify();
                String reason = pkcs7.getReason();
                Calendar signedAt = pkcs7.getSignDate();
                X509Certificate x509Certificate = pkcs7.getSigningCertificate();
                Principal issuerDN = x509Certificate.getIssuerDN();
                Principal subjectDN = x509Certificate.getSubjectDN();
                logger.info("valid = {}, date = {}, reason = '{}', issuer = '{}', subject = '{}'",
                        valid, signedAt.getTime(), reason, issuerDN, subjectDN);
                break;
            }
        }
    }
    FileUtils.writeByteArrayToFile(
            new File("/Users/shivam/Documents/Projects/label-processor/src/main/resources/VerifiedFile.pdf"),
            pdfBytes
    );
}

运行结果:代码日志输出签名验证通过,但导出的文件打开后仍显示签名未验证。


问题原因分析&解决思路

第一版实现错误点

  1. 签名值来源错误:代码中用x509Certificate.getSignature()获取到的是CA机构给证书本身做的签名,不是PDF文件中存储的对文档内容的签名值,PDF的签名内容存储在文档的AcroForm签名字段中。
  2. 校验范围错误:直接使用整个PDF文件的字节做哈希校验不符合PDF签名规范,签名生成时会排除签名字段中存储的签名值本身对应的字节,只对ByteRange属性指定的分段字节范围做哈希,直接全文件计算哈希必然和签名时的哈希值不匹配。

第二版实现问题排查

  1. 文件一致性校验:先对比原始PDF和导出的VerifiedFile.pdf的MD5或SHA哈希值,如果哈希不一致,说明文件写出环节被篡改:检查是否有其他代码修改了pdfBytes变量,或者文件写出工具类存在编码处理问题。如果哈希完全一致,说明导出文件和原文件完全相同,问题出在阅读器端:检查打开导出文件使用的阅读器版本、配置是否和打开原文件时一致,是否开启了离线模式导致无法完成证书吊销校验等操作。
  2. 校验逻辑对齐:iText的pkcs7.verify()默认只校验签名的摘要完整性和签名有效性,不会校验证书信任链、有效期、吊销状态,如果需要和Adobe Reader的校验逻辑完全对齐,需要补充证书链校验逻辑,传入系统信任根证书库,对签名证书的有效性做完整校验。

内容的提问来源于stack exchange,提问作者Shivam Singh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 00:45:03