繁体   English   中英

如何从私钥中获取椭圆曲线公钥

[英]How to get an elliptic curve public key from a private key

因此,我需要使用 ECC spec256k1 从相应的 256 位数字中获取公钥。

因此,假设我使用 sha256 从任何密码中获取私钥,如下所示:

>>> import hashlib
>>> private_key = hashlib.sha3_256(b"Led Zeppelin - No Quarter").hexdigest()
>>> private_key
'c0b279f18074de51d075b152c8ce78b7bddb284e8cfde19896162abec0a0acce'

如何从该私钥中获取公钥? 我需要将公钥打印为字符串。

pip install fastecdsa

from fastecdsa import keys, curve,ecdsa
priv_key, pub_key = keys.gen_keypair(curve.secp256k1) 

print(pub_key)

产生

X: 0xcc228e1a4c8e187a0deeabcd6e43bc8f7b6bdd91b8f823912f2de188fba054e6
Y: 0x7995a9d3866a8fa11a9af933c76216a908995ec5cec6ed7d3056b787fa7d39d7

支持的原语

素数场上的曲线-

Name                      Class

P192 / secp192r1          fastecdsa.curve.P192  
P224 / secp224r1          fastecdsa.curve.P224  
P256 / secp256r1          fastecdsa.curve.P256  
P384 / secp384r1          fastecdsa.curve.P384  
P521 / secp521r1          fastecdsa.curve.P521 
secp192k1                 fastecdsa.curve.secp192k1     
secp224k1                 fastecdsa.curve.secp224k1     
secp256k1 (bitcoin curve) fastecdsa.curve.secp256k1     
brainpoolP160r1           fastecdsa.curve.brainpoolP160r1   
brainpoolP192r1           fastecdsa.curve.brainpoolP192r1   
brainpoolP224r1           fastecdsa.curve.brainpoolP224r1   
brainpoolP256r1           fastecdsa.curve.brainpoolP256r1   
brainpoolP320r1           fastecdsa.curve.brainpoolP320r1   
brainpoolP384r1           fastecdsa.curve.brainpoolP384r1
brainpoolP512r1           fastecdsa.curve.brainpoolP512r1

当前接受的答案描述了如何生成一个新的对,并没有解决这个问题。

生成非随机私钥

任何随机的 256 位 integer 都适合作为该曲线的私钥,因此与例如 RSA 相比,生成私钥非常快。

import hashlib
private_key = hashlib.sha3_256(b"Led Zeppelin - No Quarter").hexdigest()
print(private_key)
c0b279f18074de51d075b152c8ce78b7bddb284e8cfde19896162abec0a0acce

这与您的问题相同。

导出公钥

椭圆曲线密码学中的公钥是曲线上的点 - 一对 integer 坐标{X,Y}位于曲线上。 我们可以像这样计算

import fastecdsa.keys
import fastecdsa.curve

curve = fastecdsa.curve.secp256k1
private_key_raw = int(private_key, base=16)
pubkey = fastecdsa.keys.get_public_key(private_key_raw, curve)
print(pubkey)
X: 0xbaa41af234cb2744ddaa039929c6ff21f0d5ab5ebce045d4a7513236f9bd429a
Y: 0x30252bd111b42e5195355f7fbcb5d6586ae76facbb4b7118fa96a2e99b40f716
(On curve <secp256k1>)

公钥的压缩形式

值得注意的是,由于它们的特殊属性,公共 EC 点也可以在 SEC1 编码中“压缩”为仅一个坐标加上奇偶校验(奇数或偶数)位。 换言之,256 位私钥的公钥也可以表示为 25 位7位 integer 而不是两个坐标:

import fastecdsa.encoding.sec1
compressed_pubkey = fastecdsa.encoding.sec1.SEC1Encoder().encode_public_key(pubkey)
print("0x" + compressed_pubkey.hex())
0x02baa41af234cb2744ddaa039929c6ff21f0d5ab5ebce045d4a7513236f9bd429a

也就是上面的X坐标,以0x02为前缀。 第一个字节将是0x02 (偶数Y )或0x03 (奇数Y )。

由于偶数/奇数对应于 SEC1 编码中的pubkey.y % 2 ,您甚至可以在没有fastecdsa帮助的情况下自己对其进行编码:

compressed_pubkey = pubkey.x + ((2 if pubkey.y % 2 == 0 else 3) << 256)
print(hex(compressed_pubkey))
0x2baa41af234cb2744ddaa039929c6ff21f0d5ab5ebce045d4a7513236f9bd429a

生成 PEM 编码的密钥

最后但同样重要的是,我们可以为您的密钥对生成 PEM 编码的 forms,这是许多使用公钥的应用程序所期望的。 此外,将密钥存储在磁盘上是一种有效的方法。

公钥

