Wie bekommt man einen Bitcoin Public Key aus einem Private Key

Wie wandle ich ganz konkret einen bestimmten privaten Bitcoin-Schlüssel in einen öffentlichen Bitcoin-Schlüssel um (Sprich mit mir, als wäre ich 5 und ich muss dies Schritt für Schritt tun, oder die böse Hexe wird mich lebendig in ihrem Ofen kochen). NICHT, wo kann ich ein Programm finden, das dies tut, aber wenn ich es selbst tun würde, was würde ich tun?

Privat Schlüssel:

18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725

ergibt angeblich den öffentlichen Schlüssel:

0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6

Andere haben gefragt, wie man privat öffentlich wird, ich habe keine wirklich spezifische Antwort gesehen, nur eine allgemeinere Richtung, aber keine Antworten erklären alle Variablen. Ich verstehe, dass dies ziemlich komplex ist, und wenn eine bestimmte Person denkt, dass es zu viel Arbeit ist, um darauf zu antworten, respektiere ich das vollkommen. Hinweis: Ich interessiere mich nicht für die Bitcoin-Adresse, ich interessiere mich nur für Privatekey zu Publickey und die Einzelheiten, wie.

Variablen wie das „a“ und „b“ im ECDSA-Kurvenalgorithmus werden bereits von Bitcoin bezeichnet (gemäß https://en.bitcoin.it/wiki/Secp256k1 ). Der "Basispunkt" alias "G" ist ebenfalls auf dieser Seite angegeben. Den "privaten Exponenten" oder "k" muss ich noch finden. Einige dieser Variablen sind angeblich "zufällig", was falsch erscheint, da jeder Generator, in den Sie einen privaten Schlüssel einfügen können, immer denselben öffentlichen Schlüssel auszuspucken scheint ... also ... alle Variablen sind entweder bereits voreingestellt oder werden aus dem privaten Schlüssel abgeleitet.

Danke für jede Hilfe dazu. Ich habe tagelang versucht, dies zu recherchieren und zu verstehen, aber es scheint, dass ich manchmal die Begriffe und / oder Notationen nicht verstehe, aber ich glaube, ich bin darüber hinweggekommen und jetzt fehlen nur noch Teile der Gleichung.

EDIT ADD:
Dies ist der zuvor angegebene private Schlüssel in Dezimal:

11253563012059685825953619222107823549092147699031672238385790369351542642469

Dies ist der zuvor angegebene öffentliche Schlüssel (x- und y-Werte) in Dezimalzahl:

36422191471907241029883925342251831624200921388586025344128047678873736520530
20277110887056303803699431755396003735040374760118964734768299847012543114150

Ich möchte nur wissen, wie ich von diesem privaten Schlüssel zum öffentlichen Schlüssel komme. Angeblich handelt es sich um eine einfache Gleichung, die keine Bitverschiebung oder xor usw. beinhaltet. Sie kann "Punktmultiplikation" enthalten (wobei ich nicht sehe, wie Sie einen Punkt multiplizieren können, der sowohl mit x als auch mit ay definiert ist). Niemand scheint die Feinheiten zu verstehen. Schlägt ihr vor, ich biete jedem, der es klar erklärt, tatsächlich einen Bruchteil einer Bitcoin an?

Nein, das war eine Programmierfrage und es zeigte nicht, woher "k" kam usw.

Antworten (5)

Ich werde versuchen, dies noch einmal anders zu beantworten, indem ich kleine Zahlen verwende, um es lesbar zu halten.

  1. Konvertieren Sie den privaten Schlüssel in eine binäre Darstellung, sodass die Dezimalzahl 105, die 0x69 in Hex ist, zu 01101001 wird.

  2. Berechnen Sie diese Liste von Punkten, indem Sie den Generatorpunkt G wiederholt verdoppeln:

    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
    
  3. Schreiben Sie die Bits des privaten Schlüssels wie folgt neben diese Liste:

    privkey    pointlist
       1          1*G
       0          2*G
       0          4*G
       1          8*G
       0         16*G
       1         32*G
       1         64*G
    
  4. Beginnen Sie nun damit, nur die Punkte hinzuzufügen, neben denen ein 1geschrieben steht.

        9*G = 1*G + 8*G
       41*G = (9+32)*G = 9*G + 32*G
      105*G = (41+64)*G = 41*G + 64*G
    
  5. Jetzt haben Sie den öffentlichen Schlüssel für den privaten Schlüssel 105 berechnet, indem Sie nur Punktverdopplungs- und Punktadditionsoperationen verwendet haben.

Der tatsächliche Wert wird sein:

(0xf219ea5d6b54701c1c14de5b557eb42a8d13f3abbcd08affcc2a5e6b049b8d63,
 0x4cb95957e83d40b0f73af4544cccf6b1f4b08d3c07b27fb8d8c2962a400766d1)

Der öffentliche Schlüssel liegt dann in einem von zwei Formaten vor:

  • full, dies ist eine 65-Byte-Zahl, beginnend mit einem Byte0x04
  • komprimiert, dies ist eine kürzere Form, 33 Bytes, beginnend entweder mit 0x02 oder 0x03.

Der vollständige öffentliche Schlüssel ist 0x04, gefolgt von 32 Bytes der x-Koordinate, gefolgt von 32 Bytes der y-Koordinate.

Der komprimierte öffentliche Schlüssel ist 0x02 oder 0x03, je nachdem, ob die y-Koordinate eine gerade oder ungerade Zahl ist, gefolgt von 32 Bytes der x-Koordinate.

Dann noch ein paar Anmerkungen zu deiner Frage:

  • In einigen älteren Texten wurde die Skalarpunktmultiplikation als Potenzierung bezeichnet, weshalb manchmal der private Schlüssel als privater Exponent bezeichnet wird.

  • die Parameter der Kurve wurden zufällig einmal gewählt. was bedeutet, dass die Designer der secp256k1-Kurve erfolglos versucht haben zu vermitteln, dass diese Kurve keine spezifische Struktur hat. Das bedeutet, dass die NSA eine mathematische Hintertür in die Kurvenparameter hätte einbauen können oder nicht.

  • Wenn Sie diese Kurve verwenden und Ihren öffentlichen Schlüssel generieren, müssen Sie Ihren privaten Schlüssel zufällig auswählen, so dass es unmöglich ist, ihn zu erraten.

  • Der Generator Gist ein bestimmter Punkt auf der elliptischen Kurve, der in der Kurve secp256k1 definiert ist.

Wow, Bravo! Zum ersten Mal verstehe ich wirklich, was unter der Haube des Codes vor sich geht, den ich geschrieben und gelesen habe. Es ist nicht mehr nur ecdsa.SigningKey.from_string(s.decode('hex'), curve=ecdsa.SECP256k1).verifying_keyDanke! Bitte posten Sie eine Bitcoin-Adresse! (Edit: auf deiner verlinkten Seite gefunden)
Eine Sache: Vielleicht möchten Sie ausdrücklich darauf hinweisen, dass sich die "NSA-sicheren" Parameter der Kurve in Ihrer Erklärung speziell auf G beziehen, damit diese Zahl erklärt wird (im Wesentlichen ist G nur eine große Primzahl, die bestimmte Eigenschaften in Bezug auf Ellipsen hat Kurven).
Danke für die BTC :) Zwei Kommentare: G ist ein Kurvenpunkt, keine Primzahl. Und ob diese Kurve NSA-sicher ist, ist ein Diskussionspunkt, siehe den Link, den ich hinzugefügt habe.
Ja, ich weiß ein bisschen über die Kontroverse – deshalb habe ich „NSA-sicher“ in Anführungszeichen gesetzt. In Bezug auf G bin ich jetzt neugierig, was genau das ist, also habe ich hier eine Folgefrage gestellt: bitcoin.stackexchange.com/questions/29904/…
Was ist also der eigentliche öffentliche Schlüssel für 105? und ist G zufällig?
Nachdem wir 105 für den privaten Schlüssel 105 erhalten haben. Was machen wir von hier aus? Wie erhalten wir den öffentlichen Schlüssel von n = 105?

