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

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

000.jpg

На самом деле дело не ограничивается только лишь шифрованием файлов. И так или иначе, но все мы сталкиваемся с различными видами шифрования практически каждый день. Например, при посещении сайтов с протоколом 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 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


Обработка пользовательского выбора

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

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()

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

Вложения

  • gen_enc_dec_rsa_aes.zip
    1,4 КБ · Просмотры: 32

MLNK

Mod. Ethical Hacking
Red Team
23.01.2018
548
698
  • Нравится
Реакции: Johan Van

Johan Van

Green Team
13.06.2020
153
280
Я ту по просьбе человек писал имитатор рансома, чтобы SOC потестить. мб тебе будет интересно. Говно код конечно написанный на коленке, но всё же. Для шифрования использовал Salsa20(по запросу) GitHub - 0xMLNK/rsw-to-test-soc: Simple python script to test Ransomware markers in SOC

Классная программа. Простая и надежная. Если я правильно все понял, то она читает содержимое дериктории, шифрует, записывает данные о зашифрованных файлах с csv, а потом отправляет на pastebin. Я только не совсем понял, зачем тестируется блокнот? Я пока еще не особо разбираюсь в виндовых процессах. Понимаю, что ищется процесс по имени, его pid и прочие параметры. А вот дальше слабовато понимаю. Надо разбираться )) Спасибо за код. Интересно почитать!

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

Johan Van

Green Team
13.06.2020
153
280
Я ту по просьбе человек писал имитатор рансома, чтобы SOC потестить. мб тебе будет интересно. Говно код конечно написанный на коленке, но всё же. Для шифрования использовал Salsa20(по запросу) GitHub - 0xMLNK/rsw-to-test-soc: Simple python script to test Ransomware markers in SOC

Был у меня случай на работе, где работал давно уже, в то время когда шифровальщики только начали зверствовать. Никто ведь толком ничего не знал. И на одном из компов эта зараза словилась и все зашифровала. Да не на каком-то не важном. А у ведущего экономиста. ))) Это было занимательно восстанавливать данные, которые у нее были. Так до конца и не восстановили. Благо, она не особо в них нуждалась. А шифровальщик денег просил. Да еще и в битках. А где их взять простым сисадминам :LOL: Ну, а организации было на битки тоже не особо перпендикулярно. Потому товарищ остался ни с чем, только с чувством удовлетворенного ЧСВ и зашифрованными файлами ))
 

MLNK

Mod. Ethical Hacking
Red Team
23.01.2018
548
698
Классная программа. Простая и надежная. Если я правильно все понял, то она читает содержимое дериктории, шифрует, записывает данные о зашифрованных файлах с csv, а потом отправляет на pastebin. Я только не совсем понял, зачем тестируется блокнот? Я пока еще не особо разбираюсь в виндовых процессах. Понимаю, что ищется процесс по имени, его pid и прочие параметры. А вот дальше слабовато понимаю. Надо разбираться )) Спасибо за код. Интересно почитать!

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

1 августа стартует курс «Основы программирования на Python» от команды The Codeby

Курс будет начинаться с полного нуля, то есть начальные знания по Python не нужны. Длительность обучения 2 месяца. Учащиеся получат методички, видео лекции и домашние задания. Много практики. Постоянная обратная связь с кураторами, которые помогут с решением возникших проблем.

Запись на курс до 10 августа. Подробнее ...