Как создать более короткий закрытый ключ для ECDSA (secp256k1) в криптографии Python
Я пытаюсь узнать, как создать биткойн-адрес, следуя этому руководству. Если вы прокрутите вниз, первый шаг, шаг 0, будет иметь ключ ECDSA длиной 256 бит (64 шестнадцатеричных). Я изучил криптографию Python и использую приведенный ниже код для проверки генерации ключей, но сохраненный ключ всегда представляет собой длинную (180 символов) строку из 64 строк.
Я пытался прочитать документы и посмотреть на функции, которые я вызываю на Github, но я не вижу, где я могу указать, какой длины должен быть ключ. В строке 216 этого файла указано, что размер ключа для secp256k1 по умолчанию равен 256 битам. Означает ли это, что я экспортирую это неправильно?
Кроме того, я рассмотрел возможность создания случайной шестнадцатеричной строки длиной 64 символа в диапазоне secp256k1,(0x1
в 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4140
), но я не вижу, где я могу создать экземпляр закрытого ключа из строкового или шестнадцатеричного значения
gentest.py
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.serialization import load_pem_private_key
def gen_key():
private_key = ec.generate_private_key(
ec.SECP256K1(), default_backend()
)
return private_key
def save_key(pk, filename):
pem = pk.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
with open(filename, 'wb') as pem_out:
pem_out.write(pem)
def load_key(filename):
with open(filename, 'rb') as pem_in:
pemlines = pem_in.read()
private_key = load_pem_private_key(pemlines, None, default_backend())
return private_key
if __name__ == '__main__':
pk = gen_key()
filename = 'privkey.pem'
save_key(pk, filename)
pk2 = load_key(filename)
privkey.pem
-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgQGh8om7IuKSTW637ZQug
SZQHUTv/yQzmM+KxGi1bg0ehRANCAATALLpDeKtfHxEnrgazJUu2z2/esSfzF5bj
Z4B/IBBB9uYHyMtjY8hS926bpXiWql7y7MMZXDSDD/zYWELuJZ1U
-----END PRIVATE KEY-----
1 ответ
Если у вас нет непрозрачного закрытого ключа (я думаю, что это связано со специализированным оборудованием, поэтому вряд ли), вы можете получить доступ к информации о частных номерах через key.private_numbers()
метод объекта закрытого ключа, при котором вы можете получить доступ к самому значению в виде целого числа; .private_numbers()
метод производит EllipticCurvePrivateNumbers
объект с .private_value
атрибут, питон int
, Отформатируйте это значение как 64-символьный шестнадцатеричный гекс с format()
:
>>> key = gen_key()
>>> key.private_numbers()
<cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateNumbers object at 0x110a6b828>
>>> key.private_numbers().private_value
1704732612341385685752055250212403073347894734334856205449544619169914419683
>>> format(key.private_numbers().private_value, '064x')
'03c4d82ee8e4c9d245f5a5ceae513569fb5693a0c3cca223b198c6944521f9e3'
или закодировать его в байты с int.to_bytes()
с прямым или обратным порядком байтов (целочисленный шестнадцатеричный вывод в порядке с обратным порядком байтов):
>>> key.private_numbers().private_value.to_bytes(32, 'big')
b'\x03\xc4\xd8.\xe8\xe4\xc9\xd2E\xf5\xa5\xce\xaeQ5i\xfbV\x93\xa0\xc3\xcc\xa2#\xb1\x98\xc6\x94E!\xf9\xe3'
>>> key.private_numbers().private_value.to_bytes(32, 'big').hex()
'03c4d82ee8e4c9d245f5a5ceae513569fb5693a0c3cca223b198c6944521f9e3'
Все это немного запутанно, потому что это обычно не требуется для работы cryptography
модуль, который работает с OpenSSL или другими криптографическими бэкэндами через структуры данных, которые хранят эту информацию в дружественных к библиотеке, а не в Python-форматах.
И да, ключ, который вы производите, имеет длину 256 бит, вы можете проверить это, посмотрев на .key_size
атрибут закрытого ключа:
>>> key.key_size
256
Формат DER может быть другим путем, потому что это машиночитаемая информация. Традиционный формат OpenSSL позволяет относительно легко извлекать информацию из структуры XN90 ASN.1 вручную, без установки парсера ASN.1, но это не совсем надежно. Вы бы искали 04 20
последовательность байтов (4 - строка октетов, 20 hex означает длину 32 байта), а значением будет второй элемент в последовательности с первым целым числом; это означает, что закрытый ключ всегда будет начинаться с 8-го байта:
der_bytes = key.private_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption())
assert der_bytes[5:7] == b'\x04\x20'
key_bytes = der_bytes[7:39]
Однако я не уверен на 100%, верны ли эти утверждения, и просто получить доступ к частным номерам намного проще.