Hier ist ein eigenständiges Python-Skript, das die Konvertierung durchführt. Sie können seine Funktion überprüfen, indem Sie Ihren privaten Schlüssel als "geheimen Exponenten" bei Brainwallet eingeben . Ich habe das Skript aus diesem Bitcointalk-Thread genommen und unnötige Dinge entfernt (wie den Code zur Verwendung des öffentlichen Schlüssels zum Signieren einer Nachricht und zum Überprüfen dieser Signatur).

Das Konvertieren von Python in Anweisungen für einen Menschen bleibt dem Leser als Übung überlassen (obwohl ich argumentieren würde, dass in einem Szenario wie diesem Python-Code mit entsprechender Dokumentation als Anweisungen für einen Menschen in Ordnung ist). Beachten Sie, dass es durchaus möglich ist, dies mit Stift und Papier zu berechnen, aber es kann eine Weile dauern, und Sie werden wahrscheinlich einen Fehler machen, weil Sie mit solch enormen Zahlen umgehen müssen.

Beachten Sie auch, dass es hier keine einzelnen Operationen gibt, die viel komplizierter sind, als Sie es in der Grundschule lernen würden. Es gibt grundlegende Vergleiche < > ==, Arithmetik + - *, Division, bei denen Sie sich um den Quotienten /, den Rest %oder beides kümmern divmod, und bitweises UND ( &, was ziemlich einfach ist, wenn Sie in Hex arbeiten; oder mit Arithmetik repliziert werden kann).

