如何在Python中加密和解密PDF文件?代码实现示例

2021年11月17日03:33:28 发表评论 1,743 次浏览

了解如何使用 PyPDF4 库为 PDF 文件添加和删除密码,以及如何使用 pyAesCrypt 在 Python 中加密和解密 PDF 文件。

如何在Python中加密和解密PDF文件?你想要加密 PDF 文件的目的有很多,其中之一是阻止某人将你的 PDF 复制到他们的计算机并使其只能使用解密密钥使用。使用加密的 PDF 文件,你可以防止不受欢迎的人查看 PDF 文件中的个人或凭据信息。

Python如何加密和解密PDF文件?在本教程中,你将学习如何通过应用两个保护级别来加密 PDF 文件:

  • 级别 1:通过添加文档打开密码来限制对 PDF 文件的访问。文档打开密码(也称为用户密码)需要用户输入密码才能打开 PDF。
  • 级别 2:使用pyAesCrypt 库和使用AES256-CBC加密算法加密文件。

Python加密和解密PDF文件示例:本教程的目的是通过基于 Python 的模块开发一个轻量级的基于命令行的实用程序,而不依赖于 Python 生态系统之外的外部实用程序(例如qpdf),以保护 Python 中的 PDF 文件。

在开始之前,让我们安装所需的库:

$ pip install PyPDF4==1.27.0 pyAesCrypt==6.0.0

让我们在 Python 文件中导入必要的库:

# Import Libraries
from PyPDF4 import PdfFileReader, PdfFileWriter, utils
import os
import argparse
import getpass
from io import BytesIO
import pyAesCrypt

首先,让我们定义一个函数来检查 PDF 文件是否被加密:

# Size of chunck
BUFFER_SIZE = 64*1024

def is_encrypted(input_file: str) -> bool:
    """Checks if the inputted file is encrypted using PyPDF4 library"""
    with open(input_file, 'rb') as pdf_file:
        pdf_reader = PdfFileReader(pdf_file, strict=False)
        return pdf_reader.isEncrypted

其次,让我们制作核心功能,即加密PDF文件:

def encrypt_pdf(input_file: str, password: str):
    """
    Encrypts a file using PyPDF4 library.
    Precondition: File is not encrypted.
    """
    pdf_writer = PdfFileWriter()
    pdf_reader = PdfFileReader(open(input_file, 'rb'), strict=False)
    if pdf_reader.isEncrypted:
        print(f"PDF File {input_file} already encrypted")
        return False, None, None
    try:
        # To encrypt all the pages of the input file, you need to loop over all of them
        # and to add them to the writer.
        for page_number in range(pdf_reader.numPages):
            pdf_writer.addPage(pdf_reader.getPage(page_number))
    except utils.PdfReadError as e:
        print(f"Error reading PDF File {input_file} = {e}")
        return False, None, None
    # The default is 128 bit encryption (if false then 40 bit encryption).
    pdf_writer.encrypt(user_pwd=password, owner_pwd=None, use_128bit=True)
    return True, pdf_reader, pdf_writer

如何在Python中加密和解密PDF文件?该encrypt_pdf()函数执行以下操作:

  • 它验证输入的 PDF 文件未使用PyPDF4库加密。
  • 它遍历它的页面并将它们添加到一个pdf_writer对象中。
  • pdf_writer使用给定的密码加密对象。

现在我们有了负责加密的函数,让我们做相反的事情,那就是解密:

def decrypt_pdf(input_file: str, password: str):
    """
    Decrypts a file using PyPDF4 library.
    Precondition: A file is already encrypted
    """
    pdf_reader = PdfFileReader(open(input_file, 'rb'), strict=False)
    if not pdf_reader.isEncrypted:
        print(f"PDF File {input_file} not encrypted")
        return False, None, None
    pdf_reader.decrypt(password=password)
    pdf_writer = PdfFileWriter()
    try:
        for page_number in range(pdf_reader.numPages):
            pdf_writer.addPage(pdf_reader.getPage(page_number))
    except utils.PdfReadError as e:
        print(f"Error reading PDF File {input_file} = {e}")
        return False, None, None
    return True, pdf_reader, pdf_writer

