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

如何用iText7获取PDF文档的已签名内容(类似PDFBox的getSignedContent)

用iText7实现PDF已签名内容提取(类似PDFBox的PDSignature.getSignedContent)

要提取PDF中排除签名字节区域的已签名内容,核心是利用PDF签名的ByteRange属性——它定义了签名覆盖的原始文件字节范围,中间未被覆盖的部分就是签名值的位置,需要排除。以下是具体实现步骤和代码:

核心原理

PDF签名的ByteRange是一个四元素数组:[start1, length1, start2, length2],表示签名校验的有效内容是:

  • 从start1开始、长度为length1的字节段
  • 从start2开始、长度为length2的字节段
    把这两段字节拼接起来,就是需要提取的"已签名内容"(即原始PDF减去签名值所在的中间字节区域)。

代码实现

import com.itextpdf.kernel.pdf.PdfArray;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.signatures.PdfSignature;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class SignedContentExtractor {
    // 提取指定签名对应的已签名内容
    public static byte[] getSignedContent(String pdfFilePath, PdfSignature signature) throws IOException {
        // 读取原始PDF文件的完整字节(ByteRange基于原始文件偏移计算,必须用原始字节)
        byte[] rawPdfBytes = readRawPdfBytes(pdfFilePath);
        PdfArray byteRange = signature.getByteRange();

        if (byteRange.size() != 4) {
            throw new IllegalArgumentException("不支持非标准的ByteRange格式");
        }

        // 解析ByteRange的四个参数
        long segment1Start = byteRange.getAsNumber(0).longValue();
        long segment1Length = byteRange.getAsNumber(1).longValue();
        long segment2Start = byteRange.getAsNumber(2).longValue();
        long segment2Length = byteRange.getAsNumber(3).longValue();

        // 拼接两段有效字节
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        outputStream.write(rawPdfBytes, (int) segment1Start, (int) segment1Length);
        outputStream.write(rawPdfBytes, (int) segment2Start, (int) segment2Length);

        return outputStream.toByteArray();
    }

    // 读取原始PDF文件字节
    private static byte[] readRawPdfBytes(String filePath) throws IOException {
        try (FileInputStream inputStream = new FileInputStream(filePath);
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            return outputStream.toByteArray();
        }
    }

    // 示例用法
    public static void main(String[] args) throws IOException {
        String signedPdfPath = "path/to/your/signed.pdf";
        try (PdfReader reader = new PdfReader(signedPdfPath);
             PdfDocument pdfDoc = new PdfDocument(reader)) {
            // 获取文档中的第一个签名(可根据实际需求按字段名或索引获取)
            PdfSignature targetSignature = pdfDoc.getSignatureCatalog().getSignatureList().get(0);
            byte[] signedContent = getSignedContent(signedPdfPath, targetSignature);
            
            // 后续处理:比如保存到文件、校验哈希等
            // ...
        }
    }
}

关键注意事项

  • 必须使用原始PDF字节:iText处理后的PDF结构可能有调整,ByteRange是基于原始文件的字节偏移计算的,因此不能用PdfDocument导出的字节,必须直接读取原始文件。
  • 多签名场景:如果PDF有多个签名,每个签名的ByteRange不同,需要针对每个签名单独调用方法提取对应的已签名内容。
  • ByteRange格式校验:标准PDF签名的ByteRange是四元素数组,部分非标准签名可能格式不同,需额外处理。

内容的提问来源于stack exchange,提问作者Thiago Sayão

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 00:30:16