Статья Шифрование-дешифровка документа PDF с помощью Python

Наверное многие сталкивались с паролями, которые устанавливаются на фалы PDF. Некоторые из файлов защищены таким образом от редактирования, а на некоторых пароль устанавливается на открытие. Конечно же, если вы используете специализированную программу для создания документов данного формата, то установка пароля там уже будет доступна. Но, бывают случаи, когда просто делаешь документ в Word или Libre Office, сохраняешь как PDF. В этом случае, вместо использования программ, можно установить пароль с помощью Python.

000.jpg


Что понадобиться?

Для работы с PDF понадобиться установка библиотеки PyPDF2. Для ее установки набираем в терминале команду:

pip install PyPDF2

Так же понадобиться библиотека os, а точнее ее модуль path, который в данном случае используется для проверки правильности ввода пути к файлу. После того, как библиотека установлена, импортируем из библиотеки PyPDF2: PdfFileWriter, PdfFileReader, PdfReadError. Блок импорта со всем используемым в данном скрипте.

Python:
import os.path

# pip install PyPDF2
from PyPDF2 import PdfFileWriter
from PyPDF2 import PdfFileReader
from PyPDF2.errors import PdfReadError

Для начала создадим функцию установки пароля, то есть шифрования PDF документа. Я назову ее encrypt_pdf(pdf_path, pswd). На входе она принимает путь к файлу PDF, а также пароль, который будет использоваться для шифрования, а также дешифровки. У метода encrypt() есть два параметра. Это user_pwd, с помощью которого устанавливается пароль пользователя. Пользовательский пароль позволяет открывать и читать файлы. И параметр owner_pwd, с помощью которого устанавливается пароль владельца. Данный пароль позволяет работать с файлом PDF без ограничений. Если же, как в данном случае, устанавливается только один пароль, то есть user_pwd, параметр owner_pwd, если он не установлен в коде, становиться равным user_pwd. То есть, пароль пользователя и владельца будут равны.

Установим для начала имя файла, чтобы не запрашивать его постоянно у пользователя. Я сделал его разбиение на части равные пути и имени файла, после чего разделил имя файла с расширением, добавил суффикс _enc и восстановил расширение обратно. И создадим новый экземпляр класса PdfFileWriter.

Python:
encrypt_file_name = f'{os.path.split(pdf_path)[1].split(".")[0]}_enc.pdf'
pdf_enc_writer = PdfFileWriter()

Теперь в цикле, с помощью экземпляра класса PdfFileReader прочитаем все страницы документа и передадим их в экземпляр pdf_enc_writer для записи. Попутно будем информировать пользователя о загрузке страниц документа.

Python:
    for page in range(PdfFileReader(pdf_path).numPages):
        pdf_enc_writer.addPage(PdfFileReader(pdf_path).getPage(page))
        print(f'[+] Добавлена страница {page+1}')

Зашифруем с помощью полученного от пользователя пароля созданный документ и запишем в файл с именем, которое было определено в начале функции. После чего выведем в терминал принт об установке пароля на файл.

Python:
pdf_enc_writer.encrypt(pswd)
    with open(encrypt_file_name, 'wb') as file:
        pdf_enc_writer.write(file)

print(f'[+] Пароль на файл {encrypt_file_name} установлен')

Теперь, при открытии документа будет запрашиваться пароль, без которого ни открытие, ни редактирование будут невозможны. С точки зрения безопасности пароль на чтение и редактирование желательно устанавливать раздельный. Но, в данном случае и на чтение, и на редактирование пароль один.

Теперь создадим функцию дешифровки, то есть снятия пароля с документа. Так как в функции шифрования, установки пароля на документ, мы использовали один пароль, который используется как на чтение, так и на редактирование, то соответственно, функция и будет запрашивать его один. В случае, если вы установите разные пароли, то вам нужно будет при дешифровке указать именно тот пароль, снятие которого вы хотите произвести.

