Protocol

如何從私鑰中獲取比特幣公鑰

  • December 29, 2020

極其具體地,我如何將給定的私人比特幣密鑰轉換為公共比特幣密鑰(像我 5 歲一樣跟我說話,我必須一步一步地做這個,否則邪惡的女巫會在她的烤箱裡把我活活煮熟)。不是我在哪裡可以找到一個可以做到這一點的程序,但如果我自己做,我會怎麼做?

私鑰:

18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725

據說會導致公鑰:

0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6

其他人問如何私有化,我還沒有看到一個真正具體的答案,只是更一般的方向,但沒有答案解釋所有變數。我知道這是相當複雜的,如果某個特定的人認為它的工作量太大而無法回答,我完全尊重這一點。注意:我不關心比特幣地址,只對 Privatekey to Publickey 和具體方法感興趣。

ECDSA 曲線算法中的“a”和“b”等變數已經由比特幣指定(根據<https://en.bitcoin.it/wiki/Secp256k1>)。該頁面上還指定了“基點”又名“G”。“私人指數”或“k”,我還沒有找到。其中一些變數被認為是“隨機的”,這似乎是錯誤的,因為您可以將私鑰放入的每個生成器似乎總是吐出相同的公鑰……所以……所有變數要麼已經預設,要麼是從私鑰派生的。

感謝您對此的任何幫助。幾天來我一直在嘗試研究和理解這一點,但有時我似乎不理解這些術語和或符號,但我認為我已經超越了這一點,現在只是缺少了等式的一部分。

編輯添加:

這是先前在十進制中聲明的私鑰:

11253563012059685825953619222107823549092147699031672238385790369351542642469

這是先前聲明的十進制公鑰(x 和 y 值):

36422191471907241029883925342251831624200921388586025344128047678873736520530
20277110887056303803699431755396003735040374760118964734768299847012543114150

我只想知道如何從該私鑰轉到公鑰。假設它是一個簡單的方程,不涉及位移或異或等。它可能包括“點乘法”(我不知道如何將定義為同時具有 x 和 ay 的點相乘)。似乎沒有人理解其中的複雜性。你們是否建議我實際上向任何解釋清楚的人提供比特幣的一部分?

我將嘗試以不同的方式再次回答這個問題,使用小數字來保持可讀性。

  1. 將私鑰轉換為二進製表示,因此十進制數 105,即十六進制的 0x69,變為 01101001。
  2. 通過將生成器點 G 重複加倍來計算此點列表:
1*G
2*G = G+G
4*G = 2*G + 2*G
8*G = 4*G + 4*G
16*G = 8*G + 8*G
32*G = 16*G + 16*G
64*G = 32*G + 32*G
  1. 在這個列表旁邊寫下私鑰的位,如下所示:
privkey    pointlist
  1          1*G
  0          2*G
  0          4*G
  1          8*G
  0         16*G
  1         32*G
  1         64*G
  1. 現在開始只添加那些1旁邊寫著的點。
   9*G = 1*G + 8*G
  41*G = (9+32)*G = 9*G + 32*G
 105*G = (41+64)*G = 41*G + 64*G
  1. 現在您已經計算了私鑰 105 的公鑰,只使用了點加倍和點加操作。

實際值將是:

(0xf219ea5d6b54701c1c14de5b557eb42a8d13f3abbcd08affcc2a5e6b049b8d63,
0x4cb95957e83d40b0f73af4544cccf6b1f4b08d3c07b27fb8d8c2962a400766d1)

公鑰將採用以下兩種格式之一:

  • full,這是一個 65 字節的數字,以一個字節開頭0x04
  • 壓縮,這是一個較短的格式,33 個字節,從 0x02 或 0x03 開始。

完整的公鑰是 0x04,後跟 32 個字節的 x 座標,然後是 32 個字節的 y 座標。

壓縮後的公鑰是 0x02 還是 0x03,具體取決於 y 座標是偶數還是奇數,後跟 32 個字節的 x 座標。

然後對您的問題進行更多說明:

  • 一些較早的文本曾經將標量點乘法稱為指數,這就是為什麼有時將私鑰稱為私有指數的原因。
  • 曲線參數隨機選擇一次。這意味著 secp256k1 曲線的設計者試圖傳達該曲線沒有特定結構,但沒有成功。這意味著 NSA 可以或不可以在曲線參數中放置數學後門。
  • 當使用這條曲線並生成你的公鑰時,你必須隨機選擇你的私鑰,以一種任何人都無法猜到的方式。
  • 生成G器是橢圓曲線上的一個特定點,在secp256k1曲線中定義。

這是一個執行轉換的獨立 Python 腳本。您可以通過比較在Brainwallet輸入您的私鑰作為“秘密指數”來檢查它的工作。我從這個 Bitcointalk 執行緒中提取了腳本並刪除了不必要的東西(例如使用公鑰簽署消息並驗證該簽名的程式碼)。

將 Python 轉換為人類指令作為練習留給讀者(儘管我認為在這種 Python 程式碼的場景中,具有適當的文件,作為人類指令就可以了)。請注意,完全可以用筆和紙來計算,但這可能需要一段時間,而且由於必須處理如此龐大的數字,您可能會犯錯誤。

另請注意,這裡沒有比您在小學/小學學到的複雜得多的單個操作。有基本的比較&lt; &gt; ==、算術+ - *、除法,您關心的是商/、餘數%或兩者divmod,還有按位 AND ( &,如果您使用十六進制工作,這很容易;或者可以用算術複製)。

我不認為一個(非天才)5 歲的孩子真的能做到(對不起,邪惡的女巫贏了這一輪),但我認為一個有足夠耐心的普通成年人幾乎可以很快學會所需的數學(用Python 腳本作為..well.. 腳本,後面跟著)。然而,實際上即使在沒有電子計算設備的幫助下計算一個公鑰也可能需要很長時間(猜測:數年)。

#! /usr/bin/env python
# python 2.x

class CurveFp( object ):
 def __init__( self, p, a, b ):
   self.__p = p
   self.__a = a
   self.__b = b

 def p( self ):
   return self.__p

 def a( self ):
   return self.__a

 def b( self ):
   return self.__b

 def contains_point( self, x, y ):
   return ( y * y - ( x * x * x + self.__a * x + self.__b ) ) % self.__p == 0

class Point( object ):
 def __init__( self, curve, x, y, order = None ):
   self.__curve = curve
   self.__x = x
   self.__y = y
   self.__order = order
   if self.__curve: assert self.__curve.contains_point( x, y )
   if order: assert self * order == INFINITY

 def __add__( self, other ):
   if other == INFINITY: return self
   if self == INFINITY: return other
   assert self.__curve == other.__curve
   if self.__x == other.__x:
     if ( self.__y + other.__y ) % self.__curve.p() == 0:
       return INFINITY
     else:
       return self.double()

   p = self.__curve.p()
   l = ( ( other.__y - self.__y ) * \
         inverse_mod( other.__x - self.__x, p ) ) % p
   x3 = ( l * l - self.__x - other.__x ) % p
   y3 = ( l * ( self.__x - x3 ) - self.__y ) % p
   return Point( self.__curve, x3, y3 )

 def __mul__( self, other ):
   def leftmost_bit( x ):
     assert x &gt; 0
     result = 1L
     while result &lt;= x: result = 2 * result
     return result / 2

   e = other
   if self.__order: e = e % self.__order
   if e == 0: return INFINITY
   if self == INFINITY: return INFINITY
   assert e &gt; 0
   e3 = 3 * e
   negative_self = Point( self.__curve, self.__x, -self.__y, self.__order )
   i = leftmost_bit( e3 ) / 2
   result = self
   while i &gt; 1:
     result = result.double()
     if ( e3 & i ) != 0 and ( e & i ) == 0: result = result + self
     if ( e3 & i ) == 0 and ( e & i ) != 0: result = result + negative_self
     i = i / 2
   return result

 def __rmul__( self, other ):
   return self * other

 def __str__( self ):
   if self == INFINITY: return "infinity"
   return "(%d,%d)" % ( self.__x, self.__y )

 def double( self ):
   if self == INFINITY:
     return INFINITY

   p = self.__curve.p()
   a = self.__curve.a()
   l = ( ( 3 * self.__x * self.__x + a ) * \
         inverse_mod( 2 * self.__y, p ) ) % p
   x3 = ( l * l - 2 * self.__x ) % p
   y3 = ( l * ( self.__x - x3 ) - self.__y ) % p
   return Point( self.__curve, x3, y3 )

 def x( self ):
   return self.__x

 def y( self ):
   return self.__y

 def curve( self ):
   return self.__curve

 def order( self ):
   return self.__order

INFINITY = Point( None, None, None )

def inverse_mod( a, m ):
 if a &lt; 0 or m &lt;= a: a = a % m
 c, d = a, m
 uc, vc, ud, vd = 1, 0, 0, 1
 while c != 0:
   q, c, d = divmod( d, c ) + ( c, )
   uc, vc, ud, vd = ud - q*uc, vd - q*vc, uc, vc
 assert d == 1
 if ud &gt; 0: return ud
 else: return ud + m

# secp256k1
_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL
_r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L
_b = 0x0000000000000000000000000000000000000000000000000000000000000007L
_a = 0x0000000000000000000000000000000000000000000000000000000000000000L
_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798L
_Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L

class Public_key( object ):
 def __init__( self, generator, point ):
   self.curve = generator.curve()
   self.generator = generator
   self.point = point
   n = generator.order()
   if not n:
     raise RuntimeError, "Generator point must have order."
   if not n * point == INFINITY:
     raise RuntimeError, "Generator point order is bad."
   if point.x() &lt; 0 or n &lt;= point.x() or point.y() &lt; 0 or n &lt;= point.y():
     raise RuntimeError, "Generator point has x or y out of range."

curve_256 = CurveFp( _p, _a, _b )
generator_256 = Point( curve_256, _Gx, _Gy, _r )
g = generator_256

if __name__ == "__main__":
 print '======================================================================='
 ### set privkey
 # wiki
 #secret = 0xE9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33262L
 # question
 secret = 0x18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725L

 ### print privkey
 print 'secret', hex(secret)
 ### generate pubkey
 pubkey = Public_key( g, g * secret )
 ### print pubkey
 print 'pubkey', hex(pubkey.point.x()), hex(pubkey.point.y())
 print '======================================================================='

另請參閱用 C# 編寫的更加精簡的版本。

class CalcPub
{
   public static void Main()
   {
       var p = BigInteger.Parse("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", NumberStyles.HexNumber);
       var b = (BigInteger)7;
       var a = BigInteger.Zero;
       var Gx = BigInteger.Parse("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", NumberStyles.HexNumber);
       var Gy = BigInteger.Parse("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", NumberStyles.HexNumber);

       CurveFp curve256 = new CurveFp(p, a, b);
       Point generator256 = new Point(curve256, Gx, Gy);

       var secret = BigInteger.Parse("18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725", NumberStyles.HexNumber);

       Console.WriteLine("secret {0}", secret.ToString("X"));
       var pubkeyPoint = generator256 * secret;
       Console.WriteLine("pubkey {0}{1}", pubkeyPoint.X.ToString("X"), pubkeyPoint.Y.ToString("X"));
   }
}
class Point
{
   public static readonly Point INFINITY = new Point(null, default(BigInteger), default(BigInteger));
   public CurveFp Curve { get; private set; }
   public BigInteger X { get; private set; }
   public BigInteger Y { get; private set; }

   public Point(CurveFp curve, BigInteger x, BigInteger y)
   {
       this.Curve = curve;
       this.X = x;
       this.Y = y;
   }
   public Point Double()
   {
       if (this == INFINITY)
           return INFINITY;

       BigInteger p = this.Curve.p;
       BigInteger a = this.Curve.a;
       BigInteger l = ((3 * this.X * this.X + a) * InverseMod(2 * this.Y, p)) % p;
       BigInteger x3 = (l * l - 2 * this.X) % p;
       BigInteger y3 = (l * (this.X - x3) - this.Y) % p;
       return new Point(this.Curve, x3, y3);
   }
   public override string ToString()
   {
       if (this == INFINITY)
           return "infinity";
       return string.Format("({0},{1})", this.X, this.Y);
   }
   public static Point operator +(Point left, Point right)
   {
       if (right == INFINITY)
           return left;
       if (left == INFINITY)
           return right;
       if (left.X == right.X)
       {
           if ((left.Y + right.Y) % left.Curve.p == 0)
               return INFINITY;
           else
               return left.Double();
       }

       var p = left.Curve.p;
       var l = ((right.Y - left.Y) * InverseMod(right.X - left.X, p)) % p;
       var x3 = (l * l - left.X - right.X) % p;
       var y3 = (l * (left.X - x3) - left.Y) % p;
       return new Point(left.Curve, x3, y3);
   }
   public static Point operator *(Point left, BigInteger right)
   {
       var e = right;
       if (e == 0 || left == INFINITY)
           return INFINITY;
       var e3 = 3 * e;
       var negativeLeft = new Point(left.Curve, left.X, -left.Y);
       var i = LeftmostBit(e3) / 2;
       var result = left;
       while (i &gt; 1)
       {
           result = result.Double();
           if ((e3 & i) != 0 && (e & i) == 0)
               result += left;
           if ((e3 & i) == 0 && (e & i) != 0)
               result += negativeLeft;
           i /= 2;
       }
       return result;
   }

   private static BigInteger LeftmostBit(BigInteger x)
   {
       BigInteger result = 1;
       while (result &lt;= x)
           result = 2 * result;
       return result / 2;
   }
   private static BigInteger InverseMod(BigInteger a, BigInteger m)
   {
       while (a &lt; 0) a += m;
       if (a &lt; 0 || m &lt;= a)
           a = a % m;
       BigInteger c = a;
       BigInteger d = m;

       BigInteger uc = 1;
       BigInteger vc = 0;
       BigInteger ud = 0;
       BigInteger vd = 1;

       while (c != 0)
       {
           BigInteger r;
           //q, c, d = divmod( d, c ) + ( c, );
           var q = BigInteger.DivRem(d, c, out r);
           d = c;
           c = r;

           //uc, vc, ud, vd = ud - q*uc, vd - q*vc, uc, vc;
           var uct = uc;
           var vct = vc;
           var udt = ud;
           var vdt = vd;
           uc = udt - q * uct;
           vc = vdt - q * vct;
           ud = uct;
           vd = vct;
       }
       if (ud &gt; 0) return ud;
       else return ud + m;
   }
}
class CurveFp
{
   public BigInteger p { get; private set; }
   public BigInteger a { get; private set; }
   public BigInteger b { get; private set; }
   public CurveFp(BigInteger p, BigInteger a, BigInteger b)
   {
       this.p = p;
       this.a = a;
       this.b = b;
   }
}

引用自:https://bitcoin.stackexchange.com/questions/25024