Ich glaube nicht, dass ein (ungenialer) 5-Jähriger es wirklich schaffen könnte (sorry, die böse Hexe gewinnt diese Runde), aber ich denke, ein durchschnittlicher Erwachsener mit genug Geduld könnte die erforderliche Mathematik in kürzester Zeit lernen (mit dem Python-Skript als ... naja ... Skript, folgt). Die tatsächliche Berechnung auch nur eines öffentlichen Schlüssels ohne die Hilfe elektronischer Rechengeräte könnte jedoch sehr lange dauern (geschätzt: Jahre).

#! /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 > 0
      result = 1L
      while result <= 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 > 0
    e3 = 3 * e
    negative_self = Point( self.__curve, self.__x, -self.__y, self.__order )
    i = leftmost_bit( e3 ) / 2
    result = self
    while i > 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 < 0 or m <= 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 > 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() < 0 or n <= point.x() or point.y() < 0 or n <= 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 '======================================================================='

Sehen Sie sich auch eine noch abgespecktere Version an, die in C# geschrieben ist.

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 > 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 <= x)
            result = 2 * result;
        return result / 2;
    }
    private static BigInteger InverseMod(BigInteger a, BigInteger m)
    {
        while (a < 0) a += m;
        if (a < 0 || m <= 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 > 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;
    }
}
Ich denke, ich habe es fast .... Also multiplizieren wir den "generator256" mit dem "Geheimnis" und das "Geheimnis" ist einfach der private Schlüssel. Aber ich habe verloren, was in "CurveFp curve256 = new CurveFp(p,a,b);" passiert. Was passiert mit diesen Zahlen, um "curve256" zu entsprechen, und was passiert dann damit, um "generator256" zu erstellen? Dann, wie ist es pubkeypoint aufgeteilt, um Ihnen die x- und y-Werte zu geben? führt die Multiplikation des gnerator256 und des Geheimnisses einfach zu einer Zahl, bei der die erste Hälfte der x-Punkt und die zweite Hälfte der y-Punkt in der Grafik ist? (Übrigens, denke ich ein Großteil des Codes trifft nicht zu, habe ich recht?)
Wenn Sie dies tun new CurveFp(p,a,b), werden die Nummern einfach gespeichert. Sie können sich eine Kurve als eine Paarung von drei Zahlen vorstellen. In ähnlicher Weise ist a Pointeinfach eine Paarung aus a Curveund zwei Zahlen. Alle diese Zahlen werden benötigt, um a mit einer Zahl zu multiplizieren Point, was in geschieht generator256 * secret. Das Ergebnis dieser (sehr komplizierten) Multiplikation ist ein Point; sein X und Y, miteinander verkettet, sind der öffentliche Schlüssel. Und ich denke, der (C#)-Code trifft alles zu; Sie können C# in einem Debugger mit VS Express schrittweise durchgehen, um es anzuzeigen.
Ach Mist. Wenn der gesamte C#-Code zutrifft, ist er vielleicht wirklich zu komplex, als dass mir jemand alles erklären könnte. Beim Lesen sah es so aus, als ob die zusätzlichen Berechnungen für die Bitcoin-Adresse oder die Signatur und nicht nur für den öffentlichen Schlüssel galten (da ich die Signatur oder Adresse im Moment nicht brauche oder mich darum kümmere, da ich denke, dass dies nur dazu dienen wird, mich weiter zu verwirren) . Wie ich sehe, beziehen Sie sich auf diese Dinge (Unterschrift und Adresse). Ich suche nur, um herauszufinden, wie man den privaten Schlüssel in den öffentlichen Schlüssel umwandelt und welche Variablen benötigt werden usw. Keine Adresse, keine Signatur, nichts weiter.
Ja, ich fürchte, C# ist wirklich nur von privat nach öffentlich, ohne Signierung oder Adresse. Wenn Sie es verstehen möchten, ohne es unbedingt praktisch berechnen zu können, können Sie es in seine mathematischen Komponenten zerlegen und untersuchen: elliptische Kurven, Arbeiten modulo einer Zahl ( p), InverseMod usw. Code lesen ist gut für , "Es ist mir egal, warum oder wie es funktioniert, lass es einfach funktionieren". Diese Art von Forschung wäre besser, um die beteiligten Algorithmen zu verstehen.
Ich verstehe was du meinst. Meine Sorge ist, dass ich Code lesen und eine Vorstellung davon bekommen kann, was vor sich geht, aber ich verstehe nicht immer die vollständigen Einzelheiten oder folge ihm richtig. Wenn ich eine 30% ige Chance habe, jede Codezeile nicht zu verstehen, habe ich nach dem Lesen von 30 Codegleichungszeilen eine erhebliche Chance, dass ich so weit von der Basis abweiche, dass es nicht einmal lustig ist. Ich habe es recherchiert, aber selbst diesen Erklärungen folge ich nicht ganz, da sie Variablen zu wechseln scheinen. Sie sagen "k", dann sagen sie in anderen Gleichungen nicht genau, WIE sie einen Wert für "k" bekommen haben, oder sie nennen es vielleicht anders.
Tim, vielen Dank für die Mühe und Hilfe, die Sie geleistet haben, es hat mir einige weitere Informationen und bessere Blickwinkel gegeben, um dies zu betrachten. Aber ich habe gerade Stunden damit verbracht, dieses Zeug noch einmal durchzugehen. Es scheint ziemlich klar zu sein, dass die xors und komplizierten Teile Ihres Codes wahrscheinlich nicht Teil des Pub-to-Priv-Schlüssels sind, oder wenn sie ihre Reihenfolge in den Berechnungen nicht klar sind. Ich denke jedoch, dass Ihre Antwort, die hier bleibt, eine gute Referenz wäre.
Es liegt ein Fehler in der C#-Implementierung vor. Der Algorithmus ist korrekt, aber bei C# kann BigIntegerder Modulo-Operator %einen negativen Wert zurückgeben. Einfügen if (y3 < 0) y3 += p;nach den Berechnungen von y3in beiden Double()und operator +()korrigierte die problematischen Testfälle, auf die ich gestoßen bin; obwohl meine Tests nicht erschöpfend waren und es möglicherweise andere problematische Verwendungen von %hier gibt.

Ich nahm die Antwort von Tim S und entfernte mehr Zeug, bis es für mich auf eine einzige Seite passte:

https://gist.github.com/dooglu/3b1fcbc2449063a1c3f7f1003ca26447

#! /usr/bin/env python

class Point(object):
    def __init__(self, _x, _y, _order = None): self.x, self.y, self.order = _x, _y, _order

    def calc(self, top, bottom, other_x):
        l = (top * inverse_mod(bottom)) % p
        x3 = (l * l - self.x - other_x) % p
        return Point(x3, (l * (self.x - x3) - self.y) % p)

    def double(self):
        if self == INFINITY: return INFINITY
        return self.calc(3 * self.x * self.x, 2 * self.y, self.x)

    def __add__(self, other):
        if other == INFINITY: return self
        if self == INFINITY: return other
        if self.x == other.x:
            if (self.y + other.y) % p == 0: return INFINITY
            return self.double()
        return self.calc(other.y - self.y, other.x - self.x, other.x)

    def __mul__(self, e):
        if self.order: e %= self.order
        if e == 0 or self == INFINITY: return INFINITY
        result, q = INFINITY, self
        while e:
            if e&1: result += q
            e, q = e >> 1, q.double()
        return result

    def __str__(self):
        if self == INFINITY: return "infinity"
        return "04 %x %x" % (self.x, self.y)

def inverse_mod(a):
    if a < 0 or a >= p: a = a % p
    c, d, uc, vc, ud, vd = a, p, 1, 0, 0, 1
    while c:
        q, c, d = divmod(d, c) + (c,)
        uc, vc, ud, vd = ud - q*uc, vd - q*vc, uc, vc
    if ud > 0: return ud
    return ud + p

p, INFINITY = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL, Point(None, None) # secp256k1
g = Point(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798L, 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L,
          0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L)
secret =  0x18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725L
print '  privkey:    %x\n   pubkey: %s' % (secret, g * secret)

Erzeugt diese Ausgabe:

  privkey:    18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725
   pubkey: 04 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352 2cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6

Ich kann jetzt fast verstehen, wie es funktioniert. :)

