Время прочтения: 3 мин.

В этой статье я расскажу, как шифровать и дешифровать данные алгоритмом шифрования ECIES. ECIES – это постквантовый алгоритм шифрования, использующий открытые ключи, основанный на эллиптических кривых. Этот алгоритм был предложен Виктором Шоупом в 2001 году. ECIES используется в различных стандартах, например, ANSI X9.63, IEEE 1363a, ISO 18033-2 и SECG SEC 1.

Итак, рассмотрим подробнее алгоритм шифрования.

Устанавливаем все зависимости и импортируем их:

from coincurve import PrivateKey, PublicKey
from typing import Union
import hashlib
import codecs

from Crypto.Cipher import AES
from Crypto.Protocol.KDF import HKDF
from Crypto.Hash import SHA256
from coincurve import PrivateKey, PublicKey
from coincurve.utils import get_valid_secret
from eth_keys import keys

Создаем функцию для генерации публичного ключа:

def key():
    return PrivateKey(get_valid_secret())

Далее создаем открытый и закрытый ключ шифрования:

curve_key = generate_key()
secret_key = curve_key.secret  # байтов
public_key = curve_key.public_key.point()

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

def encrypt_ecies(secret_key, message):

    eph_key = key()
    if isinstance(secret_key, str):
        pubkey = hex2pub(secret_key)
    elif isinstance(secret_key, bytes):
        pubkey = PublicKey(secret_key)
    else:
        raise TypeError("Неверный публичный ключ")

    key_cipher = enc(eph_key, pubkey)
    ctext = aes_enc (aes_key, message)
    return eph_key.public_key.format(False) + ctext

Далее понадобится создать функцию инкапсуляции данных, на вход функции подается сгенерированный секретный ключ, открытый ключ пользователя, далее с помощью алгоритма HKDF получаем ключевой материал, состоящий из ключа MAC и ключа шифрования:

def enc (secret_key, peer_pubkey):
    point = peer_pubkey.multiply(secret_key.secret)
    mast = secret_key.public_key.format(compressed=False) + point.format(
        compressed=False
    )
    deriv = HKDF(mast, AES_KEY_BYTES_LEN, b"", SHA256)
    return deriv  # type: ignore

После необходимо создать функцию для шифрования данных Алгоритмом AES, на вход подается ключ шифрования и текст, который необходимо зашифровать. Шифрование происходит с помощью библиотеки Crypto из которой импортируется алгоритм AES-128.  В результате получаем зашифрованное сообщение и тэг сообщения:

def aes_enc (aes_key, text):

    cipher = AES.new(aes_key, AES_CIPHER_MODE)

    enc, tag = cipher.encrypt_and_digest(text)  # type: ignore
    ctext = bytearray()
    ctext.extend(cipher.nonce)  # type: ignore
    ctext.extend(tag)
    ctext.extend(enc)
    return bytes(ctext)

Например, мы хотим зашифровать сообщение «Всем привет», сначала генерируем эллиптическую кривую, после создаем приватный и публичный ключи:

secp_k = generate_key()
    sk_bytes = secp_k.secret  # байтов
    pk_bytes = secp_k.public_key.format(True)
    m='Всем привет'.encode('utf-8')
    ec=encrypt(pk_bytes,m)

В результате получаем зашифрованное сообщение в байтах:

Публичный эфемерный ключ:b'\x04:\xebd\xd3\xa0\x15\xfa3\xc0\ x04q\xe3\xdeoj\xcd\xcc\x0b\xcfe\xe5\xa2\xc8n\x0e\\U\xaf\xb7Z\x06.\xc6\xabUy\xfd\xfa\xf0Ej\x81\xd5\x11\xda\xf8N\xbe}\xed\xc3\xb4*\xfb\xd3EP\x86H;\xb7-\xcb\x03'
Зашифрованное сообщение и тег:b'\xb1q/\x17Y\xb4\x7f\xa6\xe8E \xb1\xe1\xa1\xc4\x9f\xcc\x81<\x86V\x11\x86j\x1e\xf2\x90\xf9g\xfe\x82\x85\x05\x10\x84\xc2\xa0\xbc\xc3\xefe\r\xe1\xfb\xb3a_\x1bn\xa7BZ\xcc\x9b\xf8\x04\x96[uh\xbfu'
Результат шифрования: b'\x04:\xebd\xd3\xa0\x15\xfa3\xc0 \x04q\xe3\xdeoj\xcd\xcc\x0b\xcfe\xe5\xa2\xc8n\x0e\\U\xaf\xb7Z\x06.\xc6\xabUy\xfd\xfa\xf0Ej\x81\xd5\x11\xda\xf8N\xbe}\xed\xc3\xb4*\xfb\xd3EP\x86H;\xb7-\xcb\x03\xb1q/\x17Y\xb4\x7f\xa6\xe8E\ xb1\xe1\xa1\xc4\x9f\xcc\x81<\x86V\x11\x86j\x1e\xf2\x90\xf9g\xfe\x82\x85\x05\x10\x84\xc2\xa0\xbc\xc3\xefe\r\xe1\xfb\xb3a_\x1bn\xa7BZ\xcc\x9b\xf8\x04\x96[uh\xbfu'

Для дешифрования создадим аналогичные функции:

def dec (public_key, message):
    if isinstance(public_key, str):
        secret_key = hex2prv(public_key)
    elif isinstance(public_key, bytes):
        secret_key = PrivateKey(public_key)
    else:
        raise TypeError("Неправильный секретный ключ")

    pub_key = message[0:65]  
    enc = message[65:]
    eph_pub_key = PublicKey(pub_key)

    aeskey = dec (eph_pub_key, private_key)
    return aes_dec (aeskey, enc)

def dec (pub_key, peer_secret_key):
    point = pub_key.multiply(peer_secret_key.secret)
    master = pub_key.format(compressed=False) + point.format(compressed=False)
    deriv = HKDF(master, AES_KEY_BYTES_LEN, b"", SHA256)
    return deriv  

def aes_dec (aes_key, ctext):

    iv = ctext[:16]
    tag = ctext[16:32]
   data = ctext[32:]

    cipher = AES.new(key, AES_CIPHER_MODE, nonce=iv)
    return cipher.decrypt_and_verify(data, tag) 

Например, дешифруем полученное ранее зашифрованное сообщение командой ec_d=decrypt(sk_bytes,ec), в результате получим:

Результат шифрования:  Всем привет

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