В предыдущей статье я рассказывал про шифрование в Python с помощью библиотек Cryptography и PyCryptodome. Сегодня продолжим и поговорим про алгоритм шифрования AES.
Еще по теме: Шифрование с помощью DES Python
Шифрование в Python с помощью алгоритма AES
Advanced Encryption Standard (AES) — это алгоритм блочного шифрования, принятый в качестве стандарта шифрования. Размер каждого блока алгоритма AES составляет 128 бит, а ключ может быть длиной 128, 192 или 256 бит.
Среди режимов шифрования можно выделить следующие:
- Cipher-block chaining (CBC) — в этом режиме каждый блок открытого текста применяется с операцией XOR к предыдущему блоку шифртекста перед шифрованием. Таким образом, каждый блок шифртекста зависит от всего открытого текста, обработанного до этого момента. При работе с этим режимом, чтобы сделать каждое сообщение уникальным, мы обычно используем вектор инициализации (IV).
- Electronic Code Book (ECB) — в этом режиме сообщения разделяются на блоки, и каждый из них шифруется отдельно с использованием одного и того же ключа. Недостатком этого метода является то, что идентичные блоки открытого текста могут соответствовать блокам идентичного шифртекста, поэтому можно распознать эти паттерны и обнаружить открытый текст из шифртекста. Таким образом, его использование в настоящее время в качестве режима шифрования не рекомендуется.
- Galois/Counter Mode (GCM) — это режим операции, используемый в блочных шифрах с размером блока 128 бит. AES-GCM стал очень популярным благодаря хорошей производительности и возможности использования улучшений аппаратного ускорения в процессорах. Кроме того, благодаря использованию вектора инициализации, мы можем случайным образом генерировать ключи для улучшения процесса шифрования двух сообщений с одним ключом.
Для использования алгоритма шифрования, такого как AES, необходимо импортировать его из Crypto.Cipher.AES. Так как API шифрования на уровне блока в Pycryptodome очень низкоуровневый, он принимает только ключи длиной 16, 24 или 32 байта для AES-128, AES-196 и AES-256 соответственно. Чем длиннее ключ, тем надежнее шифрование.
Таким образом, нужно убедиться, что длина данных кратна 16 байтам. Наш ключ AES должен быть длиной 16, 24 или 32 байта, а вектор инициализации (IV) должен быть длиной 16 байт. Он будет сгенерирован с использованием модулей random и string.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
from Crypto.Cipher import AES import binascii, os import random, string key = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(16)) print('Key:', key) encryptor = AES.new(key.encode("utf8"), AES.MODE_CBC, 'Это IV-12'.encode("utf8")) decryptor = AES.new(key.encode("utf8"), AES.MODE_CBC, 'Это IV-12'.encode("utf8")) def aes_encrypt(plaintext): ciphertext = encryptor.encrypt(plaintext) return ciphertext def aes_decrypt(ciphertext): plaintext = decryptor.decrypt(ciphertext) return plaintext encrypted = aes_encrypt('Это секретное сообщение '.encode("utf8")) decrypted = aes_decrypt(encrypted) print("Encrypted message:", encrypted) print("Decrypted message:", decrypted.decode()) |
В данном скрипте мы шифруем данные с использованием AES. Поэтому первое, что мы делаем, это импортируем AES. AES.new() представляет собой конструктор метода для инициализации алгоритма AES и принимает три параметра: ключ шифрования, режим шифрования и IV.
Для шифрования сообщения, для открытого текста используем метод encrypt(), а для дешифрования текста — метод decrypt().
Мы можем улучшить предыдущий скрипт, сгенерировав вектор инициализации с использованием модуля random и ключ с использованием PBKDF2, который позволяет генерировать случайный ключ из случайного числа, называемого «солью», размером ключа и количеством итераций.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
from Crypto.Cipher import AES from Crypto.Protocol.KDF import PBKDF2 from Crypto import Random # ключ должен быть длиной 16, 24 или 32 байта key = "secret-key-12345" iterations = 10000 key_size = 16 salt = Random.new().read(размер_ключа) iv = Random.new().read(AES.block_size) derived_key = PBKDF2(key, salt, key_size, iterations) encrypt_AES = AES.new(derived_key, AES.MODE_CBC, iv) # Заполнение пробелами пользователя до 32 символов message = "This is the secret message ".encode("utf8") ciphertext = encrypt_AES.encrypt(message) print("Cipher text:", ciphertext) decrypt_AES = AES.new(derived_key, AES.MODE_CBC, iv) message_decrypted = decrypt_AES.decrypt(ciphertext) print("Decrypted text:", message_decrypted.strip().decode()) |
Здесь мы используем алгоритм PBKDF2 для генерации случайного ключа, который мы будем использовать для шифрования и дешифрования. Переменная ciphertext относится к результату зашифрованных данных, а message_decrypted — к результату расшифрованных данных.
Мы также видим, что алгоритм PBKDF2 требует альтернативной «соли» и количества итераций. Случайное значение соли предотвращает атаку методом перебора ключей, и оно должно быть сохранено вместе с хешем пароля, рекомендуется использовать одну «соль» на каждый пароль.
Что касается количества итераций, рекомендуется использовать большое число, чтобы сделать процесс расшифровки при возможной атаке более сложным.
Другая возможность, предлагаемая алгоритмом AES, — это шифрование файлов с использованием блоков данных, также известных как фрагменты или порции.
Шифрование файлов с помощью AES Python
Для шифрования методом AES требуется, чтобы каждый блок был кратен 16 байтам. Поэтому мы читаем, шифруем и записываем данные частями. Размер должен быть кратен 16. Следующий код шифрует и дешифрует файл, выбранный пользователем.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
def encrypt_file(key, filename): chunk_size = 64 * 1024 output_filename = filename + '.encrypted' # Случайный вектор инициализации iv = Random.new().read(AES.block_size) # Создание шифра для шифрования encryptor = AES.new(key, AES.MODE_CBC, iv) # Определение размера файла filesize = os.path.getsize(filename) # Открытие выходного файла и запись размера файла with open(filename, 'rb') as inputfile: with open(output_filename, 'wb') as outputfile: outputfile.write(struct.pack('<Q', filesize)) outputfile.write(iv) while True: chunk = inputfile.read(chunk_size) if len(chunk) == 0: break elif len(chunk) % 16 != 0: chunk += bytes(' ', 'utf-8') * (16 - len(chunk) % 16) outputfile.write(encryptor.encrypt(chunk)) |
В скрипте мы определяем функцию для шифрования файла с использованием алгоритма AES.
Сначала мы инициализируем вектор инициализации и метод шифрования AES. Затем мы читаем файл блоками, кратными 16 байтам, с целью шифрования файла частями.
Для дешифрования, чтобы расшифровать файл с использованием AES, нужно перевернуть предыдущий процесс:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
def decrypt_file(key, filename): chunk_size = 64 * 1024 output_filename = os.path.splitext(filename)[0] # Открытие зашифрованного файла и чтение размера файла и вектора инициализации. # Вектор инициализации необходим для создания шифра. with open(filename, 'rb') as infile: origsize = struct.unpack('<Q', infile.read(struct.calcsize('Q')))[0] iv = infile.read(16) # Создание шифра с использованием ключа и вектора инициализации. decryptor = AES.new(key, AES.MODE_CBC, iv) # Также записываем расшифрованные данные в файл для проверки результатов шифрования # и дешифрования путем сравнения с оригинальным файлом. with open(output_filename, 'wb') as outfile: while True: chunk = infile.read(chunk_size) if len(chunk) == 0: break outfile.write(decryptor.decrypt(chunk)) outfile.truncate(origsize) |
Это код определяет функцию для дешифрования файла с использованием алгоритма AES.
Сначала открываем зашифрованный файл и читаем размер файла и вектор инициализации. Затем записываем расшифрованные данные в файл для проверки результатов шифрования.
Следующий код представляет нашу основную функцию, которая предоставляет возможность шифровать или дешифровать содержимое файла:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import getpass def main(): choice = input("do you want to (E)ncrypt or (D)ecrypt?: ") if choice == 'E': filename = input(''file to encrypt: ') password = getpass.getpass() encrypt_file(getKey(password.encode("utf8")), filename) print('done.') elif choice == 'D': filename = input('file to decrypt: ') password = getpass.getpass() decrypt_file(getKey(password.encode("utf8")), filename) print('done.') else: print('no option selected.') if __name__ == "__main__": main() |
Это будет вывод предыдущего сценария, где у нас есть возможность выбора шифрования и дешифрования файла.
Результат предыдущего скрипта при шифровании файла приведет к созданию файла file.txt.encrypted, содержащего тот же контент, что и оригинальный файл но с зашифрованной информацией.
Заключение
Алгоритм AES предоставляет надежное и эффективное средство для шифрования данных в Python. С использованием библиотеки PyCryptodome, мы можем легко реализовать шифрование и дешифрование файлов с помощью AES. Помните, что безопасность данных — это ключевой аспект в современном мире информационных технологий, и использование надежных алгоритмов шифрования, таких как AES, способствует обеспечению этой безопасности.
В следующий раз продолжим и будем изучать алгоритм шифрования RSA, который, для шифрования и дешифрования использует асимметричную схему с публичным ключом.
ПОЛЕЗНЫЕ ССЫЛКИ: