Привет всем читателям Хабра! Не так давно решил разобраться с алгоритмами шифрования и принципами работы электронной подписи. Тема, я считаю, интересная и актуальная. В процессе изучения попробовал несколько библиотек, однако самой удобной с моей точки зрения является библиотека PyCrypto. У неё прекрасная документация, сопровождаемая примерами.
После прочтения материала вы усвоите следующие моменты:
from Crypto.Cipher import AES # алгоритм шифрования
from Crypto.Hash import SHA256 # Для хеширования данных используем также популярный алгоритм SHA.
from Crypto.Hash import MD5 # Этот алгоритм хеширования будет использован для приведения произвольной строки пароля к 32 битной
from Crypto import Random
def transform_password(password_str):
"""Transform the password string into 32 bit MD5 hash
:param password_str: <str> password in plain text;
:return: <str> Transformed password fixed length
"""
h = MD5.new()
h.update(key.encode())
return h.hexdigest()
def symmetric_encrypt(message, key, verbose = True):
"""Encripts the message using symmetric AES algorythm.
:param message: <str> Message for encryption;
:param key: <object> symmetric key;
:return: <object> Message encrypted with key
"""
key_MD5 = transform_password(key) # Приводим произвольный пароль к длине 32 бита
message_hash = SHA256.new(message.encode())
message_with_hash = message.encode() + message_hash.hexdigest().encode() #Добавим в конец сообщения его хеш. он понадобится нам при расшифровки
iv = Random.new().read(AES.block_size)
cipher = AES.new(key_MD5, AES.MODE_CFB, iv) # Создаем объект с заданными параметрами. AES.MODE_CFB - надежный режим шифрования, который предполагает наличие вектора инициализации iv. https://www.dlitz.net/software/pycrypto/api/current/Crypto.Cipher.blockalgo-module.html#MODE_CFB
encrypted_message = iv + cipher.encrypt(message_with_hash) # Включаем случайную последовательность в начало шифруемого сообщения. Это необходимо, чтобы в случае кодирования нескольких блоков текста, аналогичные блоки не давали одинаковые кодированные сообщения.
if verbose:
print(f'Message was encrypted into: {encrypted_message.hex()}')
return encrypted_message
def symmetric_decrypt(encr_message, key):
"""Decripts the message using private_key and check it's hash
:param encrypted_message: <object> Encrypted message
:param key: <object> symmetric key;
:return: <object> Message decripted with key
"""
key_MD5 = transform_password(key)
# Размеры боков нужны, для извлечения их из текста
bsize = AES.block_size
dsize = SHA256.digest_size*2
iv = Random.new().read(bsize)
cipher = AES.new(key_MD5, AES.MODE_CFB, iv)
decrypted_message_with_hesh = cipher.decrypt(encr_message)[bsize:] # Извлекаем из блока случайные символу, которые мы добавляли при шифровании
decrypted_message = decrypted_message_with_hesh[:-dsize] # Извлекаем хеш сообщения, который мы присоединяли при шифровании
digest = SHA256.new(decrypted_message).hexdigest() # хеш расшифрованной части сообщения. Он будет сравниваться с хешем, который мы присоединили при шифровании.
if digest==decrypted_message_with_hesh[-dsize:].decode(): # Если хеш расшифровааного сообщения и хеш, который мы добавили при шифровании равны, расшифровка правильная
print(f"Success!\nEncrypted hash is {decrypted_message_with_hesh[-dsize:].decode()}\nDecrypted hash is {digest}")
return decrypted_message.decode()
else:
print(f"Encryption was not correct: the hash of decripted message doesn't match with encrypted hash\nEncrypted hash is {decrypted_message_with_hesh[-dsize:]}\nDecrypted hash is {digest}")
message = """
Длина 120 км
Ширина 120 км
Высота/глубина 120 км
МгС 10 МгС
Двигатель Субсветовой двигатель 30-5 (2)
Класс гиперпривода Класс 4.0
"""
key = 'Traveling through hyperspace ain’t like dusting crops, farm boy.'
encr_message = symmetric_encrypt(message, key, verbose = True)
print('\n')
print('DECRIPTION')
decr_message = symmetric_decrypt(encr_message, key)
print(decr_message)
Message was encrypted into: ed10e4c65358bb9e351c801c3b3200b21fa86a24021c317bb5c9d8b3f76bdf9f3a7d26781a22402f0e4f41ca831b6d2da9e1e6878c34c79ddc7959af3ae9fc2ba0cfff1c0180a7e0f637f1aa5b24507d552d5dfe7625e7b81d817b5882b2b19bb95f3988a03c78f850098dfc8e6089863deaa39b887eaea4c1d4ba006edaec90205d54b27ed4ac70ed75cdd01732e1176bf04218beb8ae742ff708a201a9d1cb57dd5f2e70dc3239208d23705f7a3aae3e315c4df6d73c871b66c4995cce5f19738f731cd58755d21ed92612c44197f875cddf3f7aa1d60e435ce1492679b9d60c4b8538f52408f321711ac1d2daa6dbbc33dc655abca10e2f5fd3ff27823995b9dcdb62c0bafc1963ab539ccb466f1c140479df34b0005f578f72fcdd76b17391332037b801f74f733a08
DECRIPTION
Success!
Encrypted hash is b0dbb35b28fbff258350a50c39282b73e31f408c9da937c81d8d48115b491026
Decrypted hash is b0dbb35b28fbff258350a50c39282b73e31f408c9da937c81d8d48115b491026
Длина 120 км
Ширина 120 км
Высота/глубина 120 км
МгС 10 МгС
Двигатель Субсветовой двигатель 30-5 (2)
Класс гиперпривода Класс 4.0
Пользователь Akela_wolf справедливо заметил, сгенерировать и отправить открытый ключ может кто угодно. Я внес некоторые коррективы в план.
Будет правильно, если до отправки агентов штаб сгенерирует несколько пар ключей и назначит каждому агенту закрытый ключ. Лучше сгенерировать именно несколько пар, чтобы у каждого агента был индивидуальный ключ. Это необходимо, чтобы точно персонифицировать владельца ключа.
Тогда в случае компрометации ключей центр создаст новый СИММЕТРИЧНЫЙ ключ, закодирует его каждому агенту открытыми ключами и отправит по открытому каналу.
Общая схема решения
# Для генерации ключей мы будем использовать распространенный криптографический алгоритм RSA.
# Для хеширования данных используем также популярный алгоритм SHA256
from Crypto.PublicKey import RSA
# Напишем функцию для генерации ключей
def generate_keys(bits = 2048):
"""Generates the pair of private and public keys.
:param bits: <int> Key length, or size (in bits)
of the RSA modulus (default 2048)
:return: <object> private_key, <object> public_key
"""
private_key = RSA.generate(bits)
public_key = private_key.publickey()
return private_key, public_key
private_key, public_key = generate_keys(bits = 2048)
print(private_key.exportKey(format='PEM').decode())
print('\n')
print('#'*65)
print('\n')
print(public_key.exportKey(format='PEM').decode())
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA4JDLu7Vtvg2yqbH6Y0eJPfoEsOlKzgmOodqhA1CqkEG4OpKi
sGW7ciGP4v37GE6edHBCEy4UNkVQtnpPBjzTHvKd1pO70B84vD5OSrS7uNw2EYkj
d/ZwhrJMrcQKRwPkkM4OiewaaAaK0vPWJIKwlW61DY9X7LfNz7aOKMTbKnm1vdR0
919AV98FUmNoQBgka6nXFGmNbi7D43MtLwxBZIXfFupEiANSvOs+57hgaCho7OWM
GUOjLkG6HBscPhJ2W1H5DU9GjwL24ynTvKifgo1/2ue61MV1Pzh5CVaicJKNaRtg
Pd99gFhBGINsXV2X6Jh/W5nNsCddU4EI0AlO8wIDAQABAoIBAARM4YnjrIlSK9Sy
EtBp40frjMFyhjsx1ahlzmWI2utORt/gRPtJx3AlEmNPZ8qMXt5t8+X4IOz1INmN
uAuviH90N++O/q66mlSIgOlPUrT0ipiFXseCUZ9StMMzGNtJSMw5FfAwNEU/stLd
VoF2ezkxWIg88XsX/fn3Tfub4XKLvu4raJGcJ+Fo2GI9hYEGKnHhSuHvDHekTLlQ
z46O+cIwtehbFGcKesyK3zDD1uP5YLPIWpiqt1TgKjJzRF0l4ZJLk+RT7kU2pGIQ
mosOnr+06WyMIg724yQyAIwtS9X0czKBGUESrtTTb1HCXLeTwnncOTxh6q2z42LF
tn34+DECgYEA6EEp4oTvjfTQfUQPMByuAjF1hpdFHQqRymygiFgoF+Mg3QmL0w8j
/84H/q7s8FSx+3th8MK87bFq4lrry+h/mYwmvF5zZbhxcnl2uaX+KUPgpT6TgvAo
WOv2wc4BSaoo9DrxrZId86vpO2qbopw6gkBsvw47HSoQ+FSqXtZ0p8kCgYEA94Zj
b1ulctUjybiszO93TAjkzx3lU3yL+B1eZiQXtJa3mgG+ka1R/uMfr0NlT+Jzo0My
wHV30YRJDxziCrDol9OgSSU0sXwEcUxUIBLBwXLCp1EmMsYG9PB/x4OTWve35a8F
O+rMxuvWaZeIOfVCfL8UEcWweYaVdWIonJN+ltsCgYEAjeSZ2UlMLZce9RjqioNL
EA31dlfeoqJ9dYUuAn6RaB6cSk51vWlnnfXazo9CNIYaAsFbkcL3t+QHn+jaXEZc
BowocjbmG4Q20zBAB6XRBJbynSIA7yMYE1N9+uOHx+CMisGkO12krOUfZex4zzzR
RhhkF8ly9htoKL9ZIv20YXkCgYBzH3UF6PkVZJ5lhtgP5Nx2Z7iLwBrV7ppnBrnO
BcFkw6iXH3KT7KmzQ82LxWvMcMVZzLpBGyFkOAOG3OchE9DKNKpa+sv8NHMYguip
li+5mneAPFTozoOTznuPvtl9OLO2RuXHTVh6uFub9tdsJW8L+A8MiQagLwE6fDHp
SQxaewKBgQDIyzL1THpW3+AMNrOZuI/d3Em5wpGJiZbDSBRosvsfGm/sHaz4Ik5E
nWnftgktmsAD60eORTTh9/ww/nm7f3q9kzT8Sv1MmqeRXq9VFIOeP/+8SSE/7LzD
izlb5xEtVD8LuY54jHyiOxiZC++TQswMnOKKi0Gx26MDoO7Tx9akVw==
-----END RSA PRIVATE KEY-----
#################################################################
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4JDLu7Vtvg2yqbH6Y0eJ
PfoEsOlKzgmOodqhA1CqkEG4OpKisGW7ciGP4v37GE6edHBCEy4UNkVQtnpPBjzT
HvKd1pO70B84vD5OSrS7uNw2EYkjd/ZwhrJMrcQKRwPkkM4OiewaaAaK0vPWJIKw
lW61DY9X7LfNz7aOKMTbKnm1vdR0919AV98FUmNoQBgka6nXFGmNbi7D43MtLwxB
ZIXfFupEiANSvOs+57hgaCho7OWMGUOjLkG6HBscPhJ2W1H5DU9GjwL24ynTvKif
go1/2ue61MV1Pzh5CVaicJKNaRtgPd99gFhBGINsXV2X6Jh/W5nNsCddU4EI0AlO
8wIDAQAB
-----END PUBLIC KEY-----
from Crypto.PublicKey import RSA # для асимметричного шифрования будем использовать распространенный алгоритм RSA.
from Crypto.Hash import SHA256 # Для хеширования данных используем также популярный алгоритм SHA256. Хеширование в данном
#случае нам нужно для проверки правильности раскодирования
from Crypto.Cipher import PKCS1_OAEP # протокол шифрования
def encrypt_message(message, public_key, verbose = True):
"""Encripts the message using public_key.
:param message: <str> Message for encryption
:param public_key: <object> public_key
:param verbose: <bool> Print description;
:return: <object> Message encrypted with public_key
"""
message_hash = SHA256.new(message.encode()) # находим хеш сообщения.
cipher = PKCS1_OAEP.new(public_key)
message_with_hash = message.encode() + message_hash.hexdigest().encode() # мы добавляем хеш сообщения в сообщение, чтобы при раскодировке сравнить его с хешем раскодированного сообщения
encrypted_message = cipher.encrypt(message_with_hash)
if verbose:
print(f'Message: {message} was encrypted to\n{encrypted_message.hex()}')
return encrypted_message
def decrypt_message(encrypted_message, private_key):
"""Decripts the message using private_key and check it's hash
:param encrypted_message: <object> Encrypted message
:param private_key: <object> private_key
:return: <object> Message decripted with private_key
"""
dsize = SHA256.digest_size*2
cipher = PKCS1_OAEP.new(private_key)
decrypted_message_with_hesh = cipher.decrypt(encrypted_message) # все сообщение (вместе с хешем)
decrypted_message = decrypted_message_with_hesh[:-dsize] # текстовая часть сообщения без хеша
digest = SHA256.new(decrypted_message).hexdigest() # хеш расшифрованной части сообщения
if digest==decrypted_message_with_hesh[-dsize:].decode(): # Если хеш расшифрованного сообщения и хеш, который мы добавили при шифровании равны, расшифровка правильная
print(f"Success!\nEncrypted hash is {decrypted_message_with_hesh[-dsize:].decode()}\nDecrypted hash is {digest}")
return decrypted_message.decode()
else:
print(f"Encryption was not correct: the hash of decripted message doesn't match with encrypted hash\nEncrypted hash is {decrypted_message_with_hesh[-dsize:]}\nDecrypted hash is {digest}")
private_key, public_key = generate_keys()
new_symmetric_key = 'SOME_KEY_asdfasdfasdfasdfsdfgrtwhetynt'
encr_msg = encrypt_message(new_symmetric_key, public_key)
Message: SOME_KEY_asdfasdfasdfasdfsdfgrtwhetynt was encrypted to
41e940507c96397e3feb4a53390c982633bb1775a52957996a8069bd22063086a0e831bf775a17909276aba0d0478ee6c953837c8ea5d20d40e1c8eb463aaa1bc5c93c71677b1a85e90439c9dbda8a98ce168acb38368155437c66815b84aa2fbdda0eb909e4e6079b4410c720eddd955ed048193bf87f8f9976a17ee32a58a71dfddf3db116343d949d29c25f72c511a440a50a5d4f1e01c37b24a1cb4127e191d3231328b2f120c7dbd0cb5bf19823f0978b8ed17d25952de4b146ef9724fff359eb2af503fdfd72b91525a5503b076ba9aaaeac55af3f8d210c12d579d45dd70362123c0b4b36ef9c2f7705e6f884a25553eb0e11e5077f11fa986d0ff280
recieved_symmetric_key = decrypt_message(encr_msg, private_key)
print('\n')
print(f"New symmetric key is: {recieved_symmetric_key}")
Success!
Encrypted hash is 42ad66445a05ac09e684bb21f9b487d95b9cfa11d02e0b459931321ee02f7c1c
Decrypted hash is 42ad66445a05ac09e684bb21f9b487d95b9cfa11d02e0b459931321ee02f7c1c
New symmetric key is: SOME_KEY_asdfasdfasdfasdfsdfgrtwhetynt
message = """
Длина 120 км
Ширина 120 км
Высота/глубина 120 км
МгС 10 МгС
Двигатель Субсветовой двигатель 30-5 (2)
Класс гиперпривода Класс 4.0
"""
encr_message = symmetric_encrypt(message, recieved_symmetric_key, verbose = True)
Message was encrypted into: 665968950814e0edf48bd80f368cda63ee0ac2b06173989b4bc239c02a064715e433d3e07f638d8e277e9b4bcd0d32c8c819483501066549a64c7ba8a29fa16a289c0283665cfbc2d1ff8ea8c4c4691b76ca430d9d1a26bfe38eb9d3c6ff398ea8926f44af13cf74a30f870f56bab3112374331b3a8bdfd5b1ca465604738e83b86481ca8852193c6c27db25690577d3e28c930d357abba6a3bf4c1f77f69dd066b933b60b353a7c44439baaabe9a58593efab6b410ee7ff4bbcc72dd07456a4f8c9a68c64e99a9151bc466f9daa2cdd3b4abcef7f87bc58caaef52a7811ce05cda5e03e9cba4b1537f7e6096b2385491a1c49a451373868f9951581643047d8f0c72e789943cc7112e00d06a4848919ee823bb18338e2b157c39b8911e518fdccdc8847a3e24be4cedad6
print('DECRIPTION')
decr_message = symmetric_decrypt(encr_message, new_symmetric_key)
print(decr_message)
DECRIPTION
Success!
Encrypted hash is b0dbb35b28fbff258350a50c39282b73e31f408c9da937c81d8d48115b491026
Decrypted hash is b0dbb35b28fbff258350a50c39282b73e31f408c9da937c81d8d48115b491026
Длина 120 км
Ширина 120 км
Высота/глубина 120 км
МгС 10 МгС
Двигатель Субсветовой двигатель 30-5 (2)
Класс гиперпривода Класс 4.0
К сожалению, не доступен сервер mySQL