My first post on this journal will be dedicated to
Bitcoin addresses. As addresses are in the hearth of Bitcoin’s protocol and softwares (and basically, allowing keeping a lot of coins), they changed a lot in the past decade, supporting multiple formats and with different tools to generate and manipulate them.
On this page, I’ll try to write most information I’ve collected on those the last monthes.
Creating a Bitcoin address
At the beginning, a Bitcoin address is simply a representation of a public key generated from a ECDSA secp256k1 private key, and using a small hashing algorithm.
Using Openssl to create a ECDSA secp256k1 private & public key
On this Stackexchange post, there is method to create a private/public ECDSA key using
Firstly, let’s create a private key in
Then, its related public key, in PEM format too.
As those are PEM files, we need to extract private key bytes in order to use them to be able to import them in Bitcoin core client.
If you need to understand
DER format, you can analyze its structure by using
(Key is at the 8th byte because we have sequence information, then an integer before reaching our key. Some ASN.1 format knowledge is mandatory!)
Then, grab the public key bytes, but this is optional as you’ll be able to get them again using private key when preparing your Bitcoin address.
Those keys are not yet usable in Bitcoin, but if you are curious enough, you’ll note that this public key & the Bitcoin’s genesis block one are the same format:
65 bytes, starting by 0x04.
Getting our Bitcoin address
There is still some work, using the public key we’ve just got, to have a ready to use Bitcoin address. The processus is described right there, but let’s take a deeper look.
Step 0: We have our ECDSA private key
In my examples, my private key is
4e7b7fb13697e6940c08d0bd9adefdea127047797bcdea85bb25d8bdd101aa33, and it is a 32 byte key. So far, so good.
Step 1: We also have corresponding public key.
In its uncompressed format, our public key is 65 bytes long, and starting by 0x04. Public key also have compressed form, starting by either 0x02 or 0x03, and we will talk about it later.
My public key is
Step 2: We perform SHA-256 on our private key.
SHA-256 hash is
Step 3: We now perform RIPEMD-160 on the last result.
RIPEMD-160 hash is
Step 4: We need to add network prefix.
The network prefix must be added. This prefix is
0x00 for Bitcoin main network, but differs along different networks & testnets. Bitcoin testnet’s is 0x6f, P2SH’s prefix is
0x05, etc. Other coins, such litecoin, have their own set of prefix too. This page has an exhaustive list of Bitcoin’s address prefixes.
Then, our key is now
Let’s store this somewhere, as we will be using it at the end. Next steps are about creating a checksum of this key for validation purposes.
Step 5: Perform a SHA-256 hash on the latest result
SHA-256 hash is
Step 6: Again, perform a last SHA-256 hash
It is now
Step 7: Appending first 4 bytes of last checksum to extended RIPEMD-160 key
From the last hash computed in
step 6, we take the first 4 bytes,
83562b05, and we append to the RIPEMD-160 hash we got at
step 4 (the one starting by the network prefix,
Our key is now
Final step: Encode this key using base58.
The key becomes
12m7LTjx5vcx23MeumZuNtAyG3qEcj2rn8. This is our bitcoin address.
Let’s take a deeper look with some real python code. Source code for this script is also available on github.
Let’s now run it:
Here we go. We have our brand fresh new bitcoin address (in its 1st version format and uncompressed). There is other formats for this public key: its compressed form, its P2SH (starting by 3) form, and its bech32 (segwit) form. We’ll talk about it later.
Like public address, bitcoin is using its own format for importing/exporting private keys. It’s called the Wallet import format.
Steps to create a WIF from a raw private key are easy.
Step 0: We have our ECDSA private key (again)
My private key still is
Step 1: We add the main net prefix for private keys.
Like public keys, you’ve to add a prefix. This prefix is
0x80 for mainnet private keys, and
0xef for testnet addresses.
Step 2: Preparing checksum (using SHA-256 twice)
Again, let’s run SHA-256 twice on this:
And we note down the first 4 bytes of the last hash.
Step 3: Append 4 first bytes of checksum to prefixed key
My private key is now
Final step: Perform base58 encode:
My private key in its WIF format is
Just add the following lines to the python script to compute this address:
You can run it too:
Checking with Bitcoin Core client.
It is easy to check this private & public key couple: Just import the private key to
Bitcoin Core using debug console or bitcoin-cli. In this sample, we need an up to date
Bitcoin Core client.
As you can see, my private key was correctly imported in
Bitcoin core, and I’m able to export its associated public address, and from this public address, my private key!
In a nutshell
There is a great tool allowing to manipulate bitcoin addresses. It’s bitcoin-tool on github, and I used it a lot during some of my investigation for debugging purpose. Unfortunately, it is not supporting all addresses types (Pay to script hash, bech32).
This tool allows creating for a private key everything we need, but it is less fun than creating our keys by ourselves!
What’s next ?
There is still a lot to say about Bitcoin’s addresses:
- compressed keys;
- P2SH addresses;
- bech32 addresses;
- deterministic keys.
I’ll write about these in a next post!