4 votes

Comment générer une clé privée plus courte pour ECDSA (secp256k1) en Cryptographie Python

J'essaie d'apprendre comment créer une adresse bitcoin en suivant les instructions suivantes ce guide . Si vous faites défiler vers le bas, la première étape, l'étape 0, est d'avoir une clé ECDSA de 256 bits (64 hex). J'ai regardé dans Python Cryptography et j'utilise le code ci-dessous pour tester la génération de clés, mais la clé enregistrée est toujours une longue (180 caractères) chaîne en base 64.

J'ai essayé de lire la documentation et de regarder les fonctions que j'appelle sur Github, mais je ne vois pas où je peux spécifier la longueur de la clé. À la ligne 216 de ce fichier il est dit que la taille de la clé pour secp256k1 est de 256 bits par défaut. Cela signifie-t-il que je l'exporte mal ?

Alternativement, j'ai envisagé de générer une chaîne hexadécimale aléatoire de 64 caractères dans l'intervalle secp256k1,( 0x1 a 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4140 ), mais je ne vois pas où je peux créer une instance de clé privée à partir d'une chaîne ou d'une valeur hexadécimale.

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-----

4voto

Martijn Pieters Points 271458

Si vous ne disposez pas d'un opaque (je pense que cela impliquerait du matériel spécialisé, donc peu probable), vous pouvez avoir accès aux informations des numéros privés via l'application key.private_numbers() méthode de l'objet de la clé privée, à partir duquel vous pouvez accéder à la valeur elle-même en tant que nombre entier ; la fonction .private_numbers() produit un EllipticCurvePrivateNumbers avec un .private_value attribut un Python int . Formatez cette valeur sous la forme d'un hexagone de 64 caractères avec un espacement de zéro, en utilisant la commande 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'

ou le coder en octets avec int.to_bytes() dans l'ordre big ou little endian (la sortie hexagonale des entiers est dans l'ordre big-endian) :

>>> 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'

Tout ceci est un peu alambiqué parce que ce n'est normalement pas nécessaire pour faire fonctionner le cryptography qui fonctionne avec OpenSSL ou d'autres backends de cryptographie via des structures de données qui conservent ces informations dans des formats adaptés à la bibliothèque et non à Python.

Et oui, la clé que vous produisez est longue de 256 bits, vous pouvez le vérifier en regardant le fichier .key_size de la clé privée :

>>> key.key_size
256

Le format DER pourrait être une autre voie, car il s'agit d'informations lisibles par machine. Le format traditionnel d'OpenSSL permet d'extraire assez facilement l'information de la base de données de l'utilisateur. X.690 Structure ASN.1 manuellement, sans installer un analyseur ASN.1, mais ce n'est pas exactement infaillible. Il faut chercher le 04 20 séquence d'octets (4 est une chaîne d'octets, 20 hex signifie qu'il s'agit de 32 octets), y la valeur sera le deuxième élément d'une séquence dont le premier est un nombre entier ; cela signifie que la clé privée commencera toujours au huitième octet :

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]

Je ne suis pas sûr à 100% que ces affirmations soient valables, et il est beaucoup plus simple d'accéder aux numéros privés.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X