ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

pypdf 全面指南:使用 Python 对 PDF 进行加密与解密(RC4 / AES 全算法实战)

pypdf 全面指南:使用 Python 对 PDF 进行加密与解密(RC4 / AES 全算法实战) pypdf 全面指南使用 Python 对 PDF 进行加密与解密RC4 / AES 全算法实战【免费下载链接】pypdfA pure-python PDF library capable of splitting, merging, cropping, and transforming the pages of PDF files项目地址: https://gitcode.com/GitHub_Trending/py/pypdf导读本文围绕 pypdf 的加密与解密能力展开系统讲解如何用PdfWriter.encrypt()为 PDF 添加密码保护、如何用PdfReader.decrypt()打开受保护的文档并深入到仓库源码层面剖析 RC4-40、RC4-128、AES-128、AES-256-R5、AES-256 五种算法的底层实现、可选加密依赖的安装方式以及用户权限位的控制方法。读完本文你将能够独立完成加密 PDF → 分发 → 密码解密的完整工作流并理解这些操作在 PDF 标准安全处理器中的真实原理。PDF 加密的基础算法与标准支持PDF 的加密机制基于对称加密算法。pypdf 支持 RC4 与 AES 两类算法且覆盖不同的密钥长度RC4历史最悠久的流密码PDF 1.1 引入密钥长度可为 40 位RC4-40或 128 位RC4-128。AES分组密码PDF 1.5 起引入 AES-128PDF 2.0 引入 AES-256含 Revision 5 与 Revision 6 两个变体。根据 加密解密官方文档pypdf 完整支持以上所有算法直至最新的PDF-2.0标准。在源码中这一能力体现在 pypdf/_encryption.py 的EncryptAlgorithm枚举里每个算法用(V, R, Length)三元组表示——V是加密字典中的算法代码R是安全处理器修订版本号Length是密钥位数class EncryptAlgorithm(tuple, Enum): # V, R, Length RC4_40 (1, 2, 40) RC4_128 (2, 3, 128) AES_128 (4, 4, 128) AES_256_R5 (5, 5, 256) AES_256 (5, 6, 256)其中AES-256-R5对应修订版本 R5AES-256对应 R6两者算法强度相同R6 增加了更强的密钥派生过程。环境准备AES 需要额外依赖一个重要的前提pypdf 对 RC4 的支持开箱即用但对 AES 的加解密依赖第三方加密库。官方文档明确推荐使用pyca/cryptography备选方案是pycryptodome。在仓库的加密提供者模块 pypdf/_crypt_providers/init.py 中可以看到清晰的依赖解析逻辑pypdf 会按顺序尝试导入_cryptography→_pycryptodome→_fallback三个实现只要装上了cryptography或pycryptodome其中之一AES 相关函数aes_cbc_encrypt、aes_cbc_decrypt、aes_ecb_encrypt、aes_ecb_decrypt即可正常工作。安装方式详见 安装指南# 安装全部可选依赖加密 图像等 pip install pypdf[full] # 仅安装加密所需依赖处理 AES 加密的 PDF 时必需 pip install pypdf[crypto] # 仅安装基础版本仅支持 RC4 加解密 pip install pypdf提示如果你只处理 RC4 加密的 PDF普通安装即可一旦需要加密或解密 AES 类的 PDF务必先执行pip install pypdf[crypto]。实战一加密 PDF加密的入口是PdfWriter.encrypt()。最简流程是先用PdfReader读取原文件再用PdfWriter(clone_fromreader)克隆内容加密后写出。完整示例取自 官方文档from pypdf import PdfReader, PdfWriter reader PdfReader(example.pdf) writer PdfWriter(clone_fromreader) # Add a password to the new PDF writer.encrypt(my-secret-password, algorithmAES-256) # Save the new PDF to a file writer.write(out-encrypt.pdf)algorithm 参数的五个可选值encrypt()的algorithm参数决定加密强度官方文档给出的可选值如下algorithm 取值对应枚举说明RC4-40RC4_4040 位密钥 RC4兼容老版本阅读器最不安全RC4-128RC4_128128 位密钥 RC4AES-128AES_128128 位密钥 AESAES-256-R5AES_256_R5256 位密钥 AESRevision 5官方推荐AES-256AES_256256 位密钥 AESRevision 6官方文档明确建议优先使用AES-256-R5。同时文档给出了一个安全警告⚠️ pypdf 在省略algorithm参数时默认使用RC4-128以兼容旧版工具。由于 RC4 已被认为不安全实际使用中应始终显式指定 AES 算法。这一点在源码 pypdf/_writer.py 中得到印证当algorithm为None时pypdf 回退到EncryptAlgorithm.RC4_128只有当use_128bitFalse时才降级为RC4_40。因此切勿省略 algorithm 参数。encrypt() 的完整参数签名结合 pypdf/_writer.py 的源码encrypt()的完整签名与含义如下writer.encrypt( user_password: str, # 用户密码打开文档需提供的密码受权限限制 owner_password: Optional[str] None, # 所有者密码无限制访问默认与 user_password 相同 use_128bit: bool True, # 兼容旧接口当 algorithm 有效时被忽略 permissions_flag: UserAccessPermissions ALL_DOCUMENT_PERMISSIONS, algorithm: Optional[str] None, # RC4-40 / RC4-128 / AES-128 / AES-256-R5 / AES-256 ) - None其中owner_password允许你设置管理者口令——持有它的人可以不受权限限制地打开文档若不提供则默认与用户密码相同。algorithm一旦合法指定use_128bit参数即被忽略。需要留意的一个限制源码 pypdf/_writer.py对增量更新的 PDFincrementalTrue调用encrypt()会抛出NotImplementedError加密操作需在普通模式下进行。实战二解密 PDF解密使用PdfReader.decrypt()流程是先判断is_encrypted再传入正确的密码用户密码或所有者密码均可最后克隆写出。完整示例取自 官方文档from pypdf import PdfReader, PdfWriter reader PdfReader(encrypted-file.pdf) if reader.is_encrypted: reader.decrypt(test) # secret password writer PdfWriter(clone_fromreader) # Save the new PDF to a file writer.write(out-decrypt.pdf)源码层面的行为细节从 pypdf/_reader.py 的源码可以看出以下关键行为PdfReader.decrypt(password)会依次核对用户密码与所有者密码先验证所有者密码见 pypdf/_encryption.py 的verify_v4与verify_v5只要其中一个匹配即获得解密密钥返回值是PasswordType枚举NOT_DECRYPTED/USER_PASSWORD/OWNER_PASSWORD可用来判断命中的是哪种密码。测试用例 tests/test_encryption.py 中也对此做了断言reader.decrypt(owner_password) PasswordType.OWNER_PASSWORD。is_encrypted是一个只读属性它检查文件 trailer 中是否包含/Encrypt条目即使调用decrypt()成功该属性依然保持True这是源码 docstring 中特别说明的行为。如果文件本身未加密却调用decrypt()会抛出PdfReadError(Not encrypted file)。密码错误时不会抛出异常而是返回PasswordType.NOT_DECRYPTED后续读取页面内容才会因无法解密而失败因此实践中建议检查decrypt()的返回值。解密后另存为新文件即可去除密码上面的示例本质上是通过解密读取 → 克隆写入生成一份新的未加密 PDF。这也是社区中去除 PDF 密码的常规做法解密成功后用PdfWriter(clone_fromreader)写出新文件新文件将不再包含加密字典。原理纵深pypdf 是如何加解密 PDF 的1. 加密字典与 CryptFilterPDF 的加密信息存放在 trailer 的加密字典Encrypt dictionary中包含/V算法版本、/R修订号、/Length密钥位数、/P权限位、/O、/U、/OE、/UE、/Perms等条目。pypdf 在 pypdf/_encryption.py 的Encryption类中统一管理这些参数。在Encryption内部_make_crypt_filter()pypdf/_encryption.py实现了 PDF 标准 Algorithm 1 / Algorithm 3.1a 的密钥派生逻辑对 V ≤ 4 的算法RC4、AES-128将对象号3 字节小端与代次号2 字节小端拼接到文件加密密钥之后再做 MD5 哈希取前 n5 字节AES-128 额外拼接sAlT四个字节。对 V ≥ 5 的算法AES-256不再使用 MD5 密钥派生直接用 32 字节文件加密密钥因此 AES-256 加密的文件可以在禁用了 MD5 的 FIPS 环境中正常读取这是源码注释中明确指出的优势。2. V4RC4 / AES-128与 V5AES-256两套实现源码 pypdf/_encryption.py 按 PDF 标准将算法分为两个实现类AlgV4pypdf/_encryption.py对应 RC4-40 / RC4-128 / AES-128实现了 PDF 规范中的 Algorithm 27包括用固定 32 字节填充串对密码补齐、MD5 哈希 50 轮迭代、O/U 值的 RC4 加密计算与口令验证等。AlgV5pypdf/_encryption.py对应 AES-256-R5 / AES-256实现了 Algorithm 3.2a3.10核心是 SHA-256 哈希 AES-256-CBC / ECB 模式通过 Validation Salt 验证口令、通过 Key Salt 派生中间密钥、再用中间密钥解出 32 字节文件加密密钥最后用 ECB 模式解密/Perms校验权限是否被篡改。3. V5 口令的 SASLprep 规范化PDF 2.0AES-256要求口令先经过SASLprepRFC 4013规范化再按 UTF-8 编码。pypdf 在 pypdf/_encryption.py 的_saslprep()中实现了完整的映射、NFKC 归一化、禁用字符检查与双向文本RandALCat/LCat检查若口令包含规范禁止的字符在严格模式下会抛出ValueError否则回退为普通 UTF-8 编码并记录警告。测试文件 tests/test_encryption.py 对_saslprep有专门覆盖。4. 加密提供者的降级机制pypdf/_crypt_providers/init.py 展示了三层降级链优先cryptography其次pycryptodome最后是仅含 RC4 的纯 Python 回退实现_fallback。因此即使两个 AES 依赖都没安装pypdf 仍能完成 RC4 加解密——这也解释了为什么文档说RC4 使用普通安装即可。进阶加密时控制文档权限encrypt()的permissions_flag参数允许在加密的同时限制用户密码持有者的操作范围。pypdf 在 pypdf/constants.py 中定义了UserAccessPermissions位标志对应 PDF 1.7 规范 Table 3.20权限标志位值含义PRINT4允许打印MODIFY8允许修改内容EXTRACT16允许提取文本与图形ADD_OR_MODIFY32允许添加或修改批注、填写表单FILL_FORM_FIELDS256允许填写表单字段EXTRACT_TEXT_AND_GRAPHICS512允许提取文本和图形辅助功能场景ASSEMBLE_DOC1024允许组装文档插入、旋转、删除页面PRINT_TO_REPRESENTATION2048允许以低分辨率表示形式打印这些标志可以按位或|组合使用。注意权限只约束用户密码持有者持有所有者密码owner password的用户始终拥有全部权限并且权限标志在 PDF 标准中属于软性限制多数阅读器会尊重但并非强制的 DRM 手段。权限位实际写入加密字典的/P条目见 pypdf/_encryption.py 的write_entry并在解密时通过/Perms校验其完整性。注意事项小结默认算法是 RC4-128省略algorithm参数即回退 RC4出于安全考虑请始终显式传入AES-256-R5或AES-256。AES 依赖必须安装处理 AES 前先执行pip install pypdf[crypto]。is_encrypted在解密后仍为True它只反映文件原本是否带加密字典不代表当前解密状态。密码错误不抛异常decrypt()返回PasswordType.NOT_DECRYPTED表示失败建议检查返回值。增量更新的 PDF 不能加密incrementalTrue时调用encrypt()会直接报错。非 ASCII 口令注意编码V4 算法优先按 Latin-1 编码口令失败才退回 UTF-8V5 算法则严格走 SASLprep UTF-8pypdf/_encryption.py若口令含特殊字符建议使用 ASCII 字符以确保跨阅读器兼容。通过本文的加密、解密实战与源码级剖析你可以放心地在项目中引入 pypdf 处理受保护 PDF加密时选对算法与权限位解密时正确处理返回值遇到 AES 文件先装好crypto可选依赖——这三步足以覆盖绝大多数生产场景。【免费下载链接】pypdfA pure-python PDF library capable of splitting, merging, cropping, and transforming the pages of PDF files项目地址: https://gitcode.com/GitHub_Trending/py/pypdf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表