3

I want to use openssl on unixoide systems to verify a transaction. I step on errors, when trying to verify a bitcoin trx’s hash. Here I am working on Mac OSX and OpenSSL (1.0.2a 19 Mar 2015).

I know I can use the QT client, or the several python/php/java solutions, this is not the goal here. I also understand, that bitcoin moves to libsecp256k1… I followed the example from the Pizza trx here:

How does the ECDSA verification algorithm work during transaction?

Verifying does not need the private key, only pubkey, hash and sig. Openssl offers two ways to verify a result:

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

or

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

Tricky part is, how to get from the hex pub key („042e930f39…ebcabb“) to the PEM format, which openssl wants for verification. At the end I added the steps, how I transformed the hex key to PEM.

When I use the bitocin double sha256 of the mentioned Pizza trx (I also tried several others), I get this 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

or with 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

When I create my priv and pub keys with openssl, and sign a „demo sha256“ string, I can verify correctly. Only these bitcoin sha256s strings don’t work. I must be missing something…

CREATING the PEM File:

based on several readings here in the stackexchange, I reverse engineered the pem keys from openssl. The pem formatted key wants a PEM_prestring („3056301006072a8648ce3d020106052b8104000a034200“) and the pubkey attached.

$ 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                           ||.......|

This will be base64 encoded, and added some nice surroundings.

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

and we give pubkey.pem some nice surroundings, to make it look like this:

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

and check via asn the structure:

$ 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

The files I used:

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

$ cat tmpfile.sig
30450221009908144ca6539e09512b9295c8a27050d478fbb96f8addbc3d075544dc41328702201aa528be2b907d316d2da068dd9eb1e23243d97e444d59290d2fddf25269ee0e

$ cat sha256.txt
692678553d1b85ccf87d4d4443095f276cdf600f2bb7dd44f6effbd7458fd4c2
2

You got the pubkey correct but at least one thing wrong and maybe more in the parts you didn't show.

First and fundamentally, bitcoin uses an unconventional (arguably nonstandard) scheme where the data is double-hashed before the nonceG,kinv(hash+nonceimage) calculation and corresponding verification. dgst -sign/-verify only does the standard single hash, so you must either hash first and then use dgst -sign/verify or hash twice and then use pkeyutl -sign/verify.

Also the signature file, verify input file (hash or otherwise), and hash input(s) must be raw aka binary data, not the hex representation.

Thus:

$ 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

Note that second hash is the one shown in #32305 item 4, except amaclin's software displayes the bytes in reverse order.

  • thx Dave, precise, crisp and clear. The hex represention and the raw data was the confusing part. thx, rgds, Volker – pebwindkraft Jul 11 '16 at 9:44
  • Hi, when converting pizza.keyhex to pizza.keypem I get an error "unable to load Public Key" Why is that? I followed the exact same commands as you did. – user2298995 Dec 26 '17 at 10:36
  • @user2298995: I don't know; it should work and isn't very complicated. Make sure you have the data exactly correct -- did you scroll all the way to the right? Make sure your xxd works: put the output in a file and examine it, then try reading that file (instead of piping) with pkey -inform der -pubin and if that fails with asn1parse -inform der. – dave_thompson_085 Dec 27 '17 at 8:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.