¿Verificando un bitcoin trx en la línea cmd de Unix con OpenSSL?

Quiero usar openssl en sistemas unixoide para verificar una transacción. Pisé errores al intentar verificar el hash de un bitcoin trx. Aquí estoy trabajando en Mac OSX y OpenSSL (1.0.2a 19 Mar 2015).

Sé que puedo usar el cliente QT, o las diversas soluciones python/php/java, este no es el objetivo aquí. También entiendo que bitcoin se mueve a libsecp256k1... Seguí el ejemplo de Pizza trx aquí:

¿Cómo funciona el algoritmo de verificación ECDSA durante la transacción?

La verificación no necesita la clave privada, solo la clave pública, el hash y la firma. Openssl ofrece dos formas de verificar un resultado:

openssl dgst -sha256 -verify pubkey.pem -signature tmpfile.sig sha256.txt

o

openssl pkeyutl -verify -pubin -inkey pubkey.pem -sigfile tmpfile.sig -in sha256.txt

La parte complicada es cómo pasar de la clave de publicación hexadecimal („042e930f39…ebcabb“) al formato PEM, que abre SSL para su verificación. Al final agregué los pasos, cómo transformé la llave hexadecimal a PEM.

Cuando uso la bitocina doble sha256 de la Pizza trx mencionada (también probé varias más), me sale este error:

openssl dgst -sha256 -verify pubkey.pem -signature tmpfile.sig sha256.txt
      Error Verifying Data
      140735175988048:error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag:tasn_dec.c:1198:
      140735175988048:error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error:tasn_dec.c:372:Type=ECDSA_SIG

o con pkeyutl:

openssl pkeyutl -verify -pubin -inkey pubkey.pem -sigfile tmpfile.sig -in sha256.txt
      Public Key operation error
      140735175988048:error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag:tasn_dec.c:1198:
      140735175988048:error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error:tasn_dec.c:372:Type=ECDSA_SIG

Cuando creo mis claves priv y pub con openssl y firmo una cadena "demo sha256", puedo verificar correctamente. Solo estas cadenas de bitcoin sha256s no funcionan. Debo estar perdiendo algo…

CREANDO el Archivo PEM:

Basado en varias lecturas aquí en el intercambio de pila, realicé ingeniería inversa de las claves pem de openssl. La clave con formato pem requiere una cadena previa PEM („3056301006072a8648ce3d020106052b8104000a034200“) y la clave pública adjunta.

$ result=3056301006072a8648ce3d020106052b8104000a034200042e930f39ba62c6534ee98ed20ca98959d34aa9e057cda01cfd422c6bab3667b76426529382c23f42b9b08d7832d4fee1d6b437a8526e59667ce9c4e9dcebcabb
$ result=$( echo $result | sed 's/[[:xdigit:]]\{2\}/\\x&/g' )
$ printf $result > tmpfile
$ hexdump -C tmpfile
00000000  30 56 30 10 06 07 2a 86  48 ce 3d 02 01 06 05 2b  |0V0...*.H.=....+|
00000010  81 04 00 0a 03 42 00 04  2e 93 0f 39 ba 62 c6 53  |.....B.....9.b.S|
00000020  4e e9 8e d2 0c a9 89 59  d3 4a a9 e0 57 cd a0 1c  |N......Y.J..W...|
00000030  fd 42 2c 6b ab 36 67 b7  64 26 52 93 82 c2 3f 42  |.B,k.6g.d&R...?B|
00000040  b9 b0 8d 78 32 d4 fe e1  d6 b4 37 a8 52 6e 59 66  |...x2.....7.RnYf|
00000050  7c e9 c4 e9 dc eb ca bb                           ||.......|

Esto será codificado en base64 y se agregará un entorno agradable.

$ openssl enc -base64 -in tmpfile -out pubkey.pem
$ cat pubkey.pem 
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELpMPObpixlNO6Y7SDKmJWdNKqeBXzaAc
/UIsa6s2Z7dkJlKTgsI/QrmwjXgy1P7h1rQ3qFJuWWZ86cTp3OvKuw==

y le damos a pubkey.pem un entorno agradable, para que se vea así:

-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELpMPObpixlNO6Y7SDKmJWdNKqeBXzaAc
/UIsa6s2Z7dkJlKTgsI/QrmwjXgy1P7h1rQ3qFJuWWZ86cTp3OvKuw==
-----END PUBLIC KEY-----

y verifique a través de asn la estructura:

$ openssl asn1parse -in pubkey.pem 
    0:d=0  hl=2 l=  86 cons: SEQUENCE
    2:d=1  hl=2 l=  16 cons: SEQUENCE
    4:d=2  hl=2 l=   7 prim: OBJECT            :id-ecPublicKey
   13:d=2  hl=2 l=   5 prim: OBJECT            :secp256k1
   20:d=1  hl=2 l=  66 prim: BIT STRING