该函数执行以下操作:

  • 它验证输入的 PDF 文件是否使用PyPDF4库加密。
  • pdf_reader使用密码(必须是正确的)解密对象。
  • 它遍历它的页面并将它们添加到一个pdf_writer对象中。

让我们进入第 2 级,加密实际文件:

def cipher_stream(inp_buffer: BytesIO, password: str):
    """Ciphers an input memory buffer and returns a ciphered output memory buffer"""
    # Initialize output ciphered binary stream
    out_buffer = BytesIO()
    inp_buffer.seek(0)
    # Encrypt Stream
    pyAesCrypt.encryptStream(inp_buffer, out_buffer, password, BUFFER_SIZE)
    out_buffer.seek(0)
    return out_buffer

通过使用pyAesCrypt库,上述函数加密输入内存缓冲区并返回加密的内存缓冲区作为输出。

现在让我们制作文件解密功能:

def decipher_file(input_file: str, output_file: str, password: str):
    """
    Deciphers an input file and returns a deciphered output file
    """
    inpFileSize = os.stat(input_file).st_size
    out_buffer = BytesIO()
    with open(input_file, mode='rb') as inp_buffer:
        try:
            # Decrypt Stream
            pyAesCrypt.decryptStream(
                inp_buffer, out_buffer, password, BUFFER_SIZE, inpFileSize)
        except Exception as e:
            print("Exception", str(e))
            return False
        inp_buffer.close()
    if out_buffer:
        with open(output_file, mode='wb') as f:
            f.write(out_buffer.getbuffer())
        f.close()
    return True

以上Python加密和解密PDF文件示例中的decipher_file(),我们使用pyAesCrypt模块中的decryptStream()方法,它接受输入和输出缓冲区、密码、缓冲区大小和文件大小作为参数,并将解密后的流写入输出缓冲区。

Python如何加密和解密PDF文件?为了更方便地使用文件的加解密,建议大家阅读本教程,该教程使用了对Python开发者更友好的加密模块。

现在让我们将我们的功能合并为一个:

def encrypt_decrypt_file(**kwargs):
    """Encrypts or decrypts a file"""
    input_file = kwargs.get('input_file')
    password = kwargs.get('password')
    output_file = kwargs.get('output_file')
    action = kwargs.get('action')
    # Protection Level
    # Level 1 --> Encryption / Decryption using PyPDF4
    # Level 2 --> Encryption and Ciphering / Deciphering and Decryption
    level = kwargs.get('level')
    if not output_file:
        output_file = input_file
    if action == "encrypt":
        result, pdf_reader, pdf_writer = encrypt_pdf(
            input_file=input_file, password=password)
        # Encryption completed successfully
        if result:
            output_buffer = BytesIO()
            pdf_writer.write(output_buffer)
            pdf_reader.stream.close()
            if level == 2:
                output_buffer = cipher_stream(output_buffer, password=password)
            with open(output_file, mode='wb') as f:
                f.write(output_buffer.getbuffer())
            f.close()
    elif action == "decrypt":
        if level == 2:
            decipher_file(input_file=input_file,
                          output_file=output_file, password=password)
        result, pdf_reader, pdf_writer = decrypt_pdf(
            input_file=input_file, password=password)
        # Decryption completed successfully
        if result:
            output_buffer = BytesIO()
            pdf_writer.write(output_buffer)
            pdf_reader.stream.close()
            with open(output_file, mode='wb') as f:
                f.write(output_buffer.getbuffer())
            f.close()

如何在Python中加密和解密PDF文件?上面的函数接受 5 个关键字参数:

  • input_file:输入的PDF文件。
  • output_file:输出PDF文件。
  • password:要加密的密码字符串。
  • action:接受“加密”或“解密”操作作为字符串。
  • level:你要使用哪种级别的加密。将其设置为1仅在打开 PDF 文件时2添加密码,添加文件加密作为另一层安全性。

现在,让我们创建一个新类,该类继承自argparse.Action以安全输入密码:

class Password(argparse.Action):
    """
    Hides the password entry
    """
    def __call__(self, parser, namespace, values, option_string):
        if values is None:
            values = getpass.getpass()
        setattr(namespace, self.dest, values)

它覆盖 __call__()方法并将对象的dest变量设置为namespace用户使用getpass模块输入的密码。

接下来,让我们定义解析命令行参数的函数:

