如何用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
相关产品推荐
相关产品推荐

