Python 使用 PyPDF2 实现 PDF 加密与密码破解
前言
在数据安全日益重要的今天,保护敏感文档的访问权限是基础需求。PDF 格式因其跨平台性和安全性被广泛使用,但默认情况下 PDF 文件可能不包含强加密。本文将详细介绍如何使用 Python 的 PyPDF2 库对 PDF 文件进行加密,以及如何通过暴力破解的方式恢复弱密码保护的 PDF 内容。这不仅有助于理解 PDF 加密机制,也能帮助开发者评估密码强度。
环境准备
1. 安装依赖库
首先,需要安装 PyPDF2 库。该库支持读取、写入和修改 PDF 文件。
pip install PyPDF2
安装完成后,可以通过以下命令验证版本:
python -m pip show PyPDF2
注意:PyPDF2 是一个较成熟的库,但在处理某些高级加密标准(如 AES-256)时可能存在限制。对于现代 PDF 安全需求,建议关注其后续项目 pypdf。
2. 代码结构说明
我们将实现两个核心功能:
- 加密函数:将普通 PDF 转换为带密码保护的 PDF。
- 解密函数:尝试遍历可能的密码组合以打开加密文件。
PDF 加密原理简述
PDF 加密通常基于对称加密算法(如 RC4 或 AES)。当设置密码后,文件内容会被加密,只有提供正确密钥才能解密并渲染页面。如果密码过于简单(如纯数字),攻击者可以通过穷举法(Brute Force)快速破解。
实现 PDF 加密
下面是一个完整的加密函数示例。该函数读取源文件,添加指定密码,并保存为新文件。
import PyPDF2
def encrypt_pdf(old_path, new_path, password='1234'):
"""
给 PDF 文件添加密码保护
:param old_path: 待加密文件的绝对路径
:param new_path: 加密后文件的保存路径
:param password: 设置的密码字符串
"""
try:
with open(old_path, 'rb') as pdf_file:
# 创建 PdfFileReader 对象
pdf_reader = PyPDF2.PdfFileReader(pdf_file)
# 创建 PdfFileWriter 对象
pdf_writer = PyPDF2.PdfFileWriter()
# 将所有页面添加到 Writer 中
for page_num in range(pdf_reader.numPages):
pdf_writer.addPage(pdf_reader.getPage(page_num))
# 设置密码并加密
pdf_writer.encrypt(user_password=password)
# 写入新文件
with open(new_path, 'wb') as result_pdf:
pdf_writer.write(result_pdf)
print(f'加密成功!文件已保存至:{new_path}')
return True
except Exception as e:
print(f'加密失败:{str(e)}')
return False
if __name__ == '__main__':
# 示例调用
# encrypt_pdf('input.pdf', 'output_encrypted.pdf', '8888')
pass
实现 PDF 密码破解(暴力破解)
针对弱密码场景,我们可以通过遍历所有可能的字符组合来尝试解密。本例演示如何破解 4 位纯数字密码。
1. 密码生成逻辑
我们需要生成从 0000 到 9999 的所有组合。
def generate_passwords(length=4):
"""
生成指定位数的纯数字密码列表
"""
passwords = []
start = 10**(length-1) if length > 1 else 0
end = 10**length
for i in range(start, end):
passwords.append(str(i).zfill(length))
return passwords
2. 解密与验证函数
利用 PyPDF2 提供的 decrypt() 方法验证密码有效性。
def decrypt_pdf(target_path, max_attempts=10000):
"""
尝试暴力破解 PDF 密码
:param target_path: 目标加密 PDF 路径
:param max_attempts: 最大尝试次数
""":
try:
with open(target_path, 'rb') as pdf_file:
pdf_reader = PyPDF2.PdfFileReader(pdf_file)
# 检查文件是否加密
if not pdf_reader.isEncrypted:
print('文件未加密,无需破解。')
return
print('开始尝试破解密码...')
# 遍历密码空间
for i in range(max_attempts):
pwd = str(i).zfill(4)
# 尝试解密
status = pdf_reader.decrypt(pwd)
if status > 0: # 状态大于 0 表示解密成功
print(f'破解成功!密码为:{pwd}')
# 可选:输出解密后的内容或保存副本
pdf_writer = PyPDF2.PdfFileWriter()
for pageNum in range(pdf_reader.numPages):
pdf_writer.addPage(pdf_reader.getPage(pageNum))
output_path = target_path.replace('.pdf', '_decrypted.pdf')
with open(output_path, 'wb') as out_file:
pdf_writer.write(out_file)
print(f'已保存解密文件至:{output_path}')
break
else:
# 为了减少日志噪音,可定期打印进度
if i % 1000 == 0 and i != 0:
print(f'尝试了 {i} 个密码...')
else:
print('未找到匹配的密码。')
except FileNotFoundError:
print('错误:找不到指定的 PDF 文件。')
except Exception as e:
print(f'发生未知错误:{str(e)}')
if __name__ == '__main__':
# 示例调用
# decrypt_pdf('encrypted.pdf')
pass
完整整合代码
为了方便使用,可以将上述功能整合到一个脚本中。
import PyPDF2
import os
class PDFFileManager:
def __init__(self):
self.reader = None
self.writer = None
def encrypt(self, input_path, output_path, password='1234'):
"""加密 PDF"""
if not os.path.exists(input_path):
print(f'文件不存在:{input_path}')
return
with open(input_path, 'rb') as f:
reader = PyPDF2.PdfFileReader(f)
writer = PyPDF2.PdfFileWriter()
for page in range(reader.numPages):
writer.addPage(reader.getPage(page))
writer.encrypt(password)
with open(output_path, 'wb') as f:
writer.write(f)
print(f'加密完成:{output_path}')
def brute_force_decrypt(self, input_path, max_digits=4):
"""暴力破解密码"""
if not os.path.exists(input_path):
print(f'文件不存在:{input_path}')
return
with open(input_path, 'rb') as f:
reader = PyPDF2.PdfFileReader(f)
if not reader.isEncrypted:
print('文件未加密')
return
total_combinations = 10 ** max_digits
print(f'总组合数:{total_combinations}')
for i in range(total_combinations):
pwd = str(i).zfill(max_digits)
if reader.decrypt(pwd) > 0:
print(f'成功!密码:{pwd}')
# 保存解密结果
writer = PyPDF2.PdfFileWriter()
for p in range(reader.numPages):
writer.addPage(reader.getPage(p))
out_name = input_path.replace('.pdf', '_unlocked.pdf')
with open(out_name, 'wb') as f_out:
writer.write(f_out)
return
print('破解失败')
if __name__ == '__main__':
manager = PDFFileManager()
# 测试加密
# manager.encrypt('test.pdf', 'test_encrypted.pdf', '1314')
# 测试破解
# manager.brute_force_decrypt('test_encrypted.pdf')
pass
安全分析与最佳实践
1. 密码强度风险
本教程演示的是 4 位纯数字密码的破解,耗时极短(通常在几秒内)。在实际应用中,这种级别的加密几乎无法提供任何安全保障。
- 4 位数字:10,000 种组合,毫秒级破解。
- 6 位数字:1,000,000 种组合,秒级破解。
- 8 位混合字符:约 200 亿种组合,暴力破解不再可行。
2. 法律与道德警示
- 授权原则:仅对自己拥有合法权限的文件进行加密或解密操作。
- 隐私保护:未经授权破解他人加密文件可能违反相关法律法规。
- 用途限制:本文技术仅用于学习、安全审计及数据恢复(需证明所有权)。
3. 优化建议
- 多线程加速:对于更长的密码空间,可使用 Python 的
multiprocessing模块并行计算。 - 字典攻击:结合常见密码字典(如
rockyou.txt)优先尝试,比全排列更高效。 - 升级库:考虑迁移至
pypdf库以获得更好的兼容性和性能。
总结
通过 Python 的 PyPDF2 库,我们可以轻松实现 PDF 文件的加密与解密操作。虽然暴力破解弱密码非常简单,但这恰恰提醒我们在实际工作中应设置高强度的密码策略。希望本文能帮助开发者更好地理解 PDF 安全机制,并在开发过程中采取适当的安全措施。
注:本文代码仅供参考,实际生产环境请结合具体业务需求调整。