Die Beschreibung der Punktmultiplikation auf Wikipedia war hilfreich, um zu verstehen, woher die lWerte kommen. lsteht für "Lambda".

Der öffentliche Schlüssel ist ein Punkt (x, y)auf der secp256k1-Kurve, der berechnet werden kann, indem der Basispunkt Gmit dem geheimen Schlüssel multipliziert wird sk. Hier ist eine in sich geschlossene prägnante Python-Funktion, die dies tut:

def sk_to_pk(sk):
    """
    Derive the public key of a secret key on the secp256k1 curve.

    Args:
        sk: An integer representing the secret key (also known as secret
          exponent).

    Returns:
        A coordinate (x, y) on the curve repesenting the public key
          for the given secret key.

    Raises:
        ValueError: The secret key is not in the valid range [1,N-1].
    """
    # base point (generator)
    G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
         0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8)

    # field prime
    P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F

    # order
    N = (1 << 256) - 0x14551231950B75FC4402DA1732FC9BEBF

    # check if the key is valid
    if not(0 < sk < N):
        msg = "{} is not a valid key (not in range [1, {}])"
        raise ValueError(msg.format(hex(sk), hex(N-1)))

    # addition operation on the elliptic curve
    # see: https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Point_addition
    # note that the coordinates need to be given modulo P and that division is
    # done by computing the multiplicative inverse, which can be done with
    # x^-1 = x^(P-2) mod P using fermat's little theorem (the pow function of
    # python can do this efficiently even for very large P)
    def add(p, q):
        px, py = p
        qx, qy = q
        if p == q:
            lam = (3 * px * px) * pow(2 * py, P - 2, P)
        else:
            lam = (qy - py) * pow(qx - px, P - 2, P)
        rx = lam**2 - px - qx
        ry = lam * (px - rx) - py
        return rx % P, ry % P

    # compute G * sk with repeated addition
    # by using the binary representation of sk this can be done in 256
    # iterations (double-and-add)
    ret = None
    for i in xrange(256):
        if sk & (1 << i):
            if ret is None:
                ret = G
            else:
                ret = add(ret, G)
        G = add(G, G)

    return ret

