如何提取PDF文件中的各流数据以完成解密任务?
如何从加密PDF中提取每条流数据用于自定义解密?
我正在开展一项PDF解密任务:PDF厂商采用自研方法加密每个流数据,同时提供了解密函数。已验证该解密函数可正确处理单条流数据,但PDF文件包含大量流数据,需要提取每条流数据并逐一传入解密函数处理。
加密流示例(原始PDF中)
6 0 obj <<Length 608/Filter[/VendorPDFEncrypt/FlateDecode]>>stream data_1 endstream endobj
解密后对应流示例(厂商提供的解密PDF中)
2 0 obj <<Filter[/FlateDecode]/Length 598>>stream data_2 endstream endobj
处理流程
encrypted pdf file -> extract each stream data -> feed it to decrypt function repeatedly-> get a readable pdf file
提取流数据的方法
方法1:使用pdfminer.six(推荐,底层精准)
pdfminer.six可以直接获取PDF中未经过任何Filter解码的原始流数据,正好匹配解密函数所需的输入:
- 安装依赖
pip install pdfminer.six
- 提取所有原始流的代码
from pdfminer.pdfparser import PDFParser, PDFDocument from pdfminer.pdftypes import PDFStream def extract_raw_pdf_streams(pdf_file_path): raw_streams = [] with open(pdf_file_path, 'rb') as pdf_file: parser = PDFParser(pdf_file) doc = PDFDocument(parser) parser.set_document(doc) doc.initialize() # 遍历文档中所有对象 for obj_item in doc.get_objects(): obj_num, obj_gen, obj_data = obj_item if isinstance(obj_data, PDFStream): # 获取未经过任何Filter处理的原始流字节 raw_stream_bytes = obj_data.get_raw_data() raw_streams.append(raw_stream_bytes) return raw_streams
方法2:使用PyPDF2(需调整解码设置)
PyPDF2默认会自动应用PDF标准Filter解码,需调整以获取原始加密流:
- 安装依赖
pip install PyPDF2
- 提取原始流的代码
from PyPDF2 import PdfReader def extract_raw_streams_with_pypdf2(pdf_file_path): raw_streams = [] # 初始化阅读器,关闭自动解码逻辑 reader = PdfReader(pdf_file_path, strict=False) # 遍历所有文档对象 for obj_id in reader._objects: obj = reader._objects[obj_id] if hasattr(obj, 'get_object'): obj_content = obj.get_object() # 判断是否为流对象 if '/Stream' in obj_content and '/Length' in obj_content: # 直接获取原始流字节,跳过自动解码 raw_stream = obj_content['/Stream'] raw_streams.append(raw_stream) return raw_streams
方法3:手动解析(不推荐,仅作参考)
PDF流的结构固定为<<字典>>stream\n流数据\nendstream,可以手动遍历文件字节截取,但需处理格式细节问题:
- 严格匹配
stream和endstream关键字(PDF大小写敏感) - 依据字典中的
/Length值确认流数据长度,避免截取错误 - 处理流数据中的换行、空白等格式差异
手动解析示例伪代码:
def extract_streams_manual(pdf_file_path): streams = [] with open(pdf_file_path, 'rb') as f: content = f.read().decode('latin1') start_marker = 'stream\n' end_marker = '\nendstream' while start_marker in content: start_idx = content.find(start_marker) + len(start_marker) end_idx = content.find(end_marker, start_idx) if end_idx == -1: break stream_data = content[start_idx:end_idx].encode('latin1') streams.append(stream_data) content = content[end_idx + len(end_marker):] return streams
后续处理建议
提取到原始流数据后,逐一传入解密函数得到解密后的流,然后:
- 替换原PDF中对应流的内容为解密后的数据
- 修改流对象字典中的
/Filter,移除VendorPDFEncrypt项 - 更新
/Length值为解密后流数据的字节长度 - 重新生成PDF文件
内容的提问来源于stack exchange,提问作者Tom Xue
相关产品推荐
相关产品推荐

