1

I want to understand how Bitcoin sign transactions with Openssl. I saw this post But I have some issues.

Above my private and public keys.

Private keys:

$ cat chiave_priv_3.pem 

    -----BEGIN EC PARAMETERS-----
    BgUrgQQACg==
    -----END EC PARAMETERS-----
    -----BEGIN EC PRIVATE KEY-----
    MHQCAQEEIOLcjvH4RhZFl1hthXl7DD3MHXUCWKiI2b/zoYlvBmKboAcGBSuBBAAK
    oUQDQgAE3Qc0PacS/HmhnZjIot48dZ++rvh121Rq+xjreFKrf/QCl2sDXTncDbe0
    wcCtq4yaUpdbmzV9OgrP94EFsEC/1w==
    -----END EC PRIVATE KEY-----

public key compressed

$ cat chiave_pubblica_compressa_3.txt 
03dd07343da712fc79a19d98c8a2de3c759fbeaef875db546afb18eb7852ab7ff4

I'm working on regtest and my UTXO is:

 {
    "txid": "0c647aadcb4028260ecb753c727e0237658873a0cbdbeb5695b8cb85ea87f98d",
    "vout": 0,
    "address": "myiGTzGG8rJikr2HcVyPYqprh6Ds1kdY7v",
    "label": "",
    "scriptPubKey": "76a914c79602205abbe1d35ee6dcb4a19791cbd5a26e1588ac",
    "amount": 49.99900000,
    "confirmations": 6,
    "spendable": true,
    "solvable": true,
    "desc": "pkh([c7960220]03dd07343da712fc79a19d98c8a2de3c759fbeaef875db546afb18eb7852ab7ff4)#5c03vyak",
    "safe": true
  },

I can create transaction data with this parameters:

TXID=0c647aadcb4028260ecb753c727e0237658873a0cbdbeb5695b8cb85ea87f98d
VOUT=0
AMOUNT=49.998
ADDR_MITT=msPjuNgbmbRSkNGJPvquKJRRmrbzS96s62

bitcoin-cli createrawtransaction '[{"txid":"'$TXID'","vout":'$VOUT'}]' '[{"'$ADDR_MITT'":'$AMOUNT'}]'

02000000018df987ea85cbb89556ebdbcba073886537027e723c75cb0e262840cbad7a640c0000000000ffffffff01c0e4022a010000001976a914824441111b374bec1952a5b3fa9dd4e3ed679b3888ac00000000

Now I get the scriptPubKey and put in ScriptSig and add SIGHASH in little endian.

02000000018df987ea85cbb89556ebdbcba073886537027e723c75cb0e262840cbad7a640c000000001976a914c79602205abbe1d35ee6dcb4a19791cbd5a26e1588acffffffff01c0e4022a010000001976a914824441111b374bec1952a5b3fa9dd4e3ed679b3888ac0000000001000000

Where

02000000 
01
8df987ea85cbb89556ebdbcba073886537027e723c75cb0e262840cbad7a640c
00000000
19 76a914c79602205abbe1d35ee6dcb4a19791cbd5a26e1588ac
ffffffff
01
c0e4022a01000000
19 76a914824441111b374bec1952a5b3fa9dd4e3ed679b3888ac
00000000
01000000

Sha256 twice

$ printf 02000000018df987ea85cbb89556ebdbcba073886537027e723c75cb0e262840cbad7a640c000000001976a914c79602205abbe1d35ee6dcb4a19791cbd5a26e1588acffffffff01c0e4022a010000001976a914824441111b374bec1952a5b3fa9dd4e3ed679b3888ac0000000001000000 | xxd -r -p | sha256sum -b | xxd -r -p | sha256sum -b | awk '{ print $1 }' > a.txt
daac47088b7eba5593282013442dbcba59556d095982d470f0a02972269bc0e1 

Sign with chiave_priv_3.pem

$ openssl dgst -sha256 -hex -sign chiave_priv_3.pem a.txt | sed 's/^.* //'                                                           
304402207071c9fd7341a15cacfea72e215b44bed42d1a00c7fc5bfed5e01d36acd953c20220308e45aedd080ba65993b69b4c82cc928db8c3e70a6f402fe0258157a4690c6e

Add 01 at the end of signature and check the length

printf 304402207071c9fd7341a15cacfea72e215b44bed42d1a00c7fc5bfed5e01d36acd953c20220308e45aedd080ba65993b69b4c82cc928db8c3e70a6f402fe0258157a4690c6e01 | wc -c
 142

142 char is 47

Debug DER signature

30 DER prefix
44 Length of rest of Signature
02 Marker for r value
20 Length of r value
r = 7071c9fd7341a15cacfea72e215b44bed42d1a00c7fc5bfed5e01d36acd953c2
02 Marker for s value
20 Length of s value
s = 308e45aedd080ba65993b69b4c82cc928db8c3e70a6f402fe0258157a4690c6e
01 SIGHASH_ALL