Die erforderlichen Parameter ( G, Pund N) finden Sie in der Spezifikation: http://www.secg.org/sec2-v2.pdf#page=13 . Beachten Sie, dass diese Implementierung auf Klarheit abzielt. Es ist überhaupt nicht effizient und wahrscheinlich auch nicht sicher gegen Seitenkanal-Timing-Angriffe. Ein Angreifer könnte die Ausführungszeit eines Aufrufs dieser Funktion nutzen, um Informationen über den geheimen Schlüssel abzuleiten.

Ich hatte auf eine solche Antwort gehofft und sie nicht gesehen, also hier ist meine:

Eine elliptische Kurve ("EC") ist eine Funktion, bei der das Quadrat der y-Koordinate gleich einem Polynom dritten Grades der x-Koordinate ist.

  1. Eine interessante Eigenschaft elliptischer Kurven ist, dass zwei beliebige Punkte auf einem EC eine Linie definieren, die die Kurve auch an einer weiteren Stelle trifft. Die "Summe" der ersten beiden Punkte ist als Spiegelbild (über der X-Achse) dieser Stelle definiert. Nachdem Sie also den Schnittpunkt gefunden haben, negieren Sie einfach die Y-Koordinate, und Sie haben den Punkt, der die "Summe" von ist andere zwei.
  2. Um sich selbst einen Punkt hinzuzufügen, verwenden Sie eine Linie, die den EC tangiert. Auch sie wird die Kurve irgendwo schneiden. (Sie können sehen, warum dies funktioniert, wenn Sie sich vorstellen, dass die beiden Punkte, die Sie hinzufügen, auf der Kurve immer näher zusammenrücken.)
  3. Sie müssen den "Erzeugungspunkt" ("G") so oft zu sich selbst addieren, wie es der private Schlüssel darstellt, um den Punkt zu finden, der Ihnen den öffentlichen Schlüssel gibt.
  4. Wenn Sie die Algebra machen, stellen Sie fest, dass die Gleichungen zum Identifizieren des Punktes auf der Kurve, der sich mit der Linie schneidet (entweder tangential zu einem Punkt, den Sie verdoppeln, oder durch zwei Punkte, die Sie hinzufügen), diejenigen sind, die in implementiert sind Python-Skript in einer anderen Antwort. **
  5. Um den Punkt G mal zu sich selbst [private-key] zu addieren, können Sie ihn in eine Binärzahl umwandeln. Sie können den Punkt G verdoppeln, indem Sie die Tangentenlinie und den Schnittpunkt wie oben beschrieben verwenden, um 2G (einen neuen Punkt) zu erhalten, und dann erneut für 4G, 8G usw. alle neuen Punkte. Jetzt ist also jede Bitposition in Ihrem (binären) privaten Schlüssel mit einem Punkt verbunden.
  6. Beginnen Sie mit dem zugehörigen Punkt des niederwertigsten Bits (LSB) als Ergebnis. Berechnen Sie für jedes andere Bit (links vom LSB) in Ihrem privaten Schlüssel, das 1 ist (überspringen Sie die Nullen), den "Summenpunkt" wie in Schritt 1 beschrieben, indem Sie den zugehörigen Punkt dieses Bits aus Schritt 5 und Ihr aktuelles Ergebnis verwenden. Wiederholen Sie dies, bis Sie alle Bits in Ihrem privaten Schlüssel erledigt haben. Die Addition ist assoziativ, sodass Sie sie in beliebiger Reihenfolge ausführen können.
  7. Stellen Sie sich vor, dass diese Kurve in ihrer unendlichen Fülle auf einer unendlich großen transparenten Ebene dargestellt wird. Stellen Sie sich nun vor, dass das Flugzeug in Quadrate zerlegt wird, sodass jedes Quadrat p Einheiten auf einer Seite hat, und dann werden alle Quadrate übereinander gestapelt. Somit liegen alle Punkte mit Koordinaten größer als p auf einem Punkt, für den beide Koordinaten kleiner als p sind. Das ist die Modulo-Funktion bei der Arbeit. Bei all der Mathematik, die Sie in den anderen 5 Schritten durchführen, finden Sie die Antwort modulo p, wenn Sie eine Antwort erhalten, die größer als p ist.
  8. Sie erhalten am Ende ein Ergebnis, das ein Punkt ist. Ich schätze, Sie verketten die X- und Y-Koordinaten und das ist Ihr öffentlicher Schlüssel, aber ich bin mir bei diesem letzten Schritt nicht so sicher.

So verstehe ich ECC, und es kann ungenau sein. Ich würde mich sehr über Korrekturen oder Fragen freuen, damit ich diese Beschreibung verfeinern kann. Angesichts der anderen Antworten hier bereits dachte ich, dass diese vielen Menschen helfen könnte.

**Es wäre schön, die Algebra zu sehen, die zeigt, wie man den Tangens des EC (für Verdopplungen) erhält, und auch für die Berechnung des Schnittpunkts zwischen einer durch zwei andere Punkte definierten Linie und der Kurve selbst.

Dies ist eine großartige Antwort. Das einzige, was in IMO fehlt, ist, wie der "Punkt im Unendlichen" als besonderer Punkt behandelt wird.