May 25, 2022

Шифрование файлов директории с помощью гибридного алгоритма в Python

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

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

Симметричное и ассиметричное шифрование

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

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

А что, если скрестить «бульдога с носорогом», то есть использовать оба метода при шифровании файлов? Воспользоваться так называемым гибридным шифрованием. В нашем случае будут использоваться два алгоритма: RSA и AES.

Что потребуется?

Для шифрования файлов с помощью обоих методов потребуется установить библиотеку pycryptodome. Выполним установку с помощью терминала:

pip install pycryptodome

После установки данной библиотеки импортируем нужные нам модули:

Python:

import os

from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES, PKCS1_OAEP

Генерация открытого и закрытого ключей

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

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

Полный код функции для генерации публичного и приватного ключей:

Python:

def generate_priv_pub_key():
    key = RSA.generate(2048)
    with open('private.pem', 'wb') as priv:
        priv.write(key.export_key())
    print('\n[+] Приватный ключ "private.pem" сохранен')

    with open('public.pem', 'wb') as pub:
        pub.write(key.publickey().export_key())
    print('[+] Публичный ключ "public.pem" сохранен')
    main()
    return

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

Шифрование файлов

После того, как созданы необходимые для шифрования и расшифровки ключи, можно приступать к шифрованию с помощью публичного ключа. Создадим функцию encrypt(dest), которая будет на входе получать путь к шифруемому файлу. Затем в побайтовом режиме открывается и читается файл для шифрования по указанному пути. Проверяется наличие открытого ключа. Если ключа нет, выполнение шифрование файлов прерывается, так как отсутствует ключ. В данном случае пользователя выбрасывает в основное меню с сообщением, что нужно сгенерировать ключи шифрования. Если же открытый ключ есть, он импортируется в переменную. Далее, рандомно генерируется сессионный ключ их случайных байт. Сессионный ключ шифруется с помощью сгенерированного нами ранее открытого ключа и далее шифруется уже открытый файл с помощью зашифрованного сессионного ключа алгоритмом AES. После чего сохраняется в ту же директорию, где он и находился, а оригинальный файл удаляется.

Python:

 def encrypt(dest):
    with open(dest, 'rb') as enc_file:
        data_enc = enc_file.read()

    if os.path.isfile('public.pem'):
        public_rsa = RSA.import_key(open('public.pem').read())
        session_key = get_random_bytes(16)

        # шифруем сессионный ключ открытым ключом RSA
        chips_rsa = PKCS1_OAEP.new(public_rsa)
        enc_session_key = chips_rsa.encrypt(session_key)

        # шифруем файл с сессионным ключом алгоритм AES
        chips_aes = AES.new(session_key, AES.MODE_EAX)
        chips_text, tag = chips_aes.encrypt_and_digest(data_enc)

        with open(f'{dest}.bin', 'wb') as file_out:
            for x in (enc_session_key, chips_aes.nonce, tag, chips_text):
                file_out.write(x)
        print(f'{dest} зашифрован')
        os.remove(dest)
    else:
        print('\n[+] Нет публичного ключа для шифрования. Сгенерируйте ключи.')
        main()
        return

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

Дешифровка фалов

Так как мы создали функцию для шифрования файлов, нужно создать функцию и для дешифровки. Создаем ее с названием: decrypt(dest). На входе она так же принимает путь к дешифруемому файлу. Далее проверяется наличие в папке со скриптом приватного ключа. Если ключа нет, дешифровка не производиться. В противном случае импортируется приватный ключ. Открывается на чтение зашифрованный файл, из которого считывается зашифрованный сессионный ключ. Затем он расшифровывается алгоритмом RSA с помощью приватного ключа. После чего выполняется расшифровка файлов уже сессионным ключом с помощью алгоритма AES. После расшифровки он сохраняется в ту же директорию, где находился зашифрованный файл. А зашифрованный файл в свою очередь удаляется.

Python:

def user_change_scan_dir(user_change):
    if user_change == "1":
        generate_priv_pub_key()
    elif user_change == "2":
        dir_crypt = input('\n[+] Введите директорию для шифрования: ')
        print(" ")
        while not os.path.isdir(dir_crypt):
            dir_crypt = input('\n[-] Нет такой папки\n[+] Введите директорию для шифрования: ')
        for address, dirs, files in os.walk(dir_crypt):
            for name in files:
                encrypt(os.path.join(address, name))
        main()
        return
    elif user_change == "3":
        dir_crypt = input('\n[+] Введите директорию для дешифровки: ')
        print(" ")
        while not os.path.isdir(dir_crypt):
            dir_crypt = input('\n[-] Нет такой папки\n[+] Введите директорию для дешифровки: ')
        for address, dirs, files in os.walk(dir_crypt):
            for name in files:
                decrypt(os.path.join(address, name))
        main()
        return
    elif user_change == "4":
        exit(0)
    else:
        main()
        return

Запуск скрипта

