Now that you’re able to generate EC keypairs, the next step is using them to sign and verify messages. By message I mean any data –from text to binary– that needs to be authenticated. Specifically, Bitcoin clients produce signatures to authenticate their transactions, whereas miners verify such signatures to authorize and broadcast valid transactions.
This post is going to deal with generic messages. Later on, we’ll learn what kind of messages are involved in the Bitcoin transaction signing process.
Not surprisingly, the EC signature algorithm is ECDSA (Elliptic-Curve Digital Signature Algorithm). In ECDSA, all parties involved must agree on a hash function H, because we’re going to sign H(message) rather than the message itself. It’s worth noting that only the signing party S has access to the private key, while the verifying party V holds the corresponding public key to verify S signatures. I’ll reuse the same private key and public key from the previous post.
The following examples operate on SHA-256 digests, but keep in mind that Bitcoin’s designated H function is hash256, also known as double SHA-256 (read the article on hashes).
The first step is putting our message into a file, say ex-message.txt:
This is a very confidential message
whose SHA-256 digest is (don’t forget the trailing
45 54 81 3e 91 f3 d5 be 79 0c 7c 60 8f 80 b2 b0 0f 3e a7 75 12 d4 90 39 e9 e3 dc 45 f8 9e 2f 01
After that, we sign the SHA-256 digest of the message with the private key:
$ openssl dgst -sha256 -sign ec-priv.pem ex-message.txt >ex-signature.der
The ex-signature.der file is the message signature in DER format. OpenSSL uses the DER encoding for any binary output (keys, certificates, signatures etc.), but I’ll skip the underlying details. You don’t need to know the semantic of an ECDSA signature, just remember it’s a simple pair of big numbers .
You’ll probably notice that the signature changes each time you run the program, that is, the default signing process is not deterministic. This can be an issue when serializing blockchain transactions, because signatures are part of the transaction bytes and you probably remember that transaction bytes hash to the txid. As a consequence, the txid would change each time you sign a transaction. This behaviour is namely a source of transaction malleability.
To display a hex-encoded signature, just add the
$ openssl dgst -sha256 -hex -sign ec-priv.pem ex-message.txt
For a repeatable output, though, you’d better hexdump the DER file:
$ hexdump ex-signature.der
Whenever an authenticated message is published to the network, the readers expect to find a signature attached. Both files are the input to the verification routine, provided that we received the author’s public key in advance:
$ openssl dgst -sha256 -verify ec-pub.pem -signature ex-signature.der ex-message.txt
If the signature is verified, we’re able to state that the message is authentic.
The code below does what we did from the command line in the previous section.
OpenSSL makes the signing operation trivial, look at ex-ecdsa-sign.c:
ECDSA_SIG is a simple structure holding the pair described in the previous paragraph:
My test output (yours will differ):
r: 2B2B529BDBDC93E78AF7E00228B179918B032D76902F74EF454426F7D06CD0F9 s: 62DDC76451CD04CB567CA5C5E047E8AC41D3D4CF7CB92434D55CB486CCCF6AF2
i2d_ECDSA_SIG function we also get the DER-encoded signature:
that in my test is tidily rendered like this (can you spot
30 44 02 20 2b 2b 52 9b db dc 93 e7 8a f7 e0 02 28 b1 79 91 8b 03 2d 76 90 2f 74 ef 45 44 26 f7 d0 6c d0 f9 02 20 62 dd c7 64 51 cd 04 cb 56 7c a5 c5 e0 47 e8 ac 41 d3 d4 cf 7c b9 24 34 d5 5c b4 86 cc cf 6a f2
Verifying the signature is easy too, here’s ex-ecdsa-verify.c:
Since we don’t own the private key, we’ll have to decode
pub_bytes into a compressed public key with the following helper from ec.h:
On the other hand,
der_bytes is the DER-encoded signature returned by the signing program. We decode the DER signature into a convenient
ECDSA_SIG structure and then do the verification against the same SHA-256 message digest:
ECDSA_do_verify function returns:
1if the signature is valid.
0if the signature is not valid.
-1for unexpected errors.
Note: the signature decoding can be skipped by using
ECDSA_verify, which takes a DER-encoded signature directly.
Get the code!
Full source on GitHub.
Next block in chain?
You learned how to sign a message with a private key and how to verify a message signature with a public key.
This is the last post about generic crypto, phew! In the next article I’ll present the Bitcoin network. Please share this post if you enjoyed it and use the form below for questions and comments!