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