Private and Public Keys
A PrivateKey is a secp256k1 secret with two pieces of metadata attached to it: the network it is meant for,
and whether its Public Key is compressed. It turns out that the metadata only matters when we write the key
out as WIF. Signing, and the Public Key itself, are the same on every network.
Creating a Private Key
Section titled “Creating a Private Key”We create a random Private Key as follows:
// A new random Private Key. It is compressed, and tagged for mainnet.PrivateKey privateKey = new PrivateKey();
// For a testnet key we wrap a new ECKey and say which network it belongs to.PrivateKey testnetKey = new PrivateKey(new ECKey(), true, NetworkType.TEST);In the preceding example:
- the no-argument constructor draws from
SecureRandom, and tags the key as mainnet, - the three-argument constructor takes an
ECKey(the BitcoinJ key class the library builds on), a compression flag and aNetworkType, which is one ofMAIN,TEST,REGTESTorSCALINGTEST.
Importing and exporting WIF
Section titled “Importing and exporting WIF”Wallet Import Format is the usual way to store a Private Key as text, or move it between wallets. A WIF string
encodes the network (mainnet keys start with K, L or 5, testnet keys with c or 9) as well as the
compression flag.
PrivateKey privateKey = PrivateKey.fromWIF("cVVvUsNHhbrgd7aW3gnuGo2qJM45LhHhTCVXrDSJDDcNGE6qmyCs");
// toWIF() encodes for the network the key was read from (testnet, here).String wif = privateKey.toWIF();
// toWif(NetworkType) encodes the same secret for another network.String mainnetWif = privateKey.toWif(NetworkType.MAIN);fromWIF() reads the network from the string, and toWIF() writes it back the same way. To move the same
secret to another network, we pass the network to toWif(NetworkType).
Please note the case.
toWIF()andtoWif(...)are two different methods.
A string that is not valid WIF (wrong length, wrong prefix or a bad checksum) throws InvalidKeyException,
which is a checked exception.
assertThrows(InvalidKeyException.class, () -> PrivateKey.fromWIF("not-a-wif-key"));Working with hex
Section titled “Working with hex”There is no hex factory on PrivateKey. Instead we go through ECKey, which takes the secret either as a
BigInteger or as 32 bytes.
// We start from a 32-byte secret in hex...BigInteger secret = new BigInteger("ec3da55fea673d63e760a604ff50ae65e2edaa23e5ecddc5ca8ea437fa71baa6", 16);PrivateKey privateKey = new PrivateKey(ECKey.fromPrivate(secret), true, NetworkType.TEST);
// ...and get back to hex through the underlying ECKey.String hex = privateKey.getKey().getPrivateKeyAsHex();Getting the Public Key
Section titled “Getting the Public Key”PrivateKey privateKey = PrivateKey.fromWIF("cVVvUsNHhbrgd7aW3gnuGo2qJM45LhHhTCVXrDSJDDcNGE6qmyCs");PublicKey publicKey = privateKey.getPublicKey();
String hex = publicKey.getPubKeyHex(); // 33-byte compressed encoding, in hexbyte[] bytes = publicKey.getPubKeyBytes(); // the same, as bytesbyte[] hash160 = publicKey.getPubKeyHash(); // RIPEMD160(SHA256(pubkey)), used by P2PKH
// Reading a Public Key that someone gave us.PublicKey theirs = PublicKey.fromHex(hex);getPubKeyHash() gives us the 20-byte hash that a P2PKH Address and Locking Script contain.
PublicKey.fromHex() and PublicKey.fromBytes() accept both the 33-byte compressed and the 65-byte
uncompressed encodings.
A key read from an uncompressed WIF (an old one, starting with 5 on mainnet) keeps that form. Its Public Key is
the 65-byte encoding, so the Address we derive is the one that holds the old wallet’s coins, and toWIF()
writes it back unchanged. isCompressed() tells us which form a key uses.
// An uncompressed mainnet WIF (these start with 5).PrivateKey privateKey = PrivateKey.fromWIF("5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ");
// The key remembers the form the WIF asked for, and its Public Key, Address and WIF all keep it.assertFalse(privateKey.isCompressed());assertEquals(65, privateKey.getPublicKey().getPubKeyBytes().length);assertEquals("1GAehh7TsJAHuUAeKZcXf5CnwuGuGgyX2S", Address.fromKey(NetworkAddressType.MAIN_PKH, privateKey.getPublicKey()).toString());assertEquals("5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ", privateKey.toWIF());Signing raw data
Section titled “Signing raw data”PrivateKey.sign(byte[]) signs a 32-byte hash and returns a DER-encoded ECDSA Signature. One rarely needs
it directly, since TransactionSigner signs Transactions and
signMessage() signs text.
Do note that Signatures are deterministic (RFC 6979). The same key and hash always give the same Signature.