concatenate with public key compressed

47304402207071c9fd7341a15cacfea72e215b44bed42d1a00c7fc5bfed5e01d36acd953c20220308e45aedd080ba65993b69b4c82cc928db8c3e70a6f402fe0258157a4690c6e012103dd07343da712fc79a19d98c8a2de3c759fbeaef875db546afb18eb7852ab7ff4

the whole scriptSig length is: 6A (212 char hex) Now I can build my transaction:

02000000018df987ea85cbb89556ebdbcba073886537027e723c75cb0e262840cbad7a640c000000006A47304402207071c9fd7341a15cacfea72e215b44bed42d1a00c7fc5bfed5e01d36acd953c20220308e45aedd080ba65993b69b4c82cc928db8c3e70a6f402fe0258157a4690c6e012103dd07343da712fc79a19d98c8a2de3c759fbeaef875db546afb18eb7852ab7ff4ffffffff01c0e4022a010000001976a914824441111b374bec1952a5b3fa9dd4e3ed679b3888ac00000000

Sendrawtransaction

bitcoin-cli sendrawtransaction 02000000018df987ea85cbb89556ebdbcba073886537027e723c75cb0e262840cbad7a640c000000006A47304402207071c9fd7341a15cacfea72e215b44bed42d1a00c7fc5bfed5e01d36acd953c20220308e45aedd080ba65993b69b4c82cc928db8c3e70a6f402fe0258157a4690c6e012103dd07343da712fc79a19d98c8a2de3c759fbeaef875db546afb18eb7852ab7ff4ffffffff01c0e4022a010000001976a914824441111b374bec1952a5b3fa9dd4e3ed679b3888ac00000000
error code: -26
error message:
mandatory-script-verify-flag-failed (Signature must be zero for failed CHECK(MULTI)SIG operation) (code 16)`
3

The problem is that you are doing all of the hashing (and putting it into hex) and then having OpenSSL hash it again. openssl dgst will hash the message before signing, but this is incorrect for Bitcoin. Traditionally, in ECDSA, the message is hashed once and then signed. But with Bitcoin, it is actually hashed twice. Another way to think of this is that the message is a hash.

So you have already double hashed the transaction. But OpenSSL will hash it again, making it triple-hashed, which means that you sign a different message. Furthermore, OpenSSL will read your message as binary data, not interpret the hex that you gave it. So really your hashing command should be

printf 02000000018df987ea85cbb89556ebdbcba073886537027e723c75cb0e262840cbad7a640c000000001976a914c79602205abbe1d35ee6dcb4a19791cbd5a26e1588acffffffff01c0e4022a010000001976a914824441111b374bec1952a5b3fa9dd4e3ed679b3888ac0000000001000000 | xxd -r -p | sha256sum -b | xxd -r -p > a.txt

This will hash the sighash message once and put the binary data into a.txt.

Then you can sign it as you did.


Alternatively, OpenSSL has a pkeyutl tool that does not do any additional hashing on the data to be signed and instead expects that you have already hashed it. You could use this instead, but you will still need your hash to be binary rather than hex. So you can do

printf 02000000018df987ea85cbb89556ebdbcba073886537027e723c75cb0e262840cbad7a640c000000001976a914c79602205abbe1d35ee6dcb4a19791cbd5a26e1588acffffffff01c0e4022a010000001976a914824441111b374bec1952a5b3fa9dd4e3ed679b3888ac0000000001000000 | xxd -r -p | sha256sum -b | xxd -r -p | sha256sum -b | xxd -r -p > a.txt

to get the double hash. Then do

openssl pkeyutl -inkey chiave_priv_3.pem a.txt -sign -in a.txt -pkeyopt digest:sha256 | xxd -p -c 256

to sign.

|improve this answer|||||
  • Thanks for reply, I tried both solutions but I get the same error. the second solution has a little error openssl pkeyutl -inkey chiave_priv_3.pem -sign -in a.txt -pkeyopt digest:sha256 | xxd -p -c 256 – monkeyUser Feb 17 at 18:55
  • It works but sometime I get this error mandatory-script-verify-flag-failed (Non-canonical signature: S value is unnecessarily high) (code 16) – monkeyUser Feb 19 at 11:16
  • 1
    That's expected. There's a standardness rule that requires the S value to be low. You can tell if the S value is low by checking it's length; it needs to be 32 bytes or less. A high S can be converted into a low S by negating it, but if you don't know how to do that manually, just make signatures until one has a low S. There's a 50% probability that any given signature has a low S. – Andrew Chow Feb 19 at 18:05
  • thanks for your time, I will repeat the signature for now. Next step is sign P2SH custom. – monkeyUser Feb 19 at 22:07

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.