¿Cómo puedo, con extrema especificidad, convertir una clave de bitcoin privada dada en una clave de bitcoin pública? NO, ¿dónde puedo encontrar un programa que haga esto, pero si lo hiciera yo mismo, qué haría?
Llave privada:
18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725
supuestamente da como resultado la clave pública:
0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6
Otros han preguntado cómo pasar de privado a público, no he visto una respuesta realmente específica, solo una dirección más general, pero ninguna respuesta explica todas las variables. Entiendo que esto es bastante complejo y si un individuo determinado piensa que es demasiado trabajo para responder, lo respeto totalmente. Nota: No me importa la dirección de Bitcoin, solo me interesa la clave privada a la clave pública y los detalles de cómo.
Variables como "a" y "b" en el algoritmo de curva ECDSA ya están designadas por Bitcoin (según https://en.bitcoin.it/wiki/Secp256k1 ). el "punto base" también conocido como "G" también se especifica en esa página. el "exponente privado" o "k", todavía tengo que encontrarlo. Algunas de estas variables son supuestamente "aleatorias", lo que parece ser falso, ya que cada generador en el que puede poner una clave privada parece siempre escupir la misma clave pública... así que... todas las variables ya están preestablecidas o se derivan de la clave privada.
gracias por cualquier ayuda en esto. He estado tratando de investigar y entender esto durante días, pero parece que a veces no entiendo los términos y/o las notaciones, pero creo que he superado eso y ahora solo me faltan partes de la ecuación.
EDITAR AGREGAR:
Esta es la clave privada indicada anteriormente en Decimal:
11253563012059685825953619222107823549092147699031672238385790369351542642469
Esta es la clave pública indicada anteriormente (valores x e y) en decimal:
36422191471907241029883925342251831624200921388586025344128047678873736520530
20277110887056303803699431755396003735040374760118964734768299847012543114150
Todo lo que quiero saber es cómo pasar de esa clave privada a la clave pública. Supuestamente, es una ecuación simple que no implica cambio de bits o xor, etc. Puede incluir "multiplicación de puntos" (que no veo cómo se puede multiplicar un punto definido como x y ay). Nadie parece entender las complejidades de esto. ¿Sugieren que ofrezca una fracción de bitcoin a quien lo explique claramente?
Intentaré responder esto nuevamente de una manera diferente, usando números pequeños para que sea legible.
convierta la clave privada a representación binaria, de modo que el número decimal 105, que es 0x69 en hexadecimal, se convierta en 01101001.
calcular esta lista de puntos, duplicando repetidamente el punto generador 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
escriba los bits de la clave privada al lado de esta lista de esta manera:
privkey pointlist
1 1*G
0 2*G
0 4*G
1 8*G
0 16*G
1 32*G
1 64*G
ahora comience a agregar solo aquellos puntos que tienen un 1
escrito junto a ellos.
9*G = 1*G + 8*G
41*G = (9+32)*G = 9*G + 32*G
105*G = (41+64)*G = 41*G + 64*G
ahora ha calculado la clave pública para la clave privada 105 usando solo operaciones de duplicación de puntos y adición de puntos.
El valor real será:
(0xf219ea5d6b54701c1c14de5b557eb42a8d13f3abbcd08affcc2a5e6b049b8d63,
0x4cb95957e83d40b0f73af4544cccf6b1f4b08d3c07b27fb8d8c2962a400766d1)
La clave pública estará entonces en uno de dos formatos:
0x04
La clave pública completa es 0x04, seguida de 32 bytes de la coordenada x, seguida de 32 bytes de la coordenada y.
La clave pública comprimida es 0x02 o 0x03 dependiendo de si la coordenada y es un número par o impar, seguida de 32 bytes de la coordenada x.
Luego, algunas notas más sobre su pregunta:
Algunos textos antiguos solían referirse a la multiplicación de puntos escalares, como exponenciación, es por eso que a veces la clave privada se refiere al exponente privado.
los parámetros de la curva se eligieron aleatoriamente una vez. lo que significa que los diseñadores de la curva secp256k1 intentaron sin éxito transmitir que no existe una estructura específica para esta curva. Lo que significa que la NSA podría o no haber puesto una puerta trasera matemática en los parámetros de la curva.
al usar esta curva y generar tu clave pública, -tienes- que elegir tu clave privada al azar, de manera que sea imposible que nadie la adivine.
el generador G
es un punto específico de la curva elíptica, definido en la curva secp256k1 .
ecdsa.SigningKey.from_string(s.decode('hex'), curve=ecdsa.SECP256k1).verifying_key
¡Gracias! ¡Por favor publique una dirección de bitcoin! (Editar: lo encontré en su sitio web vinculado)Aquí hay un script de Python autónomo que realiza la conversión. Puede verificar su trabajo comparándolo con ingresar su clave privada como el "Exponente secreto" en Brainwallet . Tomé el script de este hilo de Bitcointalk y eliminé cosas innecesarias (como el código para usar la clave pública para firmar un mensaje y verificar esa firma).
Convertir Python en instrucciones para un ser humano se deja como un ejercicio para el lector (aunque diría que en un escenario como este código de Python, con la documentación adecuada, está bien como instrucciones para un ser humano). Tenga en cuenta que es completamente posible calcular esto con lápiz y papel, pero podría llevar un tiempo y es probable que cometa un error debido a que tiene que lidiar con números tan enormes.
También tenga en cuenta que no hay operaciones individuales aquí que sean mucho más complicadas de lo que aprendería en la escuela primaria/primaria. Hay comparaciones básicas < > ==
, aritmética + - *
, división en la que te preocupas por el cociente /
, el resto %
o ambos divmod
, y AND bit a bit ( &
, que es bastante fácil si trabajas en hexadecimal; o se puede replicar con aritmética).
No creo que un niño de 5 años (que no sea un genio) pueda hacerlo (lo siento, la bruja malvada gana esta ronda), pero creo que un adulto promedio con suficiente paciencia podría aprender las matemáticas necesarias en muy poco tiempo (con el Secuencia de comandos de Python como ... bueno ... secuencia de comandos, a seguir). Sin embargo, calcular incluso una sola clave pública sin la ayuda de dispositivos informáticos electrónicos podría llevar mucho tiempo (aproximadamente: años).
#! /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 '======================================================================='
Consulte también una versión aún más simplificada escrita en 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 > 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;
}
}
new CurveFp(p,a,b)
, los números simplemente se almacenan. Puedes pensar en una curva como un par de tres números. De manera similar, a Point
es simplemente un par de a Curve
y dos números. Todos estos números son necesarios para multiplicar a Point
por un número, que es lo que sucede en generator256 * secret
. El resultado de esta multiplicación (muy complicada) es un Point
; su X e Y, concatenados entre sí, son la clave pública. Y creo que el código (C#) se aplica todo; puede recorrer el C# en un depurador con VS Express para ver.p
), InverseMod , etc. Leer código es bueno para , "No me importa por qué o cómo funciona, solo haz que funcione". Ese tipo de investigación sería mejor para comprender los algoritmos involucrados.BigInteger
el operador de módulo %
puede devolver un valor negativo. Insertar if (y3 < 0) y3 += p;
después de los cálculos de y3
en ambos Double()
y operator +()
rectificar los casos de prueba problemáticos que encontré; aunque mi prueba no fue exhaustiva y puede haber otros usos problemáticos %
aquí.Tomé la respuesta de Tim S y eliminé más cosas hasta que me quedó en una sola página:
https://gist.github.com/dooglus/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)
Produce esta salida:
privkey: 18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725
pubkey: 04 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352 2cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
Casi puedo entender cómo funciona ahora. :)
La descripción de la multiplicación de puntos en Wikipedia fue útil para comprender de dónde l
provienen los valores. l
significa 'lambda'.
La clave pública es un punto (x, y)
en la curva secp256k1 que se puede calcular multiplicando el punto base G
con la clave secreta sk
. Aquí hay una función de python concisa e independiente, que hace esto:
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
Los parámetros requeridos ( G
, P
y N
) se pueden encontrar en la especificación: http://www.secg.org/sec2-v2.pdf#page=13 . Tenga en cuenta que esta implementación tiene como objetivo la claridad. No es eficiente en absoluto y probablemente tampoco sea seguro contra ataques de sincronización de canal lateral. Un atacante podría usar el tiempo de ejecución de una llamada a esta función para obtener información sobre la clave secreta.
Esperaba una respuesta como esta y no la vi, así que aquí está la mía:
Una curva elíptica ("EC") es una función en la que el cuadrado de la coordenada y es igual a un polinomio de tercer grado de la coordenada x.
Así es como entiendo ECC, y puede ser inexacto. Agradecería mucho cualquier corrección o pregunta para poder perfeccionar esta descripción. Dadas las otras respuestas aquí ya, pensé que esta podría ayudar a mucha gente.
**Sería bueno ver el álgebra que muestra cómo obtener la tangente de la EC (para duplicaciones) y también para el cálculo del punto de intersección entre una línea definida por otros dos puntos y la propia curva.
greg hewgill
Mío
Felipe Voloch