Создаю функцию decrypt_pdf(pdf_path, pswd). На входе она принимает путь к зашифрованному файлу и пароль для расшифровки. В данном случае, я на всякий случай проверяю, нет ли суффикса _enc в имени файла, чтобы убрать его, если он есть. В соответствии с этим и устанавливаю имя файла, с которым будет сохранен дешифрованный документ. Далее создаем экземпляры классов PdfFileWriter и PdfFileReader.

Python:
    if '_enc' in os.path.split(pdf_path)[1].split(".")[0]:
        decrypt_file_name = f'{os.path.split(pdf_path)[1].split(".")[0].split("_")[0]}_dec.pdf'
    else:
        decrypt_file_name = f'{os.path.split(pdf_path)[1].split(".")[0]}_dec.pdf'
    pdf_dec_writer = PdfFileWriter()
    pdf_dec_reader = PdfFileReader(pdf_path)

Проверяем, есть ли на документе пароль. Если есть, дешифруем его, если же пароля нет, сообщаем об этом пользователю.

Python:
    if pdf_dec_reader.isEncrypted:
        pdf_dec_reader.decrypt(pswd)
    else:
        print('[+] На файле нет пароля')
        return

Теперь создаем цикл, в котором добавляем страницы в объект pdf_dec_writer. В данном случае я заключил код в try-except для того, чтобы обработать ошибку чтения. В случае, если пользователь ввел неверный пароль для дешифрования, скрипт не сможет прочитать документ. Поэтому возникнет исключение, в котором нужно сообщить пользователю о том, что он ввел неверный пароль, запустить снова функцию main, чтобы не выходить совсем из программы и завершить работу функции.

Python:
    try:
        for page in range(pdf_dec_reader.numPages):
            pdf_dec_writer.addPage(pdf_dec_reader.getPage(page))
            print(f'[+] Добавлена страница {page+1}')
    except PdfReadError:
        print('\t[-] Вы ввели неправильный пароль\n')
        main()
        return

В случае, если ошибок чтения документа не возникло, записываем полученные страницы в новый, расшифрованный файл, после чего сообщаем пользователю, что пароль с документа снят.

Python:
    with open(decrypt_file_name, 'wb') as file:
        pdf_dec_writer.write(file)

    print(f'[+] Пароль с файла {decrypt_file_name} снят')

Теперь создадим функцию main(), в которой будем обрабатывать пользовательский ввод. Независимо от того, что выбрал пользователь, кроме выхода, проверяем путь до нужного файла. И если файла не существует сообщаем об этом пользователю с просьбой ввести путь повторно. Если же все хорошо и файл существует, запрашиваем пароль у пользователя. Ну и если пользователь ввел что-то, что отличается от нужного, то выводим об этом сообщение.

Python:
def main():
    user_choise = input('[+] Выберите действие:\n\t[1] Установить пароль\n\t[2] Снять пароль\n\t[3] Выход\n\t>>> ')
    if user_choise == "1":
        while not os.path.isfile(pdf_path := input('\t[+] Введите путь к файлу PDF: ')):
            print('\t[-] По указанному пути файла не существует!')
        pswd = input('\t[+] Введите пароль для установки: ')
        encrypt_pdf(pdf_path, pswd)
    elif user_choise == "2":
        while not os.path.isfile(pdf_path := input('\t[+] Введите путь к файлу PDF: ')):
            print('\t[-] По указанному пути файла не существует!')
        pswd = input('\t[+] Введите пароль PDF: ')
        decrypt_pdf(pdf_path, pswd)
    elif user_choise == "3":
        exit(0)
    else:
        print('\n[+] ВВЕДЕННЫЕ ДАННЫЕ МНЕ НЕ ПОНЯТНЫ. ПОВТОРИТЕ ВСЕ СНАЧАЛА!\n')
        main()

Вот и все. С помощью данного, небольшого, кода можно легко установить и снять пароль документа PDF. Единственное и важное требование, это знание пароля для чтения или для редактирования. Здесь не рассматривается брут пароля. Так как при достаточно большом пароле это может занять очень продолжительное время. А перебор по словарям не всегда находит требуемое. Тут как не перебирай пароль, нужно все равно проверить, дешифруется ли с его помощью документ. А на это требуется хоть и небольшое, но время.

Python:
import os.path

# pip install PyPDF2
from PyPDF2 import PdfFileWriter
from PyPDF2 import PdfFileReader
from PyPDF2.errors import PdfReadError


# установка пароля на файл
# получение имени файла, создание объекта писателя
# добавление в цикле станиц в писателя, установка пароля
# сохранение файла
def encrypt_pdf(pdf_path, pswd):
    encrypt_file_name = f'{os.path.split(pdf_path)[1].split(".")[0]}_enc.pdf'
    pdf_enc_writer = PdfFileWriter()

    for page in range(PdfFileReader(pdf_path).numPages):
        pdf_enc_writer.addPage(PdfFileReader(pdf_path).getPage(page))
        print(f'[+] Добавлена страница {page+1}')

    pdf_enc_writer.encrypt(pswd)
    with open(encrypt_file_name, 'wb') as file:
        pdf_enc_writer.write(file)

    print(f'[+] Пароль на файл {encrypt_file_name} установлен')


# снятие пароля с файла
# получение имени файла, создание объекта писателя
# создание объекта чтеца, проверка, есть ли пароль на файле
# добавление в цикле страниц документа в писателя
# блок try-except для проверки правильности пароля, сохранение файла
def decrypt_pdf(pdf_path, pswd):
    if '_enc' in os.path.split(pdf_path)[1].split(".")[0]:
        decrypt_file_name = f'{os.path.split(pdf_path)[1].split(".")[0].split("_")[0]}_dec.pdf'
    else:
        decrypt_file_name = f'{os.path.split(pdf_path)[1].split(".")[0]}_dec.pdf'
    pdf_dec_writer = PdfFileWriter()
    pdf_dec_reader = PdfFileReader(pdf_path)

    if pdf_dec_reader.isEncrypted:
        pdf_dec_reader.decrypt(pswd)
    else:
        print('[+] На файле нет пароля')
        return

    try:
        for page in range(pdf_dec_reader.numPages):
            pdf_dec_writer.addPage(pdf_dec_reader.getPage(page))
            print(f'[+] Добавлена страница {page+1}')
    except PdfReadError:
        print('\t[-] Вы ввели неправильный пароль\n')
        main()
        return

    with open(decrypt_file_name, 'wb') as file:
        pdf_dec_writer.write(file)

    print(f'[+] Пароль с файла {decrypt_file_name} снят')


# получаем путь к файлу и пароль
# проверяем указанный путь к файлу
# проверяем пользовательский выбор
def main():
    user_choise = input('[+] Выберите действие:\n\t[1] Установить пароль\n\t[2] Снять пароль\n\t[3] Выход\n\t>>> ')
    if user_choise == "1":
        while not os.path.isfile(pdf_path := input('\t[+] Введите путь к файлу PDF: ')):
            print('\t[-] По указанному пути файла не существует!')
        pswd = input('\t[+] Введите пароль для установки: ')
        encrypt_pdf(pdf_path, pswd)
    elif user_choise == "2":
        while not os.path.isfile(pdf_path := input('\t[+] Введите путь к файлу PDF: ')):
            print('\t[-] По указанному пути файла не существует!')
        pswd = input('\t[+] Введите пароль PDF: ')
        decrypt_pdf(pdf_path, pswd)
    elif user_choise == "3":
        exit(0)
    else:
        print('\n[+] ВВЕДЕННЫЕ ДАННЫЕ МНЕ НЕ ПОНЯТНЫ. ПОВТОРИТЕ ВСЕ СНАЧАЛА!\n')
        main()


if __name__ == "__main__":
    main()

А на этом, пожалуй, все.

Спасибо за внимание. Надеюсь, что данная информация будет кому-нибудь полезна
 
Мы в соцсетях: