Загрузка класса в базу данных и получение его обратно

Мне нужно загрузить класс ключа для шифрования в БД, после чего получить его обратно для дешифровки. Вот код генерации ключей:

from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.backends import default_backend
 

def generate():
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
        backend=default_backend()
    )
    public_key = private_key.public_key()
    return private_key, public_key
 

def encrypt(data, key): #public_key кодирование
    return key.encrypt(data, 
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )
 
def decrypt(data, key): #private_key декодирование
    return key.decrypt(data,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )

Когда я заношу ключ в базу данных, он превращается в строку, по типу: <cryptography.hazmat.bindings._rust.openssl.rsa.RSAPrivateKey object at 0x"куча-цифр">
После получения ключа обратно и попытке дешифровки выходит ошибка:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "D:\projects\pometki\database\database.py", line 37, in set_superkey
    value = encrypt(value, keys[1])
            ^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\projects\pometki\crypter.py", line 17, in encrypt
    return key.encrypt(data,
           ^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'encrypt'

То есть ключ должен быть объектом класса cryptography.hazmat.bindings._rust.openssl.rsa.RSAPrivateKey
Я пробовал использовать eval(), но выходит ошибка

    <cryptography.hazmat.bindings._rust.openssl.rsa.RSAPrivateKey object at "куча-цифр">
    ^
SyntaxError: invalid syntax

Как мне хранить эти ключи в базе данных, чтобы их можно было получить обратно?
(Вместо "куча-цифр" идут разные цифры, то есть вывод ошибки немного другой, просто не хочу чтобы мои ключи украли)
Код для использования базы данных:

from sqlite3 import connect as db_con
from crypter import *

def open_connect():
    global connect, cursor
    connect = db_con('database/database.db', check_same_thread=False)
    cursor = connect.cursor()

def close_connect():
    connect.close()


open_connect()

cursor.execute('CREATE TABLE IF NOT EXISTS system(superkey TEXT, private TEXT, public TEXT)')
cursor.execute('CREATE TABLE IF NOT EXISTS passwords(id INTEGER PRIMARY KEY, _type TEXT, data TEXT)')

def check_system_created():
    cursor.execute('SELECT * FROM system')
    return bool(cursor.fetchone())

def create_system(private, public):
    cursor.execute(f'INSERT INTO system(private, public) VALUES("{private}", "{public}")')
    connect.commit()

def access_to_keys():
    global keys
    cursor.execute('SELECT private, public FROM system')
    data = cursor.fetchone()
    keys = [eval(data[0]), eval(data[1])] #тут ошибка с eval


def get_superkey():
    cursor.execute('SELECT superkey FROM system')
    return decrypt(cursor.fetchone()[0], keys[0])

def set_superkey(value):
    value = encrypt(value, keys[1])
    cursor.execute(f'UPDATE system SET superkey={value}')
    connect.commit()

Ответы (1 шт):

Автор решения: Pak Uula

Я бы экспортировал ключ в формат DER, кодировал его в Base64 и сохранял получившуюся строку в базу данных.

Пример

import base64
from io import BytesIO, StringIO
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.serialization import load_der_private_key


def gen_key():
    private_key = rsa.generate_private_key(
        public_exponent=65537, key_size=2048, backend=default_backend()
    )
    return private_key


def export_key(pk:rsa.RSAPrivateKey) -> str:
    der_bytes:bytes = pk.private_bytes(
        encoding=serialization.Encoding.DER,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption()
    )
    der_out = BytesIO()
    base64.encode(BytesIO(der_bytes), der_out)
    return str(der_out.getvalue(), "utf-8")


def import_key(b64_str:str) -> rsa.RSAPrivateKey:
    input = StringIO(b64_str)
    der_stream = BytesIO()
    base64.decode(input, der_stream)
    der_bytes : bytes = der_stream.getvalue()
    private_key = load_der_private_key(der_bytes, None, default_backend())
    return private_key


if __name__ == '__main__':
    pk = gen_key()
    der_b64 = export_key(pk)
    print(der_b64)
    pk2 = import_key(der_b64)
    print(pk.public_key().public_numbers())
    print(pk2.public_key().public_numbers())

Вывод:

MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCcdJS7K/oz7p39onp4ErPZO9yN
BsRSmI5AjXKwQxNGOG4hNx+zk5UTCLwM4lF5WY+Iq0dkiAO2ed1FVMIC4dISFpwFdtceNwryxRpu
812M/OpSRkZTC7cBuhX7x16DONbJnNavhIFU2xcsT/6YHTeOdJifE3x8iECUB0/c8E1axslSr0Zn
NyPLY/lmV6yc43g5/rLlQDqUL4KaCVFOECjeZdjuNF+IQxhEC4R4+B7M8yuqFoKL22jwNXGC/JQF
VIgf11lNCj0AwCrzoIcwU6UmeIbPtFbOPG0Bb6frr2hs/fmQ0TvjfoSIPecZt1cmANXEijn018Tv
eGIMUtM1+MovAgMBAAECggEAM8gH87zV5fs7Mhdk9jmmaab803YiYv3X0vidtUW16t9BX9/pL5uQ
UsjoyFDdS07/iOm7SsQTioDh4ZDwV1jbGJ6AC/TttLPd/USL/52vYDrS6OEWZGTzsHAg14CAQOj+
eaLdkSDHV0jd7g4ZwsmkuhLP8pJV+Ld1vCiBX6vuxjSRJ/BxFPu17XsRRgFmOvrW6Wyfznl9JEkK
CehWddHRVVqplg2R1x6DYBz6u9Qh90kOm9qetOGyJQm1spgG84+lMfXsruV/T+KweFCKt3uwoWSc
1yXVhKioBwqlJiCRaaSs7tb7uJ+YaVUZaB1z/QY16qhcj+A44QKc2nNnyHV98QKBgQDNM8aN41Qy
MjiiONNi03PWzeOCu1ag2O6Vnwaf//PtSsRbiv/V69C6IdVXc6HlaxmFJcM6AMkxu7pQ5SIAQLpz
wJ7HdWdwJ2G3vJPFC/GiXFvNXLcfI6BN88UvDnHa8lOA8M7e56lRuX5PK8EZrP8JtAVTHMWgnqbk
JncTnU8h/wKBgQDDL5WvSV3ib5VmWtX/1yBCvAIO37aVUGqH//IEcf4BRVJqFop3WDyK/LeURsPX
ZMzvQxW4qP9O0aAyK0qIxeMfIm5pGkChmb+CQdTI/LpWtySTftezmP+kklWs7uZxzlH+W52EUeLj
iyTlhZtQLgSi5LYUaowQRcDwxpvFT2/30QKBgQC2sV4ZfUe82RGq/ETXO8NqfywLfJcH5ZulAkx3
rv2RSnu/tOVJpAXVccHrdjuxccTq0lAaGBEIgkbDNv0qlHZsEBZNJq3NIILTfsVhLd0X3HqvP9iZ
lLJoNJxd5NJhSFYn+j10H3SUGj3mZuIo5ei6ztfew0MAWb/kaIqjn+6QKQKBgFISXyleeyisbLCm
P1edeIY3EdJ5RQfR0YmwGG7AE+Mq5/WN1tlJ8+MpUnP5sX2PeBCfL7H+9aOJx4dw5p1OQCsmSjDN
hiGtSXaXAcclZaDyz2Fps39kNRJZKcdWiPWPoLCIi1pJKFMvfes4287CGtb3vowlJRHukqtrh7JW
rakRAoGBAMMbKKWtB2tUufgr9hUY0j+b72JloifeQMvbIBtlpiis0cOTvSYBULE5BOR0YFW7dQ4O
po47Do3yxPoxmtWkFZBhJslrihqGS/8jgn9mr0Bly3jp/7qihroQwjMeD3F+tl+oUxGuNBbjQygu
8/PzQj7hMnU5g1+pgPl5DKITfj2R

<RSAPublicNumbers(e=65537, n=19750663799059011431450533745957882752508425212326669741021428051212326057048447128244195037706584596016889765513008789884210476698663712374152024103707892971118728814980164673018765444797534164872839823161103419020351009532442634727419039775930437020478011146255231180674249845766337417763637002501946987667514481506884063226382898497051915292139340515604749232490204577835979908946819971364137982200857630230821862118353079729961828028765851896539395307986354805888072535098749547221258754910087430202835613866707226473281493782352899805868851928502952881946566307177860644277936566436649809472760294454972883978799)>
<RSAPublicNumbers(e=65537, n=19750663799059011431450533745957882752508425212326669741021428051212326057048447128244195037706584596016889765513008789884210476698663712374152024103707892971118728814980164673018765444797534164872839823161103419020351009532442634727419039775930437020478011146255231180674249845766337417763637002501946987667514481506884063226382898497051915292139340515604749232490204577835979908946819971364137982200857630230821862118353079729961828028765851896539395307986354805888072535098749547221258754910087430202835613866707226473281493782352899805868851928502952881946566307177860644277936566436649809472760294454972883978799)>

Видно, что публичные ключи для pk и восстановленного из DER pk2 совпадают. Поэтому и сами приватные ключи равны.

→ Ссылка