pubkey_pem = fastecdsa.keys.export_key(pubkey, curve)
print(pubkey_pem)
-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEuqQa8jTLJ0TdqgOZKcb/IfDVq1684EXU
p1EyNvm9QpowJSvREbQuUZU1X3+8tdZYaudvrLtLcRj6lqLpm0D3Fg==
-----END PUBLIC KEY-----

私钥

privkey_pem = fastecdsa.keys.export_key(private_key_raw, curve)
print(privkey_pem)
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIMCyefGAdN5R0HWxUsjOeLe92yhOjP3hmJYWKr7AoKzOoAcGBSuBBAAK
oUQDQgAEuqQa8jTLJ0TdqgOZKcb/IfDVq1684EXUp1EyNvm9QpowJSvREbQuUZU1
X3+8tdZYaudvrLtLcRj6lqLpm0D3Fg==
-----END EC PRIVATE KEY-----

使用模块fastecdsa的其他两个答案是正确的,但您可能希望在没有任何外部非标准模块的情况下从头开始实现椭圆曲线算术,出于教育目的和学习乐趣。

我也这样做了,下面我展示了我的代码,它从头开始实现椭圆曲线点加法和乘法(阅读链接的 Wiki,它有所有数学描述),而不使用任何非标准模块。 椭圆曲线的数学非常简单,只需 Python 中的几十行代码即可完全实现。