Los archivos que usé:

$ cat pubkey.pem  
-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELpMPObpixlNO6Y7SDKmJWdNKqeBXzaAc
/UIsa6s2Z7dkJlKTgsI/QrmwjXgy1P7h1rQ3qFJuWWZ86cTp3OvKuw==
-----END PUBLIC KEY-----

$ cat tmpfile.sig
30450221009908144ca6539e09512b9295c8a27050d478fbb96f8addbc3d075544dc41328702201aa528be2b907d316d2da068dd9eb1e23243d97e444d59290d2fddf25269ee0e

$ cat sha256.txt
692678553d1b85ccf87d4d4443095f276cdf600f2bb7dd44f6effbd7458fd4c2

Respuestas (1)

Obtuviste la clave pública correcta, pero al menos una cosa está mal y tal vez más en las partes que no mostraste.

En primer lugar y fundamentalmente, bitcoin utiliza un esquema no convencional (posiblemente no estándar) en el que los datos se duplican antes del cálculo nonceG,kinv(hash+nonceimage) y la verificación correspondiente. dgst -sign/-verifysolo hace el hash único estándar, por lo que debe hacer hash primero y luego usar dgst -sign/verifyo hash dos veces y luego usar pkeyutl -sign/verify.

Además, el archivo de firma, el archivo de entrada de verificación (hash o de otro tipo) y las entradas de hash deben ser datos binarios sin procesar , no la representación hexadecimal.

Por lo tanto:

$ cat pizza.inphex
01000000018dd4f5fbd5e980fc02f35c6ce145935b11e284605bf599a13c6d41
5db55d07a1000000001976a91446af3fb481837fadbb421727f9959c2d32a368
2988acffffffff0200719a81860000001976a914df1bd49a6c9e34dfa8631f2c
54cf39986027501b88ac009f0a5362000000434104cd5e9726e6afeae357b180
6be25a4c3d3811775835d235417ea746b7db9eeab33cf01674b944c64561ce33
88fa1abd0fa88b06c44ce81e2234aa70fe578d455dac0000000001000000
$ xxd -r -p <pizza.inphex >pizza.inpraw
$ cat pizza.sighex
30450221009908144ca6539e09512b9295c8a27050d478fbb96f8addbc3d075544dc41328702201aa528be2b907d316d2da068dd9eb1e23243d97e444d59290d2fddf25269ee0e
$ xxd -r -p <pizza.sighex >pizza.sigraw
$ cat pizza.keyhex
3056301006072a8648ce3d020106052b8104000a034200
042e930f39ba62c6534ee98ed20ca98959d34aa9e057cda01cfd422c6bab3667b76426529382c23f42b9b08d7832d4fee1d6b437a8526e59667ce9c4e9dcebcabb
$ xxd -r -p <pizza.keyhex | openssl pkey -pubin -inform der >pizza.keypem

$ openssl sha256 <pizza.inpraw -binary >pizza.hash1; xxd pizza.hash1
0000000: 0838 6747 8cb0 d1d8 bb86 4175 bbc4 9728  .8gG......Au...(
0000010: cffc c114 bc2e 762c 6df6 4f2c 965a 9a66  ......v,m.O,.Z.f
$ openssl sha256 <pizza.hash1 -verify pizza.keypem -signature pizza.sigraw
Verified OK

$ openssl sha256 <pizza.hash1 -binary >pizza.hash2; xxd pizza.hash2
0000000: c2d4 8f45 d7fb eff6 44dd b72b 0f60 df6c  ...E....D..+.`.l
0000010: 275f 0943 444d 7df8 cc85 1b3d 5578 2669  '_.CDM}....=Ux&i
$ openssl pkeyutl <pizza.hash2 -verify -pubin -inkey pizza.keypem -sigfile pizza.sigraw
Signature Verified Successfully

Tenga en cuenta que el segundo hash es el que se muestra en el elemento 4 #32305, excepto que el software de amaclin muestra los bytes en orden inverso.

Gracias Dave, preciso, nítido y claro. La representación hexadecimal y los datos sin procesar eran la parte confusa. Gracias, gracias, Volker
Hola, cuando convierto pizza.keyhex a pizza.keypem aparece el error "no se puede cargar la clave pública". ¿Por qué? Seguí exactamente los mismos comandos que tú.
@user2298995: No sé; debería funcionar y no es muy complicado. Asegúrese de tener los datos exactamente correctos. ¿Se desplazó completamente hacia la derecha? Asegúrese de que su xxd funcione: coloque la salida en un archivo y examínelo, luego intente leer ese archivo (en lugar de canalizar) con pkey -inform der -pubiny si eso falla con asn1parse -inform der.