How
to Generate Bitcoin Addresses in Java in six steps?
In this article, I will explain how to write a Java program to generate Pay
to Public Key Hash (P2PKH) Bitcoin address (compressed and uncompressed public
key). This is three article tutorials which will demonstrate how to generate
different Bitcoin address using Java programming.
Bitcoin addresses are 26-35 characters long, consist of alphabetic and
numeric characters and either begin with “1”, “3”, or “bc1”.
Currently, there are three Bitcoin address formats in use:
1. P2PKH (Pay to Public Key Hash) (address starts with the number “1”)
Example: 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2
2. P2SH (Pay to Script Hash) (address starts with the number “3”)
Example: 3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy
3. Bech32 (address starts with “bc1”)
Example: bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq
The bitcoin address is a random string in the BASE58 encoding format used to
send and receive bitcoins in the Bitcoin network. It is public-private
asymmetry key cryptography based on ECDSA. The public part of the key is used to generate bitcoin address. The
corresponding private key is used to sign the Bitcoin transaction as
confirmation and proof at the time of the transaction.
Technically, the Bitcoin address is from ECDSA. Generated by the public
part of the key, using SHA-256 and RIPEMD-160 Hash, as described below, to
process the resulting hash, and finally, use Base58. The key is encoded by the
check code.
Let’s see how to use JCE (java encryption extension), Bouncy Castle (RIPEMD-160), and finally
Base58 encoding in the “bitcoinj” library to generate Bitcoin address.
Step 1: Initialize the security service provider.
We’ve added the BouncyCastleProvider as a security provider as we are
going to use classes of it.
java.security.Security.addProvider(new BouncyCastleProvider());
Step 2: Generate ECDSA keypair
BigInteger privKey = Keys.createEcKeyPair().getPrivateKey();
OUTPUT: Private key: a76448f06981aeb02df458f657be1f2994729f8df0de672ecc0095421089f5bc
Generated private key is usually stored by the digital
wallets.
BigInteger pubKey = Sign.publicKeyFromPrivate(privKey);
ECKeyPair keyPair = new ECKeyPair(privKey, pubKey);
OUTPUT: Public key:
f51b58c89eebcdcdedfb6733bfe45fb884186e8277910ea7dea83fd44380a96de8671ddb36f0bf38d502d9ec7b1973ffe6d696431edc163d73f95cf9acd2180a
The public part of the key generated above will be encoded into a
bitcoin address. The ECDSA public key is represented by a point on an
elliptical curve. The X and Y coordinates of this point comprise the public
key.
Once we’re done with the ECDSA key generation, all we need to do is to
add the bytes 0x04 at the start of our public key. The result is a
Bitcoin full public key.
But we can do better as we may compress the key as shown below. This key
contains the same information, but it’s almost twice as short as the
uncompressed key.
String bcPub = compressPubKey(pubKey);
OUTPUT: Compressed Public key: 02f51b58c89eebcdcdedfb6733bfe45fb884186e8277910ea7dea83fd44380a96d
Step 3: Generate hashes based on the public key
What we need to do here is to apply SHA-256 to the public key and then
apply RIPEMD-160 to the result. The order is important.
Hashed public key = RIPEMD-160 (SHA-256 (public key))
MessageDigest sha = MessageDigest.getInstance("SHA-256");
byte[] s1 = sha.digest(hexStringToByteArray(bcPub));
MessageDigest rmd = MessageDigest.getInstance("RipeMD160");
byte[] r1 = rmd.digest(s1);
OUTPUT: SHA256: b21494d6b893fd73448a5fd7123938c3d5d4ff28d599bbf51eb89ad535e2fd1c
OUTPUT: RIPEMD160: 0582ab02f0abb93a049ba65e8738d4edec06075a
Step 4: Adding the address of type of the network to the output of hashing
Add version byte in front of
RIPEMD-160 hash (0x00 for Main Network). The version byte is used to
differentiate between MainNet and TestNet addresses as well as between P2PKH
and P2SH addresses. It is 0x00 for mainnet and 0x6f for
testnet.
byte[] r2 = new byte[r1.length + 1];
r2[0] = 0;
for (int i = 0; i < r1.length; i++) {
r2[i + 1] = r1[i];
}
OUTPUT: Added network byte:
000582ab02f0abb93a049ba65e8738d4edec06075a
Step 5: Generating checksum to verify the integrity of the key
Now we need to calculate the checksum of our key. The
idea of checksum is to make sure that the data (in our case, the key) wasn’t
corrupted during transmission. The wallet software should look at the checksum
and mark the address as invalid if the checksum mismatches.
To calculate the checksum of the key, we need to apply
SHA-256 twice and then take the first 4 bytes of the result.
Checksum = SHA-256( SHA-256(output from previous step))
byte[] s2 = sha.digest(r2);
OUTPUT: SHA256: db597d4fe6e6ad49c713be33f5e1907ce5839095e311f103b937acb2ac0cec01
byte[] s3 = sha.digest(s2);
OUTPUT: SHA256:
62a9971acb5e911c3472f924bfbe836173cef62d35c04b92118d69e6b3bf2602
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 and generate 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 < 4; i++) {
a1[21 + i] = s3[i];
}
OUTPUT: Before Base58 encoding: 000582ab02f0abb93a049ba65e8738d4edec06075a62a9971a
Step 6: Applying Base58 encoding
We are using Base58.encode() Method of
bitcoinj Library to get the final bitcoin address.
Base58.encode(a1)
OUTPUT: After Base58 encoding: 1W8uw3vTUD5CwaprwCWGPoMjk482xWm1K
We require the following utility functions for the execution of the above code:
The following function is used
to convert the byte to hex representation for the sake of user’s understanding.
private static String bytesToHex(byte[] hashInBytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hashInBytes.length; i++) {
sb.append(Integer.toString((hashInBytes[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
As you may be knowing, the
public key is some point (X, Y) on the elliptic curve. We know the curve, and
for each X there are only two Ys that define the point which lies on that
curve. So why keep Y? Instead, let’s keep X and the sign of Y. Later, we can
derive Y from that if needed. The specifics are as follows: we take X from the
ECDSA public key. Now, we add the 0x02 if the last byte of Y is even,
and the byte 0x03 if the last byte is odd. The following method is used
to generate a compressed public key based on the given public key in BigInteger.
public static String compressPubKey(BigInteger pubKey) {
String pubKeyYPrefix = pubKey.testBit(0) ? "03" : "02";
String pubKeyHex = pubKey.toString(16);
String pubKeyX = pubKeyHex.substring(0, 64);
return pubKeyYPrefix + pubKeyX;
}
Hexadecimal representation
in a string and its byte array representation is different in java (“0x78”! =
“78”). The hashing algorithm which we have using in this program accept the
byte array. Hence, to convert hexadecimal to byte array use following method:
public static byte[] hexStringToByteArray(String s) {
byte[] b = new byte[s.length() / 2];
for (int i = 0; i < b.length; i++) {
int index = i * 2;
int v = Integer.parseInt(s.substring(index, index + 2), 16);
b[i] = (byte) v;
}
return b;
}
Use the following tools to validate the result of intermediate steps:
4.
Some key facts about valid Bitcoin
addresses:
·
A Bitcoin address is between 25 and
34 characters long;
·
The address always starts with a 1;
·
An address can contain all
alphanumeric characters, with the exceptions of 0, O, I, and l.
https://blog.hubspot.com/marketing/bitcoin-address
Use the following link on medium:
https://medium.com/@parthshah.ce/generate-bitcoin-addresses-using-java-in-six-steps-b1c418796a9e