标准曲线secp256k1的参数我取自比特币维基页面,这条曲线参数和其他曲线(如secp256r1secp384r1secp521r1也取自公共SECG pdf 这些参数给出所谓基点的坐标和参数。

之后公钥只是基点(标准曲线点)乘以私钥(整数)。 私钥是简单的大 integer,而公钥是由两个 integer 坐标(X,Y)和标准不可修改参数(A,B,P,Q)表示的椭圆曲线点。

在我的代码中,如果您出于某种原因想要使用外部gmpy2库并通过python -m pip install gmpy2安装它,您可以取消注释#import gmpy2行。 该库为所有曲线数学提供了大约2x倍的加速。 但是你不需要取消注释这一行,这个库的使用是非强制性的,我的代码只适用于标准 Python 模块非常快。

下面的代码作为示例计算您的问题中提供的私钥,打印它,计算公钥并打印公钥的 X 和 Y 坐标。 如您所见,获得的公钥与其他答案中打印的公钥相同,通过fastecdsa获得。

代码后可以看到我的程序的控制台output(打印的私钥和公钥)。

在线尝试!

class ECPoint:
    gmpy2 = None
    #import gmpy2
    import random
    
    class InvError(Exception):
        def __init__(self, *pargs):
            self.value = pargs
    
    @classmethod
    def Int(cls, x):
        return int(x) if cls.gmpy2 is None else cls.gmpy2.mpz(x)
    
    @classmethod
    def std_point(cls, t):
        if t == 'secp256k1':
            # https://en.bitcoin.it/wiki/Secp256k1
            # https://www.secg.org/sec2-v2.pdf
            p = 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F
            a = 0
            b = 7
            x = 0x79BE667E_F9DCBBAC_55A06295_CE870B07_029BFCDB_2DCE28D9_59F2815B_16F81798
            y = 0x483ADA77_26A3C465_5DA4FBFC_0E1108A8_FD17B448_A6855419_9C47D08F_FB10D4B8
            q = 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_BAAEDCE6_AF48A03B_BFD25E8C_D0364141
        elif t == 'secp256r1':
            # https://www.secg.org/sec2-v2.pdf
            p = 0xFFFFFFFF_00000001_00000000_00000000_00000000_FFFFFFFF_FFFFFFFF_FFFFFFFF
            a = 0xFFFFFFFF_00000001_00000000_00000000_00000000_FFFFFFFF_FFFFFFFF_FFFFFFFC
            b = 0x5AC635D8_AA3A93E7_B3EBBD55_769886BC_651D06B0_CC53B0F6_3BCE3C3E_27D2604B
            x = 0x6B17D1F2_E12C4247_F8BCE6E5_63A440F2_77037D81_2DEB33A0_F4A13945_D898C296
            y = 0x4FE342E2_FE1A7F9B_8EE7EB4A_7C0F9E16_2BCE3357_6B315ECE_CBB64068_37BF51F5
            q = 0xFFFFFFFF_00000000_FFFFFFFF_FFFFFFFF_BCE6FAAD_A7179E84_F3B9CAC2_FC632551
        elif t == 'secp384r1':
            # https://www.secg.org/sec2-v2.pdf
            p = 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFFFF_00000000_00000000_FFFFFFFF
            a = 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFFFF_00000000_00000000_FFFFFFFC
            b = 0xB3312FA7_E23EE7E4_988E056B_E3F82D19_181D9C6E_FE814112_0314088F_5013875A_C656398D_8A2ED19D_2A85C8ED_D3EC2AEF
            x = 0xAA87CA22_BE8B0537_8EB1C71E_F320AD74_6E1D3B62_8BA79B98_59F741E0_82542A38_5502F25D_BF55296C_3A545E38_72760AB7
            y = 0x3617DE4A_96262C6F_5D9E98BF_9292DC29_F8F41DBD_289A147C_E9DA3113_B5F0B8C0_0A60B1CE_1D7E819D_7A431D7C_90EA0E5F
            q = 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_C7634D81_F4372DDF_581A0DB2_48B0A77A_ECEC196A_CCC52973
        elif t == 'secp521r1':
            # https://www.secg.org/sec2-v2.pdf
            p = 2 ** 521 - 1
            a = 2 ** 521 - 4
            b = 0x0051_953EB961_8E1C9A1F_929A21A0_B68540EE_A2DA725B_99B315F3_B8B48991_8EF109E1_56193951_EC7E937B_1652C0BD_3BB1BF07_3573DF88_3D2C34F1_EF451FD4_6B503F00
            x = 0x00C6_858E06B7_0404E9CD_9E3ECB66_2395B442_9C648139_053FB521_F828AF60_6B4D3DBA_A14B5E77_EFE75928_FE1DC127_A2FFA8DE_3348B3C1_856A429B_F97E7E31_C2E5BD66
            y = 0x0118_39296A78_9A3BC004_5C8A5FB4_2C7D1BD9_98F54449_579B4468_17AFBD17_273E662C_97EE7299_5EF42640_C550B901_3FAD0761_353C7086_A272C240_88BE9476_9FD16650
            q = 0x01FF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFA_51868783_BF2F966B_7FCC0148_F709A5D0_3BB5C9B8_899C47AE_BB6FB71E_91386409
        else:
            assert False
        return ECPoint(a, b, p, x, y, q = q)
    
    def __init__(self, A, B, N, x, y, *, q = 0, prepare = True):
        if prepare:
            N = self.Int(N)
            A, B, x, y, q = [self.Int(e) % N for e in [A, B, x, y, q]]
            assert (4 * A ** 3 + 27 * B ** 2) % N != 0
            assert (y ** 2 - x ** 3 - A * x - B) % N == 0, (hex(N), hex((y ** 2 - x ** 3 - A * x) % N))
            assert N % 4 == 3
            assert y == pow(x ** 3 + A * x + B, (N + 1) // 4, N)
        self.A, self.B, self.N, self.x, self.y, self.q = A, B, N, x, y, q
    
    def __add__(self, other):
        A, B, N = self.A, self.B, self.N
        Px, Py, Qx, Qy = self.x, self.y, other.x, other.y
        if Px == Qx and Py == Qy:
            s = ((Px * Px * 3 + A) * self.inv(Py * 2, N)) % N
        else:
            s = ((Py - Qy) * self.inv(Px - Qx, N)) % N
        x = (s * s - Px - Qx) % N
        y = (s * (Px - x) - Py) % N
        return ECPoint(A, B, N, x, y, prepare = False)
    
    def __rmul__(self, other):
        other = self.Int(other - 1)
        r = self
        while True:
            if other & 1:
                r = r + self
                if other == 1:
                    return r
            other >>= 1
            self = self + self
    
    @classmethod
    def inv(cls, a, n):
        a %= n
        if cls.gmpy2 is None:
            try:
                return pow(a, -1, n)
            except ValueError:
                import math
                raise cls.InvError(math.gcd(a, n), a, n)
        else:
            g, s, t = cls.gmpy2.gcdext(a, n)
            if g != 1:
                raise cls.InvError(g, a, n)
            return s % n

    def __repr__(self):
        return str(dict(x = self.x, y = self.y, A = self.A, B = self.B, N = self.N, q = self.q))

    def __eq__(self, other):
        for i, (a, b) in enumerate([(self.x, other.x), (self.y, other.y), (self.A, other.A),
                (self.B, other.B), (self.N, other.N), (self.q, other.q)]):
            if a != b:
                return False
        return True
        
def get_pub(priv_key):
    bp = ECPoint.std_point('secp256k1')
    pub = priv_key * bp
    return pub.x, pub.y

def main():
    import hashlib
    priv_key = int(hashlib.sha3_256(b"Led Zeppelin - No Quarter").hexdigest(), 16)
    print('priv key :', hex(priv_key))
    pubx, puby = get_pub(priv_key)
    print('pub key x:', hex(pubx))
    print('pub key y:', hex(puby))

main()

Output:

priv key : 0xc0b279f18074de51d075b152c8ce78b7bddb284e8cfde19896162abec0a0acce
pub key x: 0xbaa41af234cb2744ddaa039929c6ff21f0d5ab5ebce045d4a7513236f9bd429a
pub key y: 0x30252bd111b42e5195355f7fbcb5d6586ae76facbb4b7118fa96a2e99b40f716

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM