I’ve been wondering how Bitcoin addresses are generated. This post and the ones following will explore, step by step, how to transform a Bitcoin private key to a public address.
I know that Bitcoin public and private keys are Elliptic Curve DSA (ECDSA) key pairs, and I’ve seen the
Q = dG explanation on a few sites, but they leave out some details. I want to experiment for myself, so this post describes how to derive a public key from a private key with runnable code.
- Public Key Cryptography, or Asymmetric Cryptography, is a cryptographic system that uses pairs of keys: Public Key and Private Key. It is one of the most important (if not the most important) part of cryptocurrency protocols, and it is used in sev.
- Apr 29, 2018 Bitcoin Private Key Generator 2019 Bitcoin Private Key Scanner Marcene Kolb. Unsubscribe from Marcene Kolb? Cancel Unsubscribe. Subscribe Subscribed Unsubscribe 161.
- Question: How many public keys per private key / address? I'm curious about the structure of the relationship(s) between Bitcoin addresses, public keys, and private keys. My previous assumption was that from each private key would derive only one public key, and one address via nested RIPEMD160 and SHA256 one-way hash functions.
This is the first post in a four-part series titled “Computing a Bitcoin Address”. Here are all the articles in the series:
- Part 1: Private to Public Key (this post)
- Part 2: Public Key to (Hex) Address
- Part 3: Base58Check Encoding
- Part 4: Wallet Import Format (WIF)
The accepted Stack Overflow answer from the linked elliptic curve question above says that in the
Q = dG equation,
Q is the public key and
d is the private key, but does not explain
G, the group parameter. Luckily, some Googling quickly finds that Bitcoin uses the
secp256k1 ECDSA curve.
Jul 16, 2018 We’ll use this private key throughout the article to derive both a public key and the address for the Bitcoin wallet. What we want to do is to apply a series of conversions to the private key to get a public key and then a wallet address. Most of these conversions are called hash functions. Generate a Bitcoin address. With this generator it is possible to generate a random Bitcoin address.By clicking on the generate button based on the selection the Bitcoin public, wallet and private key then is generated. All keys can be copied to clipboard with the corresponding copy button.
Next, I looked at the OpenSSL
libcrypto C library, in
EC_KEY_generate_key (the function mentioned in the Stack Overflow post). Here’s the line that performs the multiplication:
In this case, I’m supplying
pub_key is the output parameter, so I just need the appropriate group for the first parameter. OpenSSL has already defined the
secp256k1 curve, so it’s just a matter of getting the right data representation. Here is the header for
EC_POINT_mul from the OpenSSL library:
Looks like we need an
EC_GROUP. To create one we call
Putting everything together, here’s a function
priv2pub that computes a public key from a private key (disclaimer: the code has no error-checking so don’t use this in production):
The function assumes that the input private key is in hex, and returned public key is in hex as well. I had to first convert the private key to
BIGNUM, which is OpenSSL’s number representation for arbitrary precision arithmetic. The computed public key is an OpenSSL
EC_POINT data structure, which represents a curve coordinate. The curve coordinate is converted back to hex using
EC_POINT_point2hex. Public keys can either be compressed or uncompressed, and the format of the output of
priv2pub depends on the
form input parameter, which can be one of three values.
To test this function, I found a sample public/private key pair from this Bitcoin wiki article. The private key from the article is
18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725 and the public key is
0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6. The public key begins with
0x04so we know it’s in uncompressed form (see section 2.3.3) and is 65 bytes long (see ANSI X9.62 for more details).
I used the following
main function to test if
priv2pub can compute the public key using the private key from the example:
I save the code above to a file
Let’s try another example. I generated a private key with bitaddress.org,
5JQZaZrYCbJ1Kb96vFBMEefrQGuNfHSqbHbviC3URUNGJ27frFe, but it’s in Base58Check encoding and not hex. We’ll deal with Base58 encoding later so for now I went to the “Wallet Details” tab at bitaddress.org, entered the base58 key, and found that the private key in hex is
4DD3D47E491C5D34F9540EBF3444E3D6675015A46B61AF37B4EB7F17DDDF4E61 and the public key is
0492EDC09A7311C2AB83EF3D133331D7B73117902BB391D9DAC3BE261547F571E171F16775DDA6D09A6AAF1F3F6E6AA3CFCD854DCAA6AED0FA7AF9A5ED9965E117. Let’s check what our code says:
A Racket Version
I’m using Racket, a modern LISP dialect, to experiment with Bitcoin so I want a Racket version of my conversion function as well. Fortunately, Racket has an FFI that enables Racket code to call C functions directly.
First I create a slightly different version of my C function:
The difference is an extra parameter and the representation of the public key output. This new function uses
EC_POINT_point2oct to return a byte string instead of a hex string. The problem is that the hex conversion function,
EC_POINT_point2hex, allocates, but I don’t want to manually manage memory in Racket. Because
priv2pub_bytes consumes an additional buffer parameter, Racket can allocate a buffer controlled by the GC prior to calling the function, and then pass in this allocated buffer.
Next I use Racket’s FFI to create a Racket wrapper function for the
priv2pub_bytes C function. The FFI requires a library file, so I compile the
.c file to a
This creates a dynamic library file named
To create a Racket value for this library, we use the Racket
The first argument to
ffi-lib is the path of the library and the second argument specifies a list of acceptable version numbers.
Once we have a hook into the C library, we can create Racket wrappers for individual functions in the library. We use the Racket
get-ffi-obj function to do this:
This creates a Racket function
priv2pub_bytes where the first argument is a (Racket) string, the second is an integer indicating whether the output should be in compressed or uncompressed form, and the third is the output buffer. A pointer to the output buffer is also returned by the function. We make the size the output buffer equal to the uncompressed form since that is the maximum size.
Let’s make things easier to use with a couple more functions:
Bitcoin Generate Private Key C Key
priv-key->pub-key consumes a private key in hex and returns an uncompressed public key, also in hex.
priv-key->pub-key/compressed is similar except it returns a compressed public key (note that this function extracts only the first 33 bytes of the output buffer).
Testing our examples again, with the Racket (extended) REPL, we see that our Racket functions produce the same results:
Private Key Bitcoin
The C code from this post is available here and the Racket code is available here. In this post, I’m using OpenSSL 1.0.1e and gcc 4.7.2, running in Debian 7.0. I had to install the
libssl-dev package to get the proper header files.