4

我正在尝试按照本指南学习如何创建比特币地址。如果向下滚动,第一步,即第 0 步,是拥有一个 256 位(64 十六进制)长的 ECDSA 密钥。我研究了 Python Cryptography 并使用下面的代码来测试生成密钥,但保存的密钥始终是一个长(180 个字符)的 base 64 字符串。

我试图阅读文档并查看我在 Github 上调用的函数,但我看不到在哪里可以指定密钥的长度。在这个文件的第 216 行,它说 secp256k1 的密钥大小默认为 256 位。这是否意味着我导出错误?

或者,我考虑过在 secp256k1,( 0x1to 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4140) 范围内生成一个 64 个字符长的随机十六进制字符串,但我看不到在哪里可以从字符串或十六进制值创建私钥实例

测试.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)

私钥文件

-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgQGh8om7IuKSTW637ZQug
SZQHUTv/yQzmM+KxGi1bg0ehRANCAATALLpDeKtfHxEnrgazJUu2z2/esSfzF5bj
Z4B/IBBB9uYHyMtjY8hS926bpXiWql7y7MMZXDSDD/zYWELuJZ1U
-----END PRIVATE KEY-----
4

1 回答 1

4

如果您没有不透明的私钥(我认为这涉及专业硬件,所以不太可能),您可以通过私钥对象的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% 确定这些断言是否成立,而且仅访问私人号码要简单得多。

于 2018-09-08T14:38:53.503 回答