“You can’t be happy unless you’re unhappy sometimes.” ― Lauren Oliver, Delirium
Contents
1. Introduction
Let us learn how Bitcoin works by implementing various aspects of the technology, shall we? The technology includes the following aspects.
- A bitcoin address is used to send and receive bitcoins.
- A transaction is a transfer of bitcoins from one address to another.
- Several transactions are grouped into a block. A block is processed so it can be accepted into the bitcoin network. This process is known as mining.
- The blocks are collected in a blockchain, and shared by nodes in the network.
A Note of Warning – The code presented here is for the purpose of learning only. You may lose money if you attempt to send bitcoins to the addresses generated by this code. Or you could just send it to me instead 🙂
2. What is a Bitcoin Address?
A Bitcoin address is a random-looking string of hexadecimal characters that are used within the Bitcoin network to send and receive bitcoins. It is the public part of a public-private asymmetric ECDSA key. The corresponding private key is used to sign the bitcoin transaction as proof that the transaction originated from you.
Technically, a bitcoin address is generated from the public part of an ECDSA key, hashed using SHA-256 and RIPEMD-160, processing the resulting hashes as described below, and finally encoding the key using a Base58 Checked encoding.
Let us find out how to do all this using JCE (Java Cryptography Extension), Bouncy Castle (for RIPEMD-160), and finally using the Base58 encoding capability in the bitcoinj library.
3. Generate an ECDSA Key Pair
We have previously covered generating RSA public and private keys. Instead of RSA, bitcoin uses ECDSA for the key algorithm. It is generated as follows.
Create a KeyPairGenerator for the Elliptic Curve algorithm.
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
The specific elliptic curve used is the secp256k1.
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256k1"); keyGen.initialize(ecSpec);
Once you have the KeyPairGenerator, you can create the KeyPair, from which you can obtain the public and private keys.
KeyPair kp = keyGen.generateKeyPair(); PublicKey pub = kp.getPublic(); PrivateKey pvt = kp.getPrivate();
3. The ECDSA Private Key
You can store just the private part of the key since the public key can be derived from the private key.
ECPrivateKey epvt = (ECPrivateKey)pvt; String sepvt = adjustTo64(epvt.getS().toString(16)).toUpperCase(); System.out.println("s[" + sepvt.length() + "]: " + sepvt);
The static method adjustTo64() merely pads the hex string with leading 0s so the total length is 64 characters.
static private String adjustTo64(String s) { switch(s.length()) { case 62: return "00" + s; case 63: return "0" + s; case 64: return s; default: throw new IllegalArgumentException("not a valid key: " + s); } }
Here is a sample private key generated by the above code.
s[64]: 024C8E05018319CED4BB04E184C307BFF115976A05F974C7D945B5151E490ADE
This value is usually what is stored by the digital wallets.
4. The ECDSA Public Key
The public part of the key generated above is encoded into a bitcoin address. First, the ECDSA key is represented by a point on an elliptical curve. The X and Y coordinates of this point comprise the public key. They are concatenated together with “04” at the beginning to represent the public key.
ECPublicKey epub = (ECPublicKey)pub; ECPoint pt = epub.getW(); String sx = adjustTo64(pt.getAffineX().toString(16)).toUpperCase(); String sy = adjustTo64(pt.getAffineY().toString(16)).toUpperCase(); String bcPub = "04" + sx + sy; System.out.println("bcPub: " + bcPub); # prints bcPub: 04CAAA5C0BDDAA22C9D3C0DDAEC8550791891BB2C2FB0F9084D02F927537DE4F443ACED7DEB488E9BFE60D6C68596E6C78D95E20622CC05474FD962392BDC6AF29
5. Perform a SHA-256 and a RIPEMD-160 Hash
We now need to perform a SHA-256 digest on the public key, followed by a RIPEMD-160 digest.
MessageDigest sha = MessageDigest.getInstance("SHA-256"); byte[] s1 = sha.digest(bcPub.getBytes("UTF-8")); System.out.println(" sha: " + bytesToHex(s1).toUpperCase()); # prints sha: 7524DC35AEB4B62A0F1C90425ADC6732A7C5DF51A72E8B90983629A7AEC656A0
We use the Bouncy Castle provider for performing the RIPEMD-160 digest since JCE does not implement this algorithm.
MessageDigest rmd = MessageDigest.getInstance("RipeMD160", "BC"); byte[] r1 = rmd.digest(s1);
Next we need to add a version byte of 0x00 at the beginning of the hash.
byte[] r2 = new byte[r1.length + 1]; r2[0] = 0; for (int i = 0 ; i < r1.length ; i++) r2[i+1] = r1[i]; System.out.println(" rmd: " + bytesToHex(r2).toUpperCase()); # prints rmd: 00C5FAE41AB21FA56CFBAFA3AE7FB5784441D11CEC
6. Repeat the SHA-256 Hashing Twice
We now need to perform a SHA-256 hash twice on the result above.
byte[] s2 = sha.digest(r2); System.out.println(" sha: " + bytesToHex(s2).toUpperCase()); byte[] s3 = sha.digest(s2); System.out.println(" sha: " + bytesToHex(s3).toUpperCase());
The first 4 bytes of the result of the second hashing is used as the address checksum. It is appended to the RIPEMD160 hash above. This is the 25-byte bitcoin address.
byte[] a1 = new byte[25]; for (int i = 0 ; i < r2.length ; i++) a1[i] = r2[i]; for (int i = 0 ; i < 5 ; i++) a1[20 + i] = s3[i];
7. Encode the Address Using Base58
We now use the Base58.encode() method from the bitcoinj library to arrive at the final bitcoin address.
System.out.println(" adr: " + Base58.encode(a1)); # prints adr: 1K3pg1JFPtW7NvKNA77YCVghZRq2s1LwVF
This is the address to which the bitcoin should be sent to in a transaction.
Review
And that was a presentation of how to generate a bitcoin address in java. We generate an ECDSA key pair, hash the public part of the key using SHA256 and RIPEMD160. Finally we compute a checksum by performing the SHA256 twice and picking the first 4 bytes, which is appended to the RIPEMD160 hash above. The result is encoded using Base58 encoding.
Now hang in there while we show how to compose transactions in the next article.
bitcoin address validator says invalid results
The address checksum in point 6. is wrong, as you are modifying 5 bytes, not 4 as you say! It must be:
byte[] a1 = new byte[25];
for (int i = 0 ; i < r2.length ; i++) a1[i] = r2[i];
for (int i = 0 ; i < 4 ; i++) a1[21 + i] = s3[i];
This way bitcoin validator validates 😉
The line “byte[] s1 = sha.digest(bcPub.getBytes(“UTF-8″));” is an error.
Change with this line “byte[] s1 = sha.digest(hexStringToByteArray(bcPub));”
You can find method hexStringToByteArray here : https://stackoverflow.com/questions/140131/convert-a-string-representation-of-a-hex-dump-to-a-byte-array-using-java
You can check steps here : http://gobittest.appspot.com/Address