Как создать более короткий закрытый ключ для ECDSA (secp256k1) в криптографии Python - PullRequest
0 голосов
/ 08 сентября 2018

Я пытаюсь узнать, как создать биткойн-адрес, следуя этому руководству . Если вы прокрутите вниз, первый шаг, шаг 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 Ответ

0 голосов
/ 08 сентября 2018

Если у вас нет непрозрачного закрытого ключа (я думаю, что это связано со специальным оборудованием, поэтому маловероятно), вы можете получить доступ к информации о личных номерах через key.private_numbers()метод объекта закрытого ключа, после чего вы можете получить доступ к самому значению в виде целого числа;метод .private_numbers() создает объект EllipticCurvePrivateNumbers с атрибутом .private_value , Python 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 позволяет относительно легко извлекать информацию из структуры X.690 ASN.1 вручную, без установки синтаксического анализатора ASN.1, но это не совсем надежно.Вы бы искали последовательность байтов 04 20 (4 - октетная строка, 20 шестнадцатеричных означает, что ее длина составляет 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%, если эти утверждения верны, и просто получить доступ к приватным номерам намного проще.

...