Python шифрование: шифрование информации и создание электронных цифровых подписей с помощью пакета PyCrypto / Хабр
Шифрование и криптография в Python
Сегодня познакомимся с библиотеками PyCrypto и cryptography, научимся шифровать и расшифровывать строки при помощи двух этих библиотек.
Шифрование и криптография в Python
Хеширование
Если вам нужно защитить хэши или алгоритм дайджеста сообщений, то для этого прекрасно подойдет модуль стандартной библиотеки Python hashlib. Он включает в себя безопасные алгоритмы хеширования FIPS, такие как SHA1, SHA224, SHA256, SHA384, а также SHA512 и MD5. Python также поддерживает функции хеширования adler32 и crc32, но они содержатся в модуле zlib. Одно из самых популярны применений хеширования это хранение хеша пароля, вместо самого пароля. Конечно, хеш должен быть хорошим, в противном случае он может быть расшифрован.
Другой популярный случай, в котором применяется хеширование – это хеширование файла, с последующей отправкой файла и его хеша по отдельности. Получатель файла может запустить хеш в файле, чтобы убедиться в том, что файл соответствует отправленному хешу. Если это так, значит никто не менял файл, когда он был отправлен. Давайте попробуем создать хеш md5. Но оказывается, чтобы использовать хеш md5, нужно передать его строке байта, вместо обычной. Так что мы попробовали сделать это, после чего вызвали метод дайджеста, чтобы получить наш хеш. Если вы претпочитаете хешированный дайджест, мы можем сделать и это:
import hashlib
hashlib.md5.update(b’Python rocks!’)
result = hashlib.md5.digest()
print(result)
# b’\x14\x82\xec\x1b#d\xf6N}\x16*+[\x16\xf4w’
| import hashlib hashlib.md5.update(b’Python rocks!’) result = hashlib.md5.digest()
print(result) # b’\x14\x82\xec\x1b#d\xf6N}\x16*+[\x16\xf4w’ |
Давайте уделим время на то, чтобы разобраться с увиденным. Сначала мы импортировали модуль hashlib и создали экземпляр объекта md5 HASH. Далее, мы вписали небольшой текст в объект хеша и получили трассировку.
print( md5.hexdigest() )
# ‘1482ec1b2364f64e7d162a2b5b16f477’
| print( md5.hexdigest() ) # ‘1482ec1b2364f64e7d162a2b5b16f477’ |
На самом деле существует метод быстрого создания хеша, мы рассмотрим его, когда создадим наш хеш sha512:
import hashlib
sha = hashlib.sha1(b’Hello Python’).hexdigest()
print(sha) # ‘422fbfbc67fe17c86642c5eaaa48f8b670cbed1b’
| import hashlib sha = hashlib.sha1(b’Hello Python’).hexdigest()
print(sha) # ‘422fbfbc67fe17c86642c5eaaa48f8b670cbed1b’ |
Как вы видите, мы создали наш экземпляр хеша и вызвали его метод дайджеста одновременно. Далее, мы выводим наш хеш, чтобы на него посмотреть. Лично я использую хеш sha1, так как его хеш достаточно короткий и отлично ложится в страницу. Но в то же время он и не очень безопасный, так что вы вольны выбирать то, что вам удобно.
Вывод ключа
У Python весьма ограниченная поддержка вывода ключа, встроенная в стандартную библиотеку. Фактически, единственный метод, предлагаемый hashlib это pbkdf2_hmac, который является основанной на пароле функцией вывода ключа PKCS#5. Он использует HMAC в качестве своей псевдослучайной функцией. Вы можете использовать что-нибудь на подобии для хеширования вашего пароля, так как он поддерживает соль и итерации. Например, если вы собираетесь использовать SHA-256, вам может понадобиться соль минимум в 16 битов и 100.000 итераций. Являясь быстрой отсылкой, соль — это просто случайные данные, которые вы используете в качестве дополнения в вашем хеше, с целью усложнения расшифровки вашего пароля. В целом, она защищает ваш пароль от словарных атак и рассчитанных заранее радужных таблиц. Давайте посмотрим на пример:
import binascii
dk = hashlib.pbkdf2_hmac(hash_name=’sha256′,
password=b’bad_password34′,
salt=b’bad_salt’,
iterations=100000)
result = binascii.hexlify(dk)
print(result)
# b’6e97bad21f6200f9087036a71e7ca9fa01a59e1d697f7e0284cd7f9b897d7c02′
| import binascii
dk = hashlib.pbkdf2_hmac(hash_name=’sha256′, password=b’bad_password34′, salt=b’bad_salt’, iterations=100000)
result = binascii.hexlify(dk)
print(result) # b’6e97bad21f6200f9087036a71e7ca9fa01a59e1d697f7e0284cd7f9b897d7c02′ |
Здесь мы создаем хеш SHA256 в пароле при помощи такой-себе соли со 100,000 итераций. Конечно, SHA в буквальном смысле не рекомендуется для создания ключей паролей. Вместо этого, вам лучше использовать что-то вроде scrypt. Еще одним полезным инструментом может быть сторонний пакет bcrypt. Он разработан специально для хеширования паролей.
PyCrypto
Пакет PyCrypto, наверное, самый известный сторонний пакет криптографии для Python. К сожалению, его доработка остановилась в 2012 году. Однако продолжается выпускаться под разные версии Python, вы можете получить PyCrypto для версии 3.5 включительно, если вы не брезгуете использовать двоичный код стороннего производства. К примеру, я нашел несколько бинарных колес Python 3.5 для PyCrypto на Github (https://github.com/sfbahr/PyCrypto-Wheels). К счастью, есть развилка проекта под названием PyCrytodome, которая является неплохой заменой PyCrypto. Для его установки на Linux вы можете использовать следующую команду:
Для Windows немного отличается.
pip install pycryptodomex
| pip install pycryptodomex |
Если вы столкнетесь со сложностями, это, возможно, связанно с тем, что у вас нет необходимых установленных зависимостей, или необходим компилятор под Windows. Вы можете перейти на официальный сайт PyCryptodome для дополнительной информации о установке, или чтобы связаться с поддержкой. Также стоит отметить, что PyCryptodome имеет ряд преимуществ в сравнении с последней версией PyCrypto. Рекомендую потратить немного времени и посетить их сайт, для ознакомления с возможностями PyCryptodome.
Шифрование Строки
Теперь (после того, как вы ознакомились с информацией на сайте, я надеюсь), мы можем перейти к дальнейшим примерам. Для нашего следующего кода мы используем DES для шифровки строки:
from Crypto.Cipher import DES
key = b’abcdefgh’
def pad(text):
while len(text) % 8 != 0:
text += b’ ‘
return text
des = DES.new(key, DES.MODE_ECB)
text = b’Python rocks!’
padded_text = pad(text)
encrypted_text = des.encrypt(padded_text)
print(encrypted_text)
# b’>\xfc\x1f\x16x\x87\xb2\x93\x0e\xfcH\x02\xd59VQ’
| from Crypto.Cipher import DES
key = b’abcdefgh’
def pad(text): while len(text) % 8 != 0: text += b’ ‘ return text
des = DES.new(key, DES.MODE_ECB) text = b’Python rocks!’ padded_text = pad(text)
encrypted_text = des.encrypt(padded_text) print(encrypted_text) # b’>\xfc\x1f\x16x\x87\xb2\x93\x0e\xfcH\x02\xd59VQ’ |
Этот код слегка запутанный, так что давайте уделим немного времени на его анализ. Во первых, обратите внимание на то, что размер ключа под шифровку DES — 8 байт, по этому мы установили нашу переменную ключа в строку размер букв строки. Шифруемая нами строка должна быть кратна 8 в ширину, так что мы создаем функцию под названием pad, которая может заполнить любую строку пробелами, пока она не станет кратна 8. Далее мы создаем экземпляр DES и текст, который нам нужно зашифровать. Мы также создаем заполненную версию текста. Прикола ради, мы попытаемся зашифровать начальный, незаполненный вариант строки, что приведет нас к ошибке ValueError. Таким образом Python ясно дает нам понять, что нам нужно использовать заполненную строку, так что мы передаем вторую версию. Как вы видите, мы получаем зашифрованную строку! Конечно, пример нельзя назвать полным, если мы не выясним, как расшифровать нашу строку:
data = des.decrypt(encrypted_text)
print(data) # Python rocks!
| data = des.decrypt(encrypted_text) print(data) # Python rocks! |
К счастью, это очень легко сделать, так как все что нам нужно, это вызвать метод decrypt в нашем объекте des для получения расшифрованной байтовой строки. Наша следующая задача — научиться шифровать файлы и расшифровывать файлы с PyCrypto при помощи RSA. Но для начала, нам нужно создать ключи RSA.
Создание ключей RSA
Если вам нужно зашифровать ваши данные при помощи RSA, тогда вам также нужно получить доступ к паре ключа RSA public / private, или сгенерировать собственную. В данном примере мы генерируем собственную пару ключей. Так как это весьма легко, мы сделаем это в интерпретаторе Python:
from Crypto.PublicKey import RSA
code = ‘nooneknows’
key = RSA.generate(2048)
encrypted_key = key.exportKey(
passphrase=code,
pkcs=8,
protection=»scryptAndAES128-CBC»
)
with open(‘my_private_rsa_key.bin’, ‘wb’) as f:
f.write(encrypted_key)
with open(‘my_rsa_public.pem’, ‘wb’) as f:
f.write(key.publickey().exportKey())
| from Crypto.PublicKey import RSA
code = ‘nooneknows’ key = RSA.generate(2048)
encrypted_key = key.exportKey( passphrase=code, pkcs=8, protection=»scryptAndAES128-CBC» )
with open(‘my_private_rsa_key.bin’, ‘wb’) as f: f.write(encrypted_key)
with open(‘my_rsa_public.pem’, ‘wb’) as f: f.write(key.publickey().exportKey()) |
Сначала мы импортируем RSA из Crypto.PublicKey. Затем, мы создаем примитивный код доступа. Далее, мы генерируем ключ RSA на 2048 битов. Теперь мы подходим к интересной части. Для генерации приватного ключа, нам нужно вызвать метод exportKey нашего ключа RSA, и передать ему наш код доступа, который будет использован стандартом PKCS, чья схема шифровки будет использована для защиты нашего приватного ключа. После этого мы записываем файл на диск. Далее, мы создаем наш приватный ключ через метод publickey нашего ключа RSA. Мы использовали короткий путь в этой части кода, связав вызов метода exportKey с методом publickey для записи файла на диск.
Шифровка файла
Теперь у нас в распоряжении есть и приватный и публичный ключи, так что мы можем зашифровать кое-какие данные и вписать их в файл. Вот достаточно простой пример:
from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES, PKCS1_OAEP
with open(‘encrypted_data.bin’, ‘wb’) as out_file:
recipient_key = RSA.import_key(
open(‘my_rsa_public.pem’).read()
)
session_key = get_random_bytes(16)
cipher_rsa = PKCS1_OAEP.new(recipient_key)
out_file.write(cipher_rsa.encrypt(session_key))
cipher_aes = AES.new(session_key, AES.MODE_EAX)
data = b’blah blah blah Python blah blah’
ciphertext, tag = cipher_aes.encrypt_and_digest(data)
out_file.write(cipher_aes.nonce)
out_file.write(tag)
out_file.write(ciphertext)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| from Crypto.PublicKey import RSA from Crypto.Random import get_random_bytes from Crypto.Cipher import AES, PKCS1_OAEP
with open(‘encrypted_data.bin’, ‘wb’) as out_file: recipient_key = RSA.import_key( open(‘my_rsa_public.pem’).read() )
session_key = get_random_bytes(16)
cipher_rsa = PKCS1_OAEP.new(recipient_key) out_file.write(cipher_rsa.encrypt(session_key))
cipher_aes = AES.new(session_key, AES.MODE_EAX) data = b’blah blah blah Python blah blah’ ciphertext, tag = cipher_aes.encrypt_and_digest(data)
out_file.write(cipher_aes.nonce) out_file.write(tag) out_file.write(ciphertext) |
Первые три строки покрывают наши импорты из PyCryptodome. Далее мы открываем файл для записи. Далее, мы импортируем наш публичный ключ в переменной и создаем 16-битный ключ сессии. Для этого примера мы будем использовать гибридный метод шифрования, так что мы используем PKCS#1 OAEP (Optimal asymmetric encryption padding). Это позволяет нам записывать данные произвольной длинны в файл. Далее, мы создаем наш шифр AES, создаем кое-какие данные и шифруем их. Это дает нам зашифрованный текст и MAC. Наконец, мы выписываем nonce, MAC (или тег), а также зашифрованный текст. К слову, nonce – это произвольное число, которое используется только в криптографических связях. Обычно это случайные или псевдослучайные числа. Для AES, оно должно быть минимум 16 байтов в ширину. Вы вольны попытаться открыть зашифрованный файл в своем текстовом редакторе. Вы увидите только какое-то безобразие. Теперь попробуем расшифровать наши данные:
from Crypto.PublicKey import RSA
from Crypto.Cipher import AES, PKCS1_OAEP
code = ‘nooneknows’
with open(‘encrypted_data.bin’, ‘rb’) as fobj:
private_key = RSA.import_key(
open(‘my_rsa_key.pem’).read(),
passphrase=code
)
enc_session_key, nonce, tag, ciphertext = [
fobj.read(x) for x in (private_key.size_in_bytes(), 16, 16, -1)
]
cipher_rsa = PKCS1_OAEP.new(private_key)
session_key = cipher_rsa.decrypt(enc_session_key)
cipher_aes = AES.new(session_key, AES.MODE_EAX, nonce)
data = cipher_aes.decrypt_and_verify(ciphertext, tag)
print(data)
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.PublicKey import RSA from Crypto.Cipher import AES, PKCS1_OAEP
code = ‘nooneknows’
with open(‘encrypted_data.bin’, ‘rb’) as fobj: private_key = RSA.import_key( open(‘my_rsa_key.pem’).read(), passphrase=code )
enc_session_key, nonce, tag, ciphertext = [ fobj.read(x) for x in (private_key.size_in_bytes(), 16, 16, -1) ]
cipher_rsa = PKCS1_OAEP.new(private_key) session_key = cipher_rsa.decrypt(enc_session_key)
cipher_aes = AES.new(session_key, AES.MODE_EAX, nonce) data = cipher_aes.decrypt_and_verify(ciphertext, tag)
print(data) |
Если вы разобрались с предыдущим примером, то этот код должен быть весьма простым для разбора. В данном случае, мы открываем наш зашифрованный файл для чтения в бинарном режиме. Далее, мы импортируем наш приватный ключ. Обратите внимание на то, что когда вы импортируете приватный ключ, вы должны передать ему код доступа. В противном случае возникнет ошибка. Далее мы считываем наш файл. Вы заметите, что сначала мы считываем приватный ключ, затем 16 байтов для nonce, за которыми следуют 16 байтов, которые являются тегом, и наконец, остальную часть файла, который и является нашими данными. Далее нам нужно расшифровать наш ключ сессии, пересоздать наш ключ AES и расшифровать данные. Вы можете использовать PyCryptodome в намного более широком ряде случаев. Однако, нам нужно идти дальше и посмотреть, что еще мы можем сделать для наших криптографических нужд в Python.
Пакет cryptography
Пакет cryptography нацелен на то, чтобы быть «криптографом для людей», р
Шифрование и дешифрование файлов с помощью Python.
Время прочтения: 3 мин.
Шифрование
— это процесс кодирования информации, позволяющий только авторизованным
сторонам получить к ней доступ. Подробно, о том для чего требуется шифрование
файлов, рассказал Юрий Плотников в своей статье опубликованной
13.05.2020 на сайте NewTethAudit.ru,
поэтому останавливаться на этом я не буду.
Также Юрий представил
пример функции, выполняющей шифрование, используя язык программирования C#.
Я же хочу поделиться с вами, как можно шифровать файлы,
используя Python с помощью библиотеки cryptography,
построенной на основе алгоритма AES. Возможно кому-то,
также как и мне, такой способ покажется более простым для шифрования файла с
данными.
В данном примере используется симметричное шифрование. Тот же
ключ, который применяется для шифрования данных, можно использовать для их
дешифрования.
Итак,
установим библиотеку cryptography:
pip install cryptography
Открываем новый файл Python:
from cryptography.fernet import Fernet
Создаем ключ и сохраняем его в файл, например, crypto.key:
def write_key():
# Создаем ключ и сохраняем его в файл
key = Fernet.generate_key()
with open('crypto.key', 'wb') as key_file:
key_file.write(key)
Внимание! Сгенерированный ключ crypto.key необходимо хранить в надежном
месте. В случае его потери невозможно будет расшифровывать данные, которые были
зашифрованы этим ключом.
Этот ключ уникален, и нет
необходимости генерировать ключ каждый
раз, когда потребуется зашифровать что-либо.
Создадим функцию для загрузки этого ключа:
def load_key():
# Загружаем ключ 'crypto.key' из текущего каталога
return open('crypto.key', 'rb').read()
Далее понадобится создать функцию для шифрования файла:
def encrypt(filename, key):
# Зашифруем файл и записываем его
f = Fernet(key)
После инициализации объекта Fernet с заданным ключом прочитаем этот файл:
with open(filename, 'rb') as file:
# прочитать все данные файла
file_data = file.read()
После этого, зашифровываем данные:
# Зашифровать данные
encrypted_data = f.encrypt(file_data)
Запишем зашифрованный файл с тем же именем, чтобы он переопределил оригинал:
# записать зашифрованный файл
with open(filename, 'wb') as file:
file.write(encrypted_data)
Теперь создадим функцию расшифровки файла:
def decrypt(filename, key):
# Расшифруем файл и записываем его
f = Fernet(key)
with open(filename, 'rb') as file:
# читать зашифрованные данные
encrypted_data = file.read()
# расшифровать данные
decrypted_data = f.decrypt(encrypted_data)
# записать оригинальный файл
with open(filename, 'wb') as file:
file.write(decrypted_data)
И, наконец, проверим это на конкретном файле, разместив
шифруемый файл и ключ в текущем каталоге.
Например, для шифрования
файла с именем ‘report.csv’ вызываем
созданную функцию encrypt():
# раскомментируйте следующую строку, если запускаете код впервые, чтобы сгенерировать ключ
# write_key()
# загрузить ключ
key = load_key()
# имя шифруемого файла
file = 'report.csv'
# зашифровать файл
encrypt(file, key)
После шифрования будет видно, что размер файла ‘report.csv’ увеличился,
и мы не сможем прочитать содержимое этого файл.
Чтобы вернуть файл ‘report.csv’ в исходную форму, вызовем
функцию decrypt ():
# расшифровать файл
decrypt(file, key)
Получаем исходный файл ‘report.csv’ вместо ранее зашифрованного.
Обратите внимание на то, что при применении данного кода, размер
файла не должен превышать объем оперативной памяти.
Применение
этого несложного кода Python
даст возможность надежно защитить данные от несанкционированного доступа к ним
и соблюсти требования кибербезопасности при работе с критически важной
информацией. При этом установки какого-либо специального программного
обеспечения не требуется.
AES-128. Детали и реализация на python / Хабр
Идея написать для себя что-то шифрующее родилась довольно тривиально — пришлось завести еще одну дебетовую карту и, следовательно, хранить в голове еще один пин-код. Хранить такую информацию в открытом виде паранойя не позволяет, использовать сторонние сервисы тоже, поэтому после некоторых поисков остановился на стандарте AES. Сразу захотелось разобраться и реализовать алгоритм самому, не прибегая к дополнительным модулям.
В статье я расскажу подробно о составляющих алгоритма, немного окунемся в мат часть и приведу пример реализации на python. В разработке я ограничивался только тем, что входит в состав стандартной библиотеки.
Немного введения
Advanced Encryption Standard является общеизвестным названием алгоритма Rijndael ([rɛindaːl]), который был разработан двумя бельгийскими криптографами Йоаном Дайменом и Винсентом Рэйменом. Алгоритм является блочным и симметричным. Принят в качестве стандарта шифрования данных для гос учреждений в США. Нашумевшее в последнее время Агенство Национальной Безопасности использует его для хранения документов: вплоть до уровня SECRET применяется шифрование с ключом длиной в 128 бит, информация TOP SECRET требует ключа в 192 или 256 бит. В дополнение к высокой криптостойкости алгоритм базируется на не самой сложной математике.
Много шифрования
Для работы нам необходим набор байтов в качестве объекта шифрования и секретный ключ, который потребуется при расшифровке. Длинные ключи хранить в голове неудобно, да и считается, что длины ключа в 128 бит, с лихвой хватает для неприступности, поэтому на варианты 192/256 я не смотрел. К тому же, как сказано здесь, при некоторых условиях более длинный ключ может отставать в устойчивости.
Введем некоторые обозначения:
- State — промежуточный результат шифрования, который может быть представлен как прямоугольный массив байтов имеющий 4 строки и Nb колонок. Каждая ячейка State содержит значение размером в 1 байт
- Nb — число столбцов (32-х битных слов), составляющих State. Для стандарта регламентировано Nb = 4
- Nk — длина ключа в 32-х битных словах. Для AES, Nk = 4, 6, 8. Мы уже определились, что будем использовать Nk = 4
- Nr — количество раундов шифрования. В зависимости от длины ключа, Nr = 10, 12 или 14
Алгоритм имеет четыре трансформации, каждая из которых своим образом влияет на состояние State и в конечном итоге приводит к результату: SubBytes(), ShiftRows(), MixColumns() и AddRoundKey(). Общую схему шифрования можно представить как:
В начале заполняется массив State входными значениями по формуле State[r][c] = input[r + 4c], r = 0,1…4; c = 0,1..Nb. То есть по колонкам. За раз шифруется блок размером 16 байт.
Алгоритм оперирует байтами, считая их элементами конечного поля или поля Галуа GF(28). Число в скобках — это количество элементов поля или его мощность. Элементами поля GF(28) являются многочлены степени не более 7, которые могут быть заданы строкой своих коэффициентов. Байт очень легко представить в виде многочлена. Например, байту {1,1,1,0,0,0,1,1} соответствует элемент поля 1x7 + 1x6 + 1x5 + 0x4 + 0x3 + 0x2 + 1x1 + 1x0 = 1x7 + 1x6 + 1x5 + x +1. То, что мы работаем с элементами поля, очень важно потому, что это меняет правила операций сложения и умножения. Этого мы коснемся немного позже.
Далее рассмотрим подробно каждую из трансформаций.
SybButes()
Преобразование представляет собой замену каждого байта из State на соответствующий ему из константной таблицы Sbox. Значения элементов Sbox представлены в шестнадцатеричной системе счисления. Сама же таблица получена посредством преобразований уже известного нам поля GF(28)
Каждый байт из State можно представить как {xy} в шестнадцатеричной системе счисления. Тогда следует заменять его на элемент, стоящий на пересечении строки x и столбца y. Например, {6е} заменится на {9f}, а {15} на {59}.
ShiftRows()
Простая трансформация. Она выполняет циклический сдвиг влево на 1 элемент для первой строки, на 2 для второй и на 3 для третьей. Нулевая строка не сдвигается.
MixColumns()
В рамках этой трансформации каждая колонка в State представляется в виде многочлена и перемножается в поле GF(28) по модулю x4 + 1 с фиксированным многочленом 3x3 + x2 + x + 2. Звучит вроде просто, но малопонятно, как это сделать. Картина становится проще, если посмотреть на эквивалентную матричную запись, предоставленную в официальном документе стандарта:
При умножении матриц, значение аij получается как сумма произведений соответствующих элементов i-ой строки первой матрицы и j-ого столбца второй, т. е.
Здесь нужно вспомнить о неработоспособности обычных правил сложения и умножения.
Новые правила:
- Сложение в поле GF(28) эквивалентно операции XOR
- Умножение на {01} не меняет умножаемое
- Умножение на {02} производится по правилу: если умножаемое значение меньше {80}, оно сдвигается влево на 1 бит. Если же умножаемое значение больше или равно {80}, оно сначала сдвигается влево на 1 бит, а затем к результату сдвига применяется операция XOR со значением {1b}. Результат может перескочить за значение {ff}, то есть за границы одного байта. В этом случае нужно вернуть остаток от деления результата на {100}.
- Умножение на другие константы можно выразить через предыдущие
Естественно, это не общие правила арифметики в конечном поле, но в рамках алгоритма придется умножать на три константы при шифровании и на четыре при дешифровке, так что таких локальных лайфхаков вполне хватит.
MixColumns() вместе с ShiftRows() добавляют диффузию в шифр.
AddRoundKey()
Трансформация производит побитовый XOR каждого элемента из State с соответствующим элементом из RoundKey. RoundKey — массив такого же размера, как и State, который строится для каждого раунда на основе секретного ключа функцией KeyExpansion(), которую и рассмотрим далее.
KeyExpansion()
Эта вспомогательная трансформация формирует набор раундовых ключей — KeySchedule. KeySchedule представляет собой длинную таблицу, состоящую из Nb*(Nr + 1) столбцов или (Nr + 1) блоков, каждый из которых равен по размеру State. Первый раундовый ключ заполняется на основе секретного ключа, который вы придумаете, по формуле
KeySchedule[r][c] = SecretKey[r + 4c], r = 0,1…4; c = 0,1..Nk.
Понятно, что в KeySchedule мы должны заносить байты, чтобы были возможны дальнейшие операции. Если использовать этот алгоритм для домашнего шифрования, то хранить в голове последовательность чисел совсем не уютно, так что в нашей реализации KeyExpansion() будет на вход брать plaintext строку и применяя ord()
для каждого из символов, записывать результат в ячейки KeySchedule. Отсюда вытекают ограничения: не более 16 символов длиной и, так как мы работаем с байтами, ord()
символа не должен возвращать значения большие чем 255 или 11111111 в двоичной, иначе получим на выходе неверное шифрование. Получается, что с помощью ключа на русском языке зашифровать не получится.
На рисунке изображен макет KeySchedule для AES-128: 11 блоков по 4 колонки. Для других вариаций алгоритма будет соответственно (Nr + 1) блоков по Nb колонок. Теперь нам предстоит заполнить пустые места. Для преобразований опять определена константная таблица — Rcon — значения которой в шестнадцатеричной системе счисления.
Алгоритм дозаполнения KeySchedule:
- На каждой итерации работаем с колонкой таблицы. Колонки 0,..,(Nk — 1) уже предварительно заполнены значениями из секретного слова. Начинаем с колонки под номером Nk (в нашем случае с четвертой)
- Если номер Wi колонки кратен Nk (в нашем случае каждая четвертая), то берем колонку Wi-1, выполняем над ней циклический сдвиг влево на один элемент, затем все байты колонки заменяем соответствующими из таблицы Sbox, как делали это в SubBytes(). Далее выполняем операцию XOR между колонкой Wi-Nk, измененной Wi-1 и колонкой Rconi/Nk-1. Результат записывается в колонку Wi. Чтобы было немного понагляднее, иллюстрация для i = 4.
- Для остальных колонок выполняем XOR между Wi-Nk и Wi-1. Результат записываем в Wi
Эта вспомогательная трансформация является самой объемной по написанию и, наверное, самой сложной, после осознания математики в MixColumns(), в алгоритме. Шифроключ обязан состоять из 4*Nk элементов (в нашем случае 16). Но так как мы делаем все это для домашнего применения, то вполне вероятно, что придумывать ключ в 16 символов и запоминать его не каждый будет. Поэтому, если на вход поступила строка длиной менее 16, я в KeySchedule дозаношу значения {01} до нормы.
Код KeyExpansion()
def key_expansion(key):
key_symbols = [ord(symbol) for symbol in key]
# ChipherKey shoul contain 16 symbols to fill 4*Nk table. If it's less
# complement the key with "0x01"
if len(key_symbols) < 4*nk:
for i in range(4*nk - len(key_symbols)):
key_symbols.append(0x01)
# make ChipherKey(which is base of KeySchedule)
key_schedule = [[] for i in range(4)]
for r in range(4):
for c in range(nk):
key_schedule[r].append(key_symbols[r + 4*c])
# Comtinue to fill KeySchedule
for col in range(nk, nb*(nr + 1)): # col - column number
if col % nk == 0:
# take shifted (col - 1)th column...
tmp = [key_schedule[row][col-1] for row in range(1, 4)]
tmp.append(key_schedule[0][col-1])
# change its elements using Sbox-table like in SubBytes...
for j in range(len(tmp)):
sbox_row = tmp[j] // 0x10
sbox_col = tmp[j] % 0x10
sbox_elem = sbox[16*sbox_row + sbox_col]
tmp[j] = sbox_elem
# and finally make XOR of 3 columns
for row in range(4):
s = key_schedule[row][col - 4]^tmp[row]^rcon[row][col/nk - 1]
key_schedule[row].append(s)
else:
# just make XOR of 2 columns
for row in range(4):
s = key_schedule[row][col - 4]^key_schedule[row][col - 1]
key_schedule[row].append(s)
return key_schedule
Как было сказано ранее, KeySchedule используется в трансформации AddRoundKey(). В раунде инициализации раундовым ключом будут колонки с номерами 0,..,3, в первом — с номерами 4,..,7 и тд. Вся суть AddRoundKey() — произвести XOR State и раундового ключа.
Это, собственно, все, что касается процесса шифрования. Выходной массив зашифрованных байтов составляется из State по формуле output[r + 4c] = State[r][c], r = 0,1…4; c = 0,1..Nb. А тем временем статья затягивается, так что мы сейчас быстро пробежимся по процедуре дешифровки.
Быстро о расшифровке
Идея здесь проста: если с тем же ключевым словом выполнить последовательность трансформаций, инверсных трансформациям шифрования, то получится исходное сообщение. Такими инверсными трансформациями являются InvSubBytes(), InvShiftRows(), InvMixColumns() и AddRoundKey(). Общая схема алгоритма расшифровки:
Стоит отметить, что последовательность добавления раундовых ключей в AddRoundKey() должна быть обратной: от Nr + 1 до 0. Изначально, как и при шифровании, из массива входных байтов формируется таблица State. Затем над ней в каждом раунде производятся преобразования, в конце которых должно получиться расшифрованный файл. Порядок трансформаций немного изменился. Что будет первым, InvSubBytes() или InvShiftRows(), на самом деле не важно, потому что одна из них работает со значениями байтов, а вторая переставляет байты, этих самых значений не меняя, но я придерживался последовательности преобразований в псевдокоде стандарта.
InvSubBytes()
Работает точно так же, как и SubBytes(), за исключением того, что замены делаются из константной таблицы InvSbox.
Оставшиеся обратные трансформации тоже будут очень похожи на свои прямые аналоги, поэтому в коде не выделяем под них отдельных функций. Каждая функция, описывающая трансформацию, будет иметь входную переменную inv
. Если она равна False
, то функция будет работать в обычном или прямом режиме(шифрование), если True
— в инверсном(дешифровка).
Код
def sub_bytes(state, inv=False):
if inv == False: # encrypt
box = sbox
else: # decrypt
box = inv_sbox
for i in range(len(state)):
for j in range(len(state[i])):
row = state[i][j] // 0x10
col = state[i][j] % 0x10
# Our Sbox is a flat array, not a bable. So, we use this trich to find elem:
box_elem = box[16*row + col]
state[i][j] = box_elem
return state
InvShiftRows()
Трансформация производит циклический сдвиг вправо на 1 элемент для первой строки State, на 2 для второй и на 3 для третьей. Нулевая строка не поворачивается.
Пояснения к коду: left_shift/right_shift(array, count)
поворачивают входной array
в соответствующую сторону count
раз
Код
def shift_rows(state, inv=False):
count = 1
if inv == False: # encrypting
for i in range(1, nb):
state[i] = left_shift(state[i], count)
count += 1
else: # decryption
for i in range(1, nb):
state[i] = right_shift(state[i], count)
count += 1
return state
InvMixColumns()
Операции те же, но каждая колонка State перемножается с другим многочленом {0b}x3 + {0d}x2 + {09}x + {0e}. В матричной форме это выглядит так:
Или готовые формулы. Все значения, конечно же, в шестнадцатеричной системе счисления.
Здесь нужно сделать отступление в сторону математики и пояснить как получить функции умножения на константы большие, чем {02}. Как я уже говорил, они получаются путем разложения их через {01} и {02}, а именно:
Преобразования
n*{03} = n*({02} + {01}) = n*{02} + n*{01}
n*{09} = n*({08} + {01}) = n*{02}*{02}*{02} + n*{01}
n*{0b} = n*({08} + {02} + {01}) = b*{02}*{02}*{02} + n*{02} + n*{01}
n*{0d} = n*({08} + {04} + {01}) = n*{08} + n*{04} + n*{01} = n*{02}*{02}*{02} + n*{02}*{02} + n*{01}
n*{0е} = n*({08} + {04} + {02} = n*{08} + n*{04} + n*{02} = n*{02}*{02}*{02} + n*{02}*{02} + b*{02}
Разумеется, можно разложить числа и по-другому, но лично проверено, что разложение
n * {09} = n * {03} + n * {03} + n * {03}
и вызов соответствующих функций будут давать неверный результат (эталонные таблицы с результатами есть в одной из ссылок в списке источников). Специально оставил альтернативные (закомменченные) варианты вычислений, ибо они понятнее, изящнее, но почему-то работают неправильно.
Вспомогательные функции умножения
def mul_by_02(num):
if num < 0x80:
res = (num << 1)
else:
res = (num << 1)^0x1b
return res % 0x100
def mul_by_03(num):
return mul_by_02(num)^num
def mul_by_09(num):
#return mul_by_03(num)^mul_by_03(num)^mul_by_03(num) - works wrong, I don't know why
return mul_by_02(mul_by_02(mul_by_02(num)))^num
def mul_by_0b(num):
#return mul_by_09(num)^mul_by_02(num)
return mul_by_02(mul_by_02(mul_by_02(num)))^mul_by_02(num)^num
def mul_by_0d(num):
#return mul_by_0b(num)^mul_by_02(num)
return mul_by_02(mul_by_02(mul_by_02(num)))^mul_by_02(mul_by_02(num))^num
def mul_by_0e(num):
#return mul_by_0d(num)^num
return mul_by_02(mul_by_02(mul_by_02(num)))^mul_by_02(mul_by_02(num))^mul_by_02(num)
Пояснения к коду: функции mul_by_<константа>
выполняют умножение на соответствующую константу в GF(28) по правилам, что описывались в разделе про MixColumns().
Код
def mix_columns(state, inv=False):
for i in range(nb):
if inv == False: # encryption
s0 = mul_by_02(state[0][i])^mul_by_03(state[1][i])^state[2][i]^state[3][i]
s1 = state[0][i]^mul_by_02(state[1][i])^mul_by_03(state[2][i])^state[3][i]
s2 = state[0][i]^state[1][i]^mul_by_02(state[2][i])^mul_by_03(state[3][i])
s3 = mul_by_03(state[0][i])^state[1][i]^state[2][i]^mul_by_02(state[3][i])
else: # decryption
s0 = mul_by_0e(state[0][i])^mul_by_0b(state[1][i])^mul_by_0d(state[2][i])^mul_by_09(state[3][i])
s1 = mul_by_09(state[0][i])^mul_by_0e(state[1][i])^mul_by_0b(state[2][i])^mul_by_0d(state[3][i])
s2 = mul_by_0d(state[0][i])^mul_by_09(state[1][i])^mul_by_0e(state[2][i])^mul_by_0b(state[3][i])
s3 = mul_by_0b(state[0][i])^mul_by_0d(state[1][i])^mul_by_09(state[2][i])^mul_by_0e(state[3][i])
state[0][i] = s0
state[1][i] = s1
state[2][i] = s2
state[3][i] = s3
return state
AddRoundKey()
Эта трансформация обратна сама себе в силу свойства операции XOR:
(a XOR b) XOR b = a
Поэтому никаких изменений в нее вносить не нужно. Набор раундовых ключей формируется таким же образом, как и для шифрования с помощью функции KeyExpansion(), но раундовые ключи необходимо подставлять в обратном порядке.
Код
def add_round_key(state, key_schedule, round=0):
for col in range(nk):
# nb*round is a shift which indicates start of a part of the KeySchedule
s0 = state[0][col]^key_schedule[0][nb*round + col]
s1 = state[1][col]^key_schedule[1][nb*round + col]
s2 = state[2][col]^key_schedule[2][nb*round + col]
s3 = state[3][col]^key_schedule[3][nb*round + col]
state[0][col] = s0
state[1][col] = s1
state[2][col] = s2
state[3][col] = s3
return state
Теперь мы обладаем исчерпывающим набором вспомогательных функций-трансформаций, чтобы написать
Шифрующую функцию
def encrypt(input_bytes, key):
# let's prepare our input data: State array and KeySchedule
state = [[] for j in range(4)]
for r in range(4):
for c in range(nb):
state[r].append(input_bytes[r + 4*c])
key_schedule = key_expansion(key)
state = add_round_key(state, key_schedule)
for rnd in range(1, nr):
state = sub_bytes(state)
state = shift_rows(state)
state = mix_columns(state)
state = add_round_key(state, key_schedule, rnd)
state = sub_bytes(state)
state = shift_rows(state)
state = add_round_key(state, key_schedule, rnd + 1)
output = [None for i in range(4*nb)]
for r in range(4):
for c in range(nb):
output[r + 4*c] = state[r][c]
return output
Дешифрующую функцию
def decrypt(cipher, key):
# let's prepare our algorithm enter data: State array and KeySchedule
state = [[] for i in range(nb)]
for r in range(4):
for c in range(nb):
state[r].append(cipher[r + 4*c])
key_schedule = key_expansion(key)
state = add_round_key(state, key_schedule, nr)
rnd = nr - 1
while rnd >= 1:
state = shift_rows(state, inv=True)
state = sub_bytes(state, inv=True)
state = add_round_key(state, key_schedule, rnd)
state = mix_columns(state, inv=True)
rnd -= 1
state = shift_rows(state, inv=True)
state = sub_bytes(state, inv=True)
state = add_round_key(state, key_schedule, rnd)
output = [None for i in range(4*nb)]
for r in range(4):
for c in range(nb):
output[r + 4*c] = state[r][c]
return output
Эти две функции на вход берут список байтов, не шифрованных или шифрованных, и plaintext строку с секретным ключевым словом.
Заключение, интересные ссылки
Статья получилась довольно длинной. Я старался разбавлять текст картинками и, надеюсь, вам было интересно и никто не заскучал. Код, представленный в статье, не совсем исчерпывающий. Я не вставлял глобальное объявление константных таблиц, мелких функций для MixColumns(), а только на словах пояснил, что они где-то есть. Не сочтите, что я пиарюсь, но полный, склеенный вместе код можно взять в репозитории. Там же лежит скромный CLI интерфейс, который позволяет просто указать путь до файла, ввести секретный ключ и получить зашифрованный файл в той же директории, что и исходный (расшифрованный файл можно получить таким же способом). Шифруйте на здоровье!
И в конце необходимо сказать об одном немаловажном нюансе — padding или дополнение до блока. AES — алгоритм блочного шифрования, функции encrypt()/decrypt()
берут на вход ровно один блок входных байтов (для нашего варианта с ключом в 128 бит это 16 байт). В конце файла могут остаться от 1 до 15 байт, которые не формируют цельный блок. Их можно просто, не шифруя, отдать на запись в конечный файл, но в некоторых случаях, в конце файла может содержаться что-то важное, и такой вариант не подходит. Второй вариант мне подсказала статья на Википедии про блочные шифры
Простое дополнение нулевыми битами не решает проблемы, так как получатель не сможет найти конец полезных данных. К тому же, такой вариант приводит к атакам Оракула дополнения. Поэтому на практике применимо решение, стандартизованное как «Метод дополнения 2» в ISO/IEC 9797-1, добавляющее единичный бит в конец сообщения и заполняющее оставшееся место нулями. В этом случае была доказана стойкость к подобным атакам.
Так я и сделал (хотя первый вариант остался, просто закомментированный. Вдруг и пригодится).
Подборка источников:
Шифрование строк с помощью модуля hashlib Python
Для шифрования строк предназначен модуль hashlib. Прежде чем использовать функции из этого модуля, необходимо подключить модуль с помощью инструкции:
Модуль предоставляет следующие функции: md5(), sha1(), sha224(), sha256(), sha384 и sha512(). В качестве необязательного параметра функциям можно передать шифруемую последовательность байтов. Например:
>>> import hashlib
>>> h = hashlib.sha1(b»password»)
| >>> import hashlib >>> h = hashlib.sha1(b»password») |
Передать последовательность байтов можно также с помощью метода update(). В этом случае объект присоединяется к предыдущему значению:
>>> h = hashlib.sha1()
>>> h.update(b»password»)
| >>> h = hashlib.sha1() >>> h.update(b»password») |
Получить зашифрованную последовательность байтов и строку позволяют два метода — digest() и hexdigest():
>>> h = hashlib.sha1(b»password»)
>>> h.digest()
b'[\xaaa\xe4\xc9\xb9??\x06\x82%\x0b1\xf83\x1b~\xe6\x8f\xd9’
>>> h.hexdigest()
‘5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8’
| >>> h = hashlib.sha1(b»password») >>> h.digest() b'[\xaaa\xe4\xc9\xb9??\x06\x82%\x0b1\xf83\x1b~\xe6\x8f\xd9′ >>> h.hexdigest() ‘5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8’ |
Наиболее часто применяемой функцией является функция md5(), которая шифрует строку с помощью алгоритма MD5. Она используется для шифрования паролей так как не существует алгоритма для дешифровки. Для сравнения введенного пользователем пароля с сохраненным в базе необходимо зашифровать введенный пароль, а затем произвести сравнение.
>>> import hashlib
>>> h = hashlib.md5(b»password»)
>>> p = h.hexdigest()
>>> p # Пароль, сохраненный в базе
‘5f4dcc3b5aa765d61d8327deb882cf99’
>>> h3 = hashlib.md5(b»password») # Пароль, введенный пользователем
>>> if p == h3.hexdigest(): print(«Пароль правильный»)
| >>> import hashlib >>> h = hashlib.md5(b»password») >>> p = h.hexdigest() >>> p # Пароль, сохраненный в базе ‘5f4dcc3b5aa765d61d8327deb882cf99’ >>> h3 = hashlib.md5(b»password») # Пароль, введенный пользователем >>> if p == h3.hexdigest(): print(«Пароль правильный») |
Программа выведет что пароль правильный.
Злой шифровальщик. Шифруем все алгоритмов AES в Python.
Размышляя о шифровании после статьи о чате, я наткнулся на очень простую и удобную библиотеку pyAesCrypt . Она использует симметричный алгоритм шифрования AES256 -CBC и очень проста в использовании для шифрования файлов. Вы также можете выполнить шифрование/расшифровку в памяти (используя BytesIO) что может пригодиться. Правда я еще не разобрался с этим.
Давайте теперь просто опробуем ее и зашифруем все нафиг ). Вот простой пример зашифровки файла:
import os
def crypt(dir):
import pyAesCrypt
print('-----------------------------------')
password = input('Enter key: ') # Вводим ключ шифрования
bufferSize = 512*1024 #Размер буфера 512 килобайт, не нужно его делать очень большим
pyAesCrypt.encryptFile(str(dir),str(dir)+'.aes',password, bufferSize) # Собственно сама функция шифрования, создаст зашифрованный файл с расширение .aes
print('[Crypted] '+str(dir)+'.aes')
dir = input('Enter file name: ') # Вводим имя файла
crypt(dir)
Вот такой в итоге получился зашифрованный файл
Скучно ? Давайте добавим в наш код парсера директорий и файлов. Зашифруем все в указанной директории.
def parsdir(dir):
import os
for name in os.listdir(dir):
if os.path.isfile(os.path.join(dir,name)):
print ('file: '+os.path.join(dir,name))
else :
if os.path.isdir(os.path.join(dir,name)) :
parsdir(os.path.join(dir,name))
- os.path.isfile(path) — является ли путь файлом.
- os.path.isdir(path) — является ли путь директорией.
- os.path.join(path2[, path3[, …]]) — соединяет пути с учётом особенностей операционной системы.
- os.listdir(path=».») — список файлов и директорий в папке.
Расшифровка.
Конечно любое шифрование имеет смысл если можно расшифровать. Сделать это в этой библиотеке также просто как и зашифровать:
pyAesCrypt.decryptFile("data.txt.aes","data.txt",password,bufferSize)
Вообще мы сделаем все интересней. При шифрование файлов в каталоге будет создаваться автоматически файл Питона для расшифровки всех файлов. Нам останется его только запустить.
with open((dir+'/decrypt.py'),'w') as crypt: #Создаем файл
crypt.write(''' # Записываем в него текст
import os
def decrypt (file):
import pyAesCrypt
print ('--------------------------')
password = "'''+str(password)+'''" #Вставка значения переменной
bufferSize = 512*1024
pyAesCrypt.decryptFile(str(file),str(os.path.splitext(file)[0]),password,bufferSize)
print('[decryptFile]'+str(os.path.splitext(file)[0]))
os.remove(file)
def walk(dir):
for name in os.listdir(dir):
#print (name)
if os.path.isfile(os.path.join(dir,name)):
print ('file: '+os.path.join(dir,name))
# crypt (os.path.join(dir,name))
try:decrypt(os.path.join(dir,name))
except: pass
else :
if os.path.isdir(os.path.join(dir,name)) :
walk(os.path.join(dir,name))
walk("'''+str(dir)+'''")
''')
Ну вот собственно и все. Дальше я думаю вы лучше меня справитесь.
Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.
RSA шифрование на Python. | LinuxBlog.РФ
Что такое RSA ?
RSA это асимметричный алгоритм шифрования. Зашифровка и расшифровка сообщение происходит двумя разными ключами, так называемый Публичны ключ за зашифровки и приватный(секретный) ключ для расшифровки сообщений . Допустим Сережа хочет отправить сообщение Алисе. Он спрашивает у Алисы ее публичный ключ для зашифровки сообщения, а Алиса вторым(секретным) ключом его расшифрует.
RSA и Python.
Для реализации RSA в Питоне мы будем использовать модуль который так и называется RSA. Он поддерживает шифрование и расшифровку, подписание и проверку подписей в соответствии с PKCS#1 версия 1.5.
Первым делом нам надо сгенерировать пару ключей(публичный и приватный).
import rsa
(pubkey, privkey) = rsa.newkeys(512) # 512 bits длина ключа, рекомендуется не меньше 1024
Так же модуль поддерживает сохранение и загрузку ключей в формате PEM и DER.
pubkey_pem = pubkey.save_pkcs1() # (format='PEM')
privkey_pem = privkey.save_pkcs1()
pubkey = rsa.PublicKey.load_pkcs1(pubkey_tmp, 'PEM') #(keyfile:bytes, format='PEM')
Теперь зашифруем и расшифруем сообщение :
message = 'hello Alisa!'.encode('utf8')
crypto = rsa.encrypt(message, pubkey) # Зашифровка
message = rsa.decrypt(crypto, privkey) # Расшифровка
print(message.decode('utf8'))
Цифровая подпись.
Цифровая подпись (ЦП) позволяет подтвердить авторство электронного документа . Подпись связана как с автором, так и с самим документом с помощью криптографических методов, и не может быть подделана с помощью обычного копирования. Наш модуль RSA позволяет подписывать сообщения для подтверждения автора и целостности сообщения :
(pubkey, privkey) = rsa.newkeys(512)
message = 'Test message'
signature = rsa.sign(message, privkey, 'SHA-1') # Создание подписи rsa.sign(message, priv_key, hash_method),можно использовать ‘MD5’, ‘SHA-1’, ‘SHA-224’, 'SHA-256’, ‘SHA-384’ и ‘SHA-512’
Для проверки подписи используйте rsa.verify() функция. Эта функция возвращает значение True, если проверка прошла успешно:
>>> message = 'Test message'
>>> rsa.verify(message, signature, pubkey)
True
Если подпись не действительно выйдет исключение rsa.pkcs1.VerificationError
>>> message = 'Test message not true'
>>> rsa.verify(message, signature, pubkey)
Traceback (most recent call last):
File "", line 1, in
File "/home/sybren/workspace/python-rsa/rsa/pkcs1.py", line 289, in verify
raise VerificationError('Verification failed')
rsa.pkcs1.VerificationError: Verification failed
Проблема больших сообщений.
RSA может шифровать только сообщения, которые меньше, чем ключ. Пара байт теряются на случайном заполнении, а остальное доступно для само послание. Например, 512-битный ключ может кодировать 53-байт сообщения (512 бит = 64 байта, 11 байт используются для случайного заполнения и другая вещь.)
Но оф. руководство нам предлагает для шифрования больших сообщений воспользоваться блочным шифром, например AES. А его ключ передать зашифрованным с помощью алгоритма RSA :
import rsa.randnum
aes_key = rsa.randnum.read_random_bits(128)# Создаем случайный ключ 128 бит
encrypted_aes_key = rsa.encrypt(aes_key, public_rsa_key) # Зашифровываем ключ и передаем для расшифровки большого сообщения.
На этом все. Успехов )
Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.
Криптография с Python — процесс XOR
В этой главе давайте разберемся с процессом XOR и его кодированием на Python.
Алгоритм
Алгоритм шифрования и дешифрования XOR преобразует простой текст в формат байтов ASCII и использует процедуру XOR для преобразования его в указанный байт. Он предлагает следующие преимущества для своих пользователей —
- Быстрое вычисление
- Нет разницы отмечены в левой и правой части
- Легко понять и проанализировать
Код
Вы можете использовать следующий фрагмент кода для выполнения процесса XOR —
def xor_crypt_string(data, key = 'awesomepassword', encode = False, decode = False): from itertools import izip, cycle import base64 if decode: data = base64.decodestring(data) xored = ''.join(chr(ord(x) ^ ord(y)) for (x,y) in izip(data, cycle(key))) if encode: return base64.encodestring(xored).strip() return xored secret_data = "XOR procedure" print("The cipher text is") print xor_crypt_string(secret_data, encode = True) print("The plain text fetched") print xor_crypt_string(xor_crypt_string(secret_data, encode = True), decode = True)
Выход
Код для процесса XOR дает следующий вывод —
объяснение
Функция xor_crypt_string () включает в себя параметр для указания режима кодирования и декодирования, а также строковое значение.
Основные функции выполняются с модулями base64, которые следуют процедуре / операции XOR для шифрования или дешифрования обычного текста / зашифрованного текста.
Функция xor_crypt_string () включает в себя параметр для указания режима кодирования и декодирования, а также строковое значение.
Основные функции выполняются с модулями base64, которые следуют процедуре / операции XOR для шифрования или дешифрования обычного текста / зашифрованного текста.
Примечание. Шифрование XOR используется для шифрования данных, и его трудно взломать методом грубой силы, т. Е. Путем генерации случайных ключей шифрования для сопоставления с правильным текстом шифра.
Шифрование и дешифрование
в Python
В этом посте я обсуждаю, как шифровать и дешифровать сообщения в Python с использованием симметричного шифрования. Я продемонстрирую, как создавать ключи, сохранять ключи и как шифровать сообщения и текст.
Используя модуль криптографии в Python, мы будем использовать реализацию AES под названием Fernet для шифрования данных. Я также покажу вам, как сохранить ключи в безопасности и как использовать эти методы для файлов.
Установка криптографии
Поскольку в Python нет ничего, что могло бы зашифровать файлы, нам нужно будет использовать сторонний модуль.
PyCrypto довольно популярен, но, поскольку он не предлагает встроенных колес, если у вас не установлены инструменты сборки Microsoft Visual C ++, вам будет предложено установить его. Вместо того, чтобы устанавливать дополнительные инструменты только для этого, я буду использовать модуль криптографии. Чтобы установить это, выполните:
python -m pip установить криптографию
Чтобы убедиться, что он установлен правильно, откройте IDLE и выполните:
Если ошибок не было, значит, он установлен правильно.
Что такое симметричное шифрование?
Симметричное шифрование — это когда для шифрования и дешифрования сообщения используется ключ, поэтому любой, кто его зашифровал, может его расшифровать. Единственный способ расшифровать сообщение — узнать, что было использовано для его шифрования; вроде как пароль.
Чтобы использовать симметричное шифрование, мы будем использовать класс Fernet, который является реализацией AES
.
Ищете руководство по асимметричному шифрованию? Я написал один из них и для Python.
Получение ключа
Есть два основных способа получить ключ: мы можем либо сгенерировать новый, либо использовать тот, который был сгенерирован ранее.Эти ключи должны быть в определенном формате, поэтому убедитесь, что это правильно.
Чтобы сгенерировать новый случайный ключ, мы можем просто использовать
из cryptography.fernet import Fernet
ключ = Fernet.generate_key ()
Переменная key теперь будет иметь значение ключа в кодировке base64, безопасного для URL. При использовании этих ключей для шифрования убедитесь, что они в безопасности, если вы потеряете их, вы не сможете расшифровать свое сообщение.
Этот ключ будет иметь тип байтов, поэтому, если вам нужна строка, вы можете вызвать ключ .decode ()
для преобразования из UTF-8 в строковый тип Pythons.
Хранение ключей
Один из способов сохранить ключи в безопасности — это хранить их в файле. Для этого мы можем просто создать / перезаписать файл и поместить в него ключ.
file = open ('key.key', 'wb') # Открыть файл как wb для записи байтов
file.write (key) # Ключ все еще тип байтов
file.close ()
Обязательно храните эти файлы и не передавайте их никому, кому вы не доверяете.Любой, у кого есть эти ключи, может расшифровать все прошлые сообщения, зашифрованные этим ключом.
Ключи для чтения
Если вы ранее сохранили свой ключ, используя метод, который я показал, вы можете прочитать ключ обратно, используя следующий код.
file = open ('key.key', 'rb') # Открыть файл как wb для чтения байтов
key = file.read () # Ключ будет типа байтов
file.close ()
Теперь ключ будет считан в переменную , ключ и будет иметь тип байтов.
Создание ключа из пароля
Если вы хотите использовать в качестве ключа строку, которую пользователь может вводить, или какую-либо другую форму ввода, вы можете создать ключ, используя этот ввод.
импортная база64
из cryptography.hazmat.backends import default_backend
из cryptography.hazmat.primitives импортировать хеши
из cryptography.hazmat.primitives.kdf.pbkdf2 импорт PBKDF2HMAC
password_provided = "пароль" # Это вводится в виде строки
password = password_provided.encode () # Преобразовать в байты типа
salt = b'salt_ '# ИЗМЕНИТЕ ЭТО - рекомендуется использовать ключ из os.urandom (16), он должен иметь тип bytes
kdf = PBKDF2HMAC (
алгоритм = хэши.SHA256 (),
длина = 32,
соль = соль,
итераций = 100000,
backend = default_backend ()
)
key = base64.urlsafe_b64encode (kdf.derive (пароль)) # Можно использовать kdf только один раз
Переменная key теперь будет иметь значение безопасного для URL-адреса ключа в кодировке base64.
Рекомендуется использовать соль, отличную от указанной здесь.Вы можете создать новую соль с помощью os.urandom (16). Обязательно используйте одну и ту же соль каждый раз, когда вы конвертируете пароль в ключ, иначе это не даст того же результата.
Шифрование
Чтобы зашифровать сообщение, вам понадобится ключ (как обсуждалось ранее) и ваше сообщение в виде байтов типа (вы можете преобразовать строки в байты с помощью .encode ()
).
из cryptography.fernet import Fernet
message = "мой глубокий темный секрет" .encode ()
f = Фернет (ключ)
encrypted = f
.
с использованием AES-128 в режиме ECB — techtutorialsx
В этом руководстве мы проверим, как зашифровать и расшифровать данные с помощью AES-128 в режиме ECB, используя Python и библиотеку pycrypto.
Введение
В этом руководстве мы проверим, как зашифровать и расшифровать данные с помощью AES-128 в режиме ECB, используя Python и библиотеку pycrypto.
AES означает A dvanced E ncryption S tandard и представляет собой криптографический симметричный алгоритм шифрования, который может использоваться как для шифрования, так и для дешифрования информации [1].
Алгоритм может использовать ключи длиной 128, 192 и 256 бит и работать с блоками данных длиной 128 бит (16 байт) [1]. Мы собираемся использовать ключ 128 бит.
Поскольку мы можем захотеть зашифровать данные размером более 128 бит, нам нужно выбрать блочный режим. В нашем случае мы собираемся использовать ECB (Электронная кодовая книга), которая использует тот же неизмененный ключ для шифрования каждого блока простого текста [2].
С точки зрения безопасности, ECB, как правило, является плохим выбором, поскольку идентичные блоки простого текста зашифрованы в идентичные блоки зашифрованного текста [2], что позволяет возможному злоумышленнику раскрыть шаблоны в наших зашифрованных сообщениях.Вы можете посмотреть здесь очень наглядный пример проблем безопасности, которые могут возникнуть при использовании режима ECB.
Даже для одноблочных сообщений, если мы повторяем одно и то же сообщение с течением времени, злоумышленник может понять, какие сообщения идентичны.
Тем не менее, ECB очень интересен с иллюстративной точки зрения из-за своей простоты, поэтому мы анализируем его в этом руководстве.
Установка pycrypto
Как уже упоминалось, мы будем использовать библиотеку pycrypto для шифрования и дешифрования данных с помощью AES.Самый простой способ установить его — использовать pip, менеджер пакетов Python.
Чтобы установить его через pip, просто отправьте следующую команду в командной строке (в зависимости от того, как вы установили Python и pip, вам может потребоваться находиться в определенной папке, такой как папка Scripts, перед запуском команд pip):
pip install pycrypto
Это руководство было протестировано на Python 2.7.
Код
Первое, что мы собираемся сделать, это импортировать модуль AES из библиотеки pycrypto.Этот модуль предоставит функции и классы, которые нам нужны как для шифрования, так и для дешифрования данных.
из Crypto.Cipher импортировать AES
Далее нам нужно установить наш секретный ключ шифрования. Поскольку AES является симметричным алгоритмом шифрования, ключ является частным и должен быть известен только двум взаимодействующим сторонам.
Длина ключа должна быть 16, 24 или 32 байта , в зависимости от того, хотим ли мы использовать AES-128, AES-192 или AES-256 соответственно [3], как мы упоминали во введении. .
Мы собираемся выбрать произвольный ключ размером 16 байт только для иллюстраций. Обратите внимание, что выбранный ключ совсем небезопасен, и для реальных сценариев использования следует использовать надежные ключи.
ключ = 'abcdefghijklmnop'
Затем нам нужно вызвать новую функцию модуля AES . Эта функция вернет объект класса AESCipher [4], который предоставляет функции как для шифрования, так и для дешифрования данных.
Эта функция имеет много дополнительных параметров, которые вы можете проверить здесь, но мы будем использовать только параметры key и mode .
Ключ Параметр соответствует ключу шифрования, который будет использоваться алгоритмом, и мы передадим ему ключ, который мы ранее определили [4].
Режим Параметр соответствует режиму цепочки, который используется для дешифрования / шифрования [4]. Мы собираемся передать значение MODE_ECB , чтобы использовать режим электронной кодовой книги.
cipher = AES.new (ключ, AES.MODE_ECB)
Теперь, когда у нас есть объект AESCipher , мы можем зашифровать данные с помощью вызова метода encrypt .В качестве входных данных этот метод получает текстовую строку и шифрует ее с помощью предоставленного ключа и конфигураций, используемых в вызове новой функции .
Помните, что длина сообщения для шифрования должна быть кратной размеру блока, который составляет 16 байтов. В данном случае текстовое сообщение, которое я передаю, имеет 32 байта, а первый блок равен второму, чтобы позже проиллюстрировать повторение шаблона в зашифрованном тексте при использовании режима ECB.
Этот вызов метода encrypt вернет в качестве вывода строку с зашифрованным текстом.Мы также напечатаем тип возвращаемого значения, чтобы подтвердить, что это действительно строка.
msg = cipher.encrypt ('TechTutorialsX !! TechTutorialsX !!') печать (тип (сообщение))
Чтобы сделать результат более удобным для пользователя, мы преобразуем зашифрованный текст в его шестнадцатеричное представление. Для этого мы вызываем метод encode для нашей строки зашифрованного текста, передавая в качестве входных данных значение «шестнадцатеричный» .
print (msg.encode ("шестнадцатеричный"))
Теперь, когда у нас есть зашифрованный текст, мы расшифруем его обратно в обычный текст.Обратите внимание, что поскольку ранее созданный объект шифра имеет состояние [5], мы должны создать новый объект для дешифрования, снова вызывая функцию new с теми же входными параметрами.
decipher = AES.new (ключ, AES.MODE_ECB)
Наконец, мы вызываем метод decrypt для нашего нового объекта, передавая в качестве входных данных зашифрованный текст. Он возвращает в качестве вывода исходный расшифрованный простой текст, который мы будем печатать.
печать (decipher.decrypt (msg))
Окончательный исходный код можно увидеть ниже.
из Crypto.Cipher импортировать AES ключ = 'abcdefghijklmnop' cipher = AES.new (ключ, AES.MODE_ECB) msg = cipher.encrypt ('TechTutorialsX !! TechTutorialsX !!') печать (тип (сообщение)) print (msg.encode ("шестнадцатеричный")) decipher = AES.new (ключ, AES.MODE_ECB) печать (decipher.decrypt (msg))
Проверка кода
Чтобы протестировать код, просто запустите его в выбранной среде Python. Я использую IDLE, IDE, которая по умолчанию поставляется с установкой Python.
Вы должны получить результат, аналогичный показанному на рисунке 1, который показывает результаты выполнения программы.Первое, что мы видим, — это то, что результат метода encrypt действительно является строкой.
Затем мы можем проверить зашифрованный текст, полученный в результате шифрования входящего простого текста. Я выделил два блока зашифрованного текста, и, как можно видеть, они равны, потому что исходные блоки простого текста также были равны. Это иллюстрирует проблему использования ECB и почему мы должны использовать более сильные режимы.
Наконец, мы можем проверить результат дешифрования, полученный с использованием того же ключа.
Рисунок 1 — Вывод программы.
Ссылки
[1] https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.197.pdf
[2] https://proandroiddev.com/security-best-practices-symmetric-encryption-with-aes-in-java-7616beaaade9
[3] https://www.dlitz.net/software/pycrypto/api/current/Crypto.Cipher.AES-module.html
[4] https://www.dlitz.net/software/pycrypto/api/current/Crypto.Cipher.AES-module.html # new
[5] https://www.dlitz.net/software/pycrypto/api/current/Crypto.Cipher.blockalgo.BlockAlgo-class.html#encrypt
Нравится:
Нравится Загрузка …
.
Python Rijndael Encryption — переполнение стека
Переполнение стека
- Около
Продукты
- Для команд
Переполнение стека
Общественные вопросы и ответыПереполнение стека для команд
Где разработчики и технологи делятся частными знаниями с коллегамиВакансии
Программирование и связанные с ним технические возможности карьерного ростаТалант
Нанимайте технических специалистов и создавайте свой бренд работодателяРеклама
Обратитесь к разработчикам и технологам со всего мира- О компании
.