Для запуска данного скрипта служит функция main(), в которой пользователю предлагается выбрать одно из доступных действий. А затем пользовательский выбор передается в функцию его обработки.

Функция main():

Python:

def main():
    user_change = input('\n[+] Выберите действие:\n\t[1] Генерировать публичный и приватный ключ;\n'
                        '\t[2] Зашифровать файлы;\n\t[3] Дешифровать файлы;\n\t[4] Выход\n\t>>> ')
    user_change_scan_dir(user_change)

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

Данный алгоритм шифрования можно использовать даже на не особо быстрых машинах. И, как вы понимаете, область применения данного алгоритма так же широка, помимо выполнения им его основной функции – защиты ваших данных. Достаточно вспомнить бум шифровальщиков, которые шифровали файлы, если не так, то очень даже похоже. Но, это уже действия, которые совершать определенно не стоит, так как они несколько выходят за рамки закона. Но, для информации сгодится.

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

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

Python:

# pip install pycryptodome
import os

from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES, PKCS1_OAEP


def generate_priv_pub_key():
    key = RSA.generate(2048)
    with open('private.pem', 'wb') as priv:
        priv.write(key.export_key())
    print('\n[+] Приватный ключ "private.pem" сохранен')

    with open('public.pem', 'wb') as pub:
        pub.write(key.publickey().export_key())
    print('[+] Публичный ключ "public.pem" сохранен')
    main()
    return


def encrypt(dest):
    with open(dest, 'rb') as enc_file:
        data_enc = enc_file.read()

    if os.path.isfile('public.pem'):
        public_rsa = RSA.import_key(open('public.pem').read())
        session_key = get_random_bytes(16)

        # шифруем сессионный ключ открытым ключом RSA
        chips_rsa = PKCS1_OAEP.new(public_rsa)
        enc_session_key = chips_rsa.encrypt(session_key)

        # шифруем файл с сессионным ключом алгоритм AES
        chips_aes = AES.new(session_key, AES.MODE_EAX)
        chips_text, tag = chips_aes.encrypt_and_digest(data_enc)

        with open(f'{dest}.bin', 'wb') as file_out:
            for x in (enc_session_key, chips_aes.nonce, tag, chips_text):
                file_out.write(x)
        print(f'{dest} зашифрован')
        os.remove(dest)
    else:
        print('\n[+] Нет публичного ключа для шифрования. Сгенерируйте ключи.')
        main()
        return


def decrypt(dest):
    if os.path.isfile("private.pem"):
        priv_key_rsa = RSA.import_key(open("private.pem").read())
        with open(dest, "rb") as file_in:
            enc_session_key, nonce, tag, chips_text = [file_in.read(x) for x in (priv_key_rsa.size_in_bytes(), 16, 16, -1)]

        # расшифровка сессионного ключа закрытым ключом RSA
        chips_rsa = PKCS1_OAEP.new(priv_key_rsa)
        session_key = chips_rsa.decrypt(enc_session_key)

        # расшифровка данных сессионным ключом алгоритм AES
        chips_aes = AES.new(session_key, AES.MODE_EAX, nonce)
        data = chips_aes.decrypt_and_verify(chips_text, tag)
        with open(dest[:-4], "wb") as file_out:
            file_out.write(data)
        print(f'{dest} дешифрован')
        os.remove(dest)
    else:
        print('\n[+] Нет приватного ключа для дешифровки.\nСкопируйте ключ "private.pem" в папку со скриптом!')
        main()
        return


def user_change_scan_dir(user_change):
    if user_change == "1":
        generate_priv_pub_key()
    elif user_change == "2":
        dir_crypt = input('\n[+] Введите директорию для шифрования: ')
        print(" ")
        while not os.path.isdir(dir_crypt):
            dir_crypt = input('\n[-] Нет такой папки\n[+] Введите директорию для шифрования: ')
        for address, dirs, files in os.walk(dir_crypt):
            for name in files:
                encrypt(os.path.join(address, name))
        main()
        return
    elif user_change == "3":
        dir_crypt = input('\n[+] Введите директорию для дешифровки: ')
        print(" ")
        while not os.path.isdir(dir_crypt):
            dir_crypt = input('\n[-] Нет такой папки\n[+] Введите директорию для дешифровки: ')
        for address, dirs, files in os.walk(dir_crypt):
            for name in files:
                decrypt(os.path.join(address, name))
        main()
        return
    elif user_change == "4":
        exit(0)
    else:
        main()
        return


def main():
    user_change = input('\n[+] Выберите действие:\n\t[1] Генерировать публичный и приватный ключ;\n'
                        '\t[2] Зашифровать файлы;\n\t[3] Дешифровать файлы;\n\t[4] Выход\n\t>>> ')
    user_change_scan_dir(user_change)


if __name__ == "__main__":
    main()

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


Один хакер может причинить столько же вреда, сколько 10 000 солдат! Подпишись на наш Телеграм канал, чтобы узнать первым, как выжить в цифровом кошмаре!