I understand the high level concept I am more interested specific details. How is a private key generated. How exactly is public key generated, how is address generated from public key.
The public and private keys in a Bitcoin address are a normal ECDSA key pair. I haven't poked through this particular bit of Bitcoin's own code but the offshoot products I've had a chance to work with typically use the Bouncy Castle crypto library. Bouncy Castle also has an excellent introduction/tutorial on how to use their library. Their examples are in Java but it's fairly simple and should be easily ported to other languages.
As is normal when doing Elliptic Curve encryption, a private key is simply a random number. In the case of secp256k1, the elliptic curve used by Bitcoin, it has to be a number between 1 and 115792089237316195423570985008687907852837564279074904382605163141518161494336 (or in hexadecimal, between
0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364140).
This private key is converted to a public key by performing an EC point multiplication with the curve's base point. The result is an
(x,y) coordinate pair, which constitutes the public key.
RIPEMD160(SHA256(pubkey)), where pubkey is a serialization of those coordinates, is computed, and encoded in base58, together with a checksum. This becomes the address.
Bitcoin private keys are most commonly displayed in wallet import format (WIF), also known as base58check (a number expressed in base 58 with a checksum at the end and a version byte at the beginning).
To create a WIF private key, you need to:
- Generate an ECDSA secret exponent (the private key) using the SECP256k1 curve.
- Convert the secret exponent/private key into base58check format.
Here are two simple ways to generate a secret exponent:
- Select a random number in the range [1, curve_order).
- Generate a random 64-character hexidecimal string (the hex string representation of a 256-bit number) and check to make sure that the number is in the range [1, curve_order). Repeat if necessary.
For SECP256k1, the curve order is 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140 (or 115792089237316195423570985008687907852837564279074904382605163141518161494336L).
This number is extremely close to the largest 256-bit number (0xFFF...FFF in hex), so you can expect that you'll never exceed the curve order if you generate a random 256-bit number.
As Pieter Wuille mentioned, public keys are derived by performing point multiplication with the curve's base point and the secret exponent/private key. The resulting (x,y) coordinate is the public key.
The Bitcoin address, just like the private key, is also displayed in base58check format. To get the address, we do the following:
- Calculate the hash160: ripemd160(sha256(public_key)).
- Convert the hash160 to base58check format.
To convert a number to base58check format, just perform the following steps:
- convert the value to a byte array and append the version byte to the beginning
- calculate the first 4 bytes of SHA256(SHA256(result of step 1)) and call it the checksum
- append the checksum to the end of the result of step 1
- move the result of step 3 to the keyspace "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
- check how many leading zeros the original binary value had and append those to the beginning of the result of step 4
Here are the more detailed instructions.
Now if you don't want to worry about any of this stuff, I would check out the python library Coinkit (https://github.com/halfmoonlabs/coinkit or "pip install coinkit").
You can perform simple operations like these:
>>> from coinkit.keypair import BitcoinKeypair >>> hex_private_key = '91149ee24f1ee9a6f42c3dd64c2287781c8c57a6e8e929c80976e586d5322a3d' >>> k = BitcoinKeypair(hex_private_key) >>> k.private_key() '91149ee24f1ee9a6f42c3dd64c2287781c8c57a6e8e929c80976e586d5322a3d' >>> k.public_key() '042c6b7e6da7633c8f226891cc7fa8e5ec84f8eacc792a46786efc869a408d29539a5e6f8de3f71c0014e8ea71691c7b41f45c083a074fef7ab5c321753ba2b3fe' >>> k.wif_pk() '5JvBUBPzU42Y7BHD7thTnySXQXMk8XEJGGQGcyBw7CCkw8RAH7m' >>> k.address() '13mtgVARiB1HiRyCHnKTi6rEwyje5TYKBW'
You can also create random keypairs:
>>> k = BitcoinKeypair()
And brain wallet keypairs:
>>> passphrase = 'shepherd mais pack rate enamel horace diva filesize maximum really roar mall' >>> k = BitcoinKeypair().from_passphrase(passphrase) >>> k.passphrase() 'shepherd mais pack rate enamel horace diva filesize maximum really roar mall'
Disclosure: I'm one of the creators of Coinkit.
How they are created is fully documented on wiki: https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses
- Include the jar of bitcoinj Java library in your classpath
import com.google.bitcoin.core.*; NetworkParameters params = new MainNetParams(); String publicAddress = new DumpedPrivateKey(params, "KzzuoFPzrPhD55icpAi7idW7z7tH8xSYo3xqTcZm3fHk3AzVxpoP").getKey() .toAddress(params).toString();
The result is
var key = new Bitcoin.ECKey('KzzuoFPzrPhD55icpAi7idW7z7tH8xSYo3xqTcZm3fHk3AzVxpoP'); console.log(key.getBitcoinAddress());
The result is
Note 1: Before, I did
bitcoin-cli getnewaddress and
bitcoin-cli dumpprivkey <pubkey> to get the key pair.
Note 2: To make things even more easy, under your directory https://github.com/pointbiz/bitaddress.org/tree/master/src just run:
cat array.map.js cryptojs.js cryptojs.sha256.js cryptojs.pbkdf2.js cryptojs.hmac.js cryptojs.aes.js cryptojs.blockmodes.js cryptojs.ripemd160.js securerandom.js ellipticcurve.js secrets.js biginteger.js qrcode.js bitcoinjs-lib.js bitcoinjs-lib.base58.js bitcoinjs-lib.address.js bitcoinjs-lib.ecdsa.js bitcoinjs-lib.eckey.js bitcoinjs-lib.util.js crypto-scrypt.js > all.js
and you get a 174kb file that is all you need to run.
Note 3: If you want to get this file (from commit f6c6bbe53df28c1bf51a6216f02ad1467b36c9f7) look here: http://pastebin.com/raw.php?i=rH8V2j9H
The full functional example