def is_valid_path(path):
    """Validates the path inputted and checks whether it is a file path or a folder path"""
    if not path:
        raise ValueError(f"Invalid Path")
    if os.path.isfile(path):
        return path
    elif os.path.isdir(path):
        return path
    else:
        raise ValueError(f"Invalid Path {path}")

def parse_args():
    """Get user command line parameters"""
    parser = argparse.ArgumentParser(description="These options are available")
    parser.add_argument("file", help="Input PDF file you want to encrypt", type=is_valid_path)
    # parser.add_argument('-i', '--input_path', dest='input_path', type=is_valid_path,
    #                     required=True, help="Enter the path of the file or the folder to process")
    parser.add_argument('-a', '--action', dest='action', choices=[
                        'encrypt', 'decrypt'], type=str, default='encrypt', help="Choose whether to encrypt or to decrypt")
    parser.add_argument('-l', '--level', dest='level', choices=[
                        1, 2], type=int, default=1, help="Choose which protection level to apply")
    parser.add_argument('-p', '--password', dest='password', action=Password,
                        nargs='?', type=str, required=True, help="Enter a valid password")
    parser.add_argument('-o', '--output_file', dest='output_file',
                        type=str, help="Enter a valid output file")
    args = vars(parser.parse_args())
    # To Display Command Arguments Except Password
    print("## Command Arguments #################################################")
    print("\n".join("{}:{}".format(i, j)
          for i, j in args.items() if i != 'password'))
    print("######################################################################")
    return args

最后,编写主要Python加密和解密PDF文件示例代码:

if __name__ == '__main__':
    # Parsing command line arguments entered by user
    args = parse_args()
    # Encrypting or Decrypting File
    encrypt_decrypt_file(
        input_file=args['file'], password=args['password'], 
        action=args['action'], level=args['level'], output_file=args['output_file']
    )

好的,让我们测试我们的程序。首先,让我们通过--help看看参数:

$ python encrypt_pdf.py --help

输出:

usage: encrypt_pdf.py [-h] [-a {encrypt,decrypt}] [-l {1,2}] -p [PASSWORD] [-o OUTPUT_FILE] file

These options are available

positional arguments:
  file                  Input PDF file you want to encrypt

optional arguments:
  -h, --help            show this help message and exit
  -a {encrypt,decrypt}, --action {encrypt,decrypt}
                        Choose whether to encrypt or to decrypt
  -l {1,2}, --level {1,2}
                        Choose which protection level to apply
  -p [PASSWORD], --password [PASSWORD]
                        Enter a valid password
  -o OUTPUT_FILE, --output_file OUTPUT_FILE
                        Enter a valid output file

太棒了,让我们加密一个示例 PDF 文件(在此处获取):

$ python encrypt_pdf.py bert-paper.pdf -a encrypt -l 1 -p -o bert-paper-encrypted1.pdf

这将提示输入两次密码:

Password: 
Password:
## Command Arguments #################################################
file:bert-paper.pdf
action:encrypt
level:1
output_file:bert-paper-encrypted1.pdf
######################################################################

Python如何加密和解密PDF文件?使用密码保护的新 PDF 文件将出现在当前工作目录中,如果你尝试使用任何 PDF 阅读器程序打开它,你将收到密码提示,如下图所示:

如何在Python中加密和解密PDF文件?代码实现示例

显然,如果你输入错误的密码,你将无法访问 PDF 文件。

如何在Python中加密和解密PDF文件?接下来,让我们现在解密它:

$ python encrypt_pdf.py bert-paper-encrypted1.pdf -a decrypt -p -l 1 -o bert-paper-decrypted1.pdf

输出:

Password: 
## Command Arguments #################################################
file:bert-paper-encrypted1.pdf
action:decrypt
level:1
output_file:bert-paper-decrypted1.pdf
######################################################################

太棒了,你会注意到bert-paper-decrypted1.pdf出现在你的目录中,它与原始(未加密)等效。

结论

综合以上的Python加密和解密PDF文件示例,请注意,如果你选择级别 2,则整个文件将被加密,因此你需要对其进行两次解密,首先使用级别 2,然后使用级别 1。

你需要注意通过添加文档打开密码锁定 PDF 文件可以使用多种方法绕过,其中之一是破解 PDF 密码,请查看本教程以了解如何操作。

木子山

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: