如何用Python脚本检测PDF是否签名及提取公钥等签名信息?
检测PDF是否已签名并提取签名证书的Python方案
一、首选工具:pyhanko
pyhanko是专门针对PDF签名处理的Python库,能直接检测PDF签名、提取签名证书,无需手动解析复杂的PDF底层结构,非常适合新手使用,比PyPDF2(仅侧重文本提取)、cryptography(通用加密库)更贴合PDF签名场景。
1. 安装pyhanko
pip install pyhanko
2. 检测签名并提取证书的实现代码
以下脚本可完成PDF签名检测、证书信息查看,以及导出CER(证书文件)和PKCS7格式签名文件:
from pyhanko.pdf_utils.reader import PdfFileReader from cryptography.hazmat.primitives.serialization import Encoding def process_pdf_signatures(pdf_path): # 读取目标PDF文件 with open(pdf_path, 'rb') as pdf_file: reader = PdfFileReader(pdf_file) # 快速判断是否存在签名 if not reader.has_signatures(): print("该PDF文件未包含任何签名") return # 遍历所有签名域并处理 for sig_field_name in reader.list_signature_fields(): sig_obj = reader.get_signature(sig_field_name) print(f"\n找到签名域:{sig_field_name}") # 提取并打印签名证书基本信息 signing_cert = sig_obj.signing_cert if signing_cert: print("签名证书详情:") print(f"颁发主体:{signing_cert.subject.rfc4514_string()}") print(f"颁发机构:{signing_cert.issuer.rfc4514_string()}") # 导出证书为CER格式(PEM编码) cer_content = signing_cert.public_bytes(encoding=Encoding.PEM) cer_output_path = f"{sig_field_name}_cert.cer" with open(cer_output_path, 'wb') as cer_file: cer_file.write(cer_content) print(f"证书已导出至:{cer_output_path}") # 导出签名为PKCS7格式 pkcs7_content = sig_obj.pkcs7_signer_info if pkcs7_content: pkcs7_output_path = f"{sig_field_name}_signature.p7s" with open(pkcs7_output_path, 'wb') as pkcs7_file: pkcs7_file.write(pkcs7_content) print(f"PKCS7格式签名已导出至:{pkcs7_output_path}") # 调用示例 process_pdf_signatures("your_target.pdf")
二、cryptography+PyPDF2的替代方案(不推荐新手)
如果一定要结合cryptography实现,需要手动解析PDF的签名字典结构,步骤繁琐,适合有PDF底层知识的开发者:
from PyPDF2 import PdfReader from cryptography.hazmat.primitives.serialization import pkcs7, Encoding from cryptography.hazmat.backends import default_backend def extract_cert_with_cryptography(pdf_path): reader = PdfReader(pdf_path) # 检查是否存在表单域 if '/AcroForm' not in reader.trailer['/Root'] or '/Fields' not in reader.trailer['/Root']['/AcroForm']: print("该PDF无签名域") return # 遍历所有表单字段,定位签名字段 for field in reader.trailer['/Root']['/AcroForm']['/Fields']: if field.get('/FT') == '/Sig': sig_dict = field['/V'] # 提取PKCS7格式的签名内容 sig_contents = sig_dict['/Contents'].get_object() try: # 解析PKCS7数据获取证书 certs = pkcs7.load_der_pkcs7_certificates(sig_contents, default_backend()) for idx, cert in enumerate(certs): cer_output_path = f"extracted_cert_{idx}.cer" with open(cer_output_path, 'wb') as cer_file: cer_file.write(cert.public_bytes(Encoding.PEM)) print(f"证书已导出至:{cer_output_path}") except Exception as e: print(f"解析证书失败:{str(e)}") # 调用示例 extract_cert_with_cryptography("your_target.pdf")
注意:此方法需要处理PDF对象的底层编码和结构,容易出现解析异常,新手优先选择pyhanko方案。
三、其他简便检测方法
- 命令行工具批量检测:使用
pdftk命令行工具快速验证PDF是否有签名,适合批量处理场景:
若输出包含pdftk your_target.pdf dump_data | grep "Signature"SignatureFieldName字段,则说明PDF已签名。 - pypdfium2快速检测:通过
pypdfium2库直接遍历PDF注释,快速判断是否存在签名:import pypdfium2 as pdfium def is_pdf_signed(pdf_path): pdf_doc = pdfium.PdfDocument(pdf_path) for page in pdf_doc: for annot in page.get_annotations(): if annot.get_type() == pdfium.PDF_ANNOT_WIDGET and annot.get_field_type() == pdfium.PDF_FIELD_SIGNATURE: return True return False print("PDF已签名" if is_pdf_signed("your_target.pdf") else "PDF未签名")
内容的提问来源于stack exchange,提问作者Raghu Varier
相关产品推荐
相关产品推荐

