I want to further understand the signing process, especially when it comes to cold storage, so I built a set of shell scripts for OpenBSD/OSX/Linux.
I know about Armory, javascript libraries, ciyam, or other webpages for cold storage and signing… not the goal here.
Based on some old message by Pieter Wuille (September 29, 2012, "why OpenSSL?") here:
https://bitcointalk.org/index.php?topic=114064.0 --> "We use it for ECDSA signing and verification..."
I thought I’d use OPENSSL to sign and verify my code. (Is this still a valid approach?)
I created a raw trx, following the first 13 steps here:
http://bitcoin.stackexchange.com/questions/3374/how-to-redeem-a-basic-txand of course supported by
https://en.bitcoin.it/wiki/Protocol_specification#txExamination with the different offline tools shows the raw trx "looks ok". The raw trx produces the same sha256 hash as on the webpage.
Then I wanted to use "my own keys" to build a raw trx, and get it signed with OpenSSL. My unsigned raw trx would look like this:
010000000167cc10a3b7c5c52435770b6a1cb34a6a783803f028c42e95f9364d21e64b43c301000
0001976A9147A8911A06EF9A75A6CB6AF47D72A99A9B6ECB77988ACffffffff01EC8A0100000000
001976A9140DE4457D577BB45DEE513BB695BDFDC3B34D467D88AC00000000
To sign it, OPENSSL requires DER/PEM key for doing this at the command line. How to get them?
I used a (throw away) wallet, and dumped priv key. I converted the wif-c privkey via base58 conversion into hex.
On the public key:
in a shell script, I didn’t want to re-create the EC math to derive pubkey from privkey (maybe later, using bc and dc). So I looked up a previous trx (5a62b8697e33b59255ddab10b8f66ece0d5efec91c72134d07d90e03db61e051), what the hex code for the pubkey was… I appended this hex string with all the ASN1 fuzz, and then created via base64 conversion the OpenSSL PEM format. I verified the PEM file with "openssl asn1parse". And tested "openssl sign" with an example sha256.txt file. After signing, I "openssl verify"’d it. Result from openssl is "Signature Verified Successfully". So internally openssl "works" correctly, priv/pub key pair match (see the code below).
When I then assemble the trx and sign with OpenSSL, I get this:
010000000167cc10a3b7c5c52435770b6a1cb34a6a783803f028c42e95f9364d21e64b43c301000
0006A47304402206548cb4429b40f314711d46aa2e582b71041091f70064b276f2cbb800bb73d43
022011b6fe802a4f25dfe73a0c764add96011e43a02c41d2dd65164e2a4f0177ca8b01210293ccb
70fee4d33179c93bade0a9fefd62fde5ac53adc017649f513eec599509cffffffff01EC8A010000
0000001976A9140DE4457D577BB45DEE513BB695BDFDC3B34D467D88AC00000000
And when I try to broadcast via blockchain.info/de/pushtx, I get the message "Script resulted in a non-true stack: []".
Obviously OP_EQUALVERIFY and then OP_CHECKSIG don't get the expected result.
So I assume my OpenSSL key conversion is "somehow" wrong.
I double checked the exactly same unsigned raw trx in bitcoin-QT, and tried to sign it manually in bitcoin-QT, and broadcasted successfully
:
signrawtransaction '010000000167cc10a3b7c5c52435770b6a1cb34a6a783803f028c42e95f9364d21e64b43c301000
0001976A9147A8911A06EF9A75A6CB6AF47D72A99A9B6ECB77988ACffffffff01EC8A0100000000
001976A9140DE4457D577BB45DEE513BB695BDFDC3B34D467D88AC00000000' '[{"txid":"c3434be6214d36f9952ec428f00338786a4ab31c6a0b773524c5c5b7a310cc67","vout":1,"scriptPubKey":"76A9147A8911A06EF9A75A6CB6AF47D72A99A9B6ECB77988AC"}]' ‚[„$my_privkey“]‘
{
"hex": "010000000167cc10a3b7c5c52435770b6a1cb34a6a783803f028c42e95f9364d21e64b43c301000
0006a47304402203d15e12271521adb6e42c3f6a5eb5020883dd2042f23783e11385fb895fa1890
02200a84ba8e8055123abb22400b5dd90dbe670a1fa1cbbc206d5da59e54f4cc9c3501210293ccb
70fee4d33179c93bade0a9fefd62fde5ac53adc017649f513eec599509cffffffff01ec8a010000
0000001976a9140de4457d577bb45dee513bb695bdfdc3b34d467d88ac00000000",
"complete": true
}
Any help appreciated…
The code I use to assemble the PEM Key to sign with OpenSSL:
#!/bin/sh
# example keys from webpage:
hex_privkey=18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725
hex_pubkey=0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
sha256_string=9302bda273a887cb40c13e02a50b4071a31fd3aae3ae04021b0b843dd61ad18e
# ASN1 structure fill-ups:
pre_string=$( echo "30740201010420" )
mid_string=$( echo "a00706052b8104000aa144034200" )
echo "use pre defined ASN.1 strings to concatenate PEM keyfile:"
echo " a pre_string : $pre_string"
echo " the privkey : $hex_privkey"
echo " a mid_string : $mid_string"
result=$( echo $hex_pubkey | cut -b 1-66 )
echo " the pubkey : $result"
result=$( echo $hex_pubkey | cut -b 67- )
echo " $result"
# assemble the hex key
result=$( echo $pre_string$hex_privkey$mid_string$hex_pubkey | sed 's/[[:xdigit:]]\{2\}/\\x&/g' )
printf "$result" > tmpfile
echo " "
echo "and base64 this file. PEM keyfiles also want some nice surroundings: "
result=$( base64 tmpfile )
echo "-----BEGIN EC PRIVATE KEY-----" > privkey.pem
echo $result | cut -b 1-64 >> privkey.pem
echo $result | cut -b 65-128 >> privkey.pem
echo $result | cut -b 129- >> privkey.pem
echo "-----END EC PRIVATE KEY-----" >> privkey.pem
cat privkey.pem
echo " "
echo "corresponding pubkey.pem (openssl ec -in privkey.pem -pubout -out pubkey.pem)"
openssl ec -in privkey.pem -pubout -out pubkey.pem > /dev/null 2>&1
cat pubkey.pem
echo " "
echo "verifying the signing process with test data (openssl asn1parse -in privkey.pem)"
echo $sha256_string > sha256.txt
openssl asn1parse -in privkey.pem
echo "-->openssl pkeyutl -sign -in sha256.txt -inkey privkey.pem -keyform PEM > privkey_pem.sig"
openssl pkeyutl -sign -in sha256.txt -inkey privkey.pem -keyform PEM > privkey_pem.sig
echo "-->openssl pkeyutl -verify -in sha256.txt -inkey privkey.pem -sigfile privkey_pem.sig"
openssl pkeyutl -verify -in sha256.txt -inkey privkey.pem -sigfile privkey_pem.sig
echo " "