对于比特币而言,公钥对应着btc地址,私钥相当于密码,拥有私钥意味着掌握了这个地址,所以了解私钥是非常重要的。
我希望通过代码实现比特币私钥、地址的生成来学习了解这方面的知识。本文的代码很多来自开源项目bitcoinj,实现的语言为java。
一、私钥与公钥的关系
通过椭圆曲线算法,我们可以得到一个密钥对,一个为私钥,一个为公钥。私钥和公钥都是256位32个字节的byte数组。
下面通过代码来实现:
ECKey key = new ECKey();
byte[] privKeyBytes = key.getPrivKeyBytes();
byte[] pubKeyBytes = key.getPubKey();
ECKey封装了椭圆曲线算法,初始化ECKey,即可获得一个密钥对。
二、公钥转换为btc地址
由于数组不便于使用,所以公钥需要进行格式化,格式化后的结果即为btc地址。
先说下原理:
下面通过代码实现:
/**
* ripemd160(sha256(in))
* 双hash,计算比特币地址时使用
*/
public static byte[] sha256hash160(byte[] input) {
try {
byte[] sha256 = MessageDigest.getInstance("SHA-256").digest(input);
RIPEMD160Digest digest = new RIPEMD160Digest();
digest.update(sha256, 0, sha256.length);
byte[] out = new byte[20];
digest.doFinal(out, 0);
return out;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e); // Cannot happen.
}
}
/**
* 从公钥计算出地址
* @param pubKeyBytes
* @return
*/
public static String getBtcAddress (byte[] pubKeyBytes) {
byte[] hash160 = sha256hash160(pubKeyBytes);
byte version = 0x00;
return Base58Check.encode(version, hash160);
}
三、base58及base58check编码
base58介绍:
base58在base64的基础上,去除了几个看起来会产生歧义的字符,如 0 (零), O (大写字母O), I (大写的字母i) and l (小写的字母L) ,和几个影响双击选择的字符,如/, +。结果字符集正好58个字符(包括9个数字,24个大写字母,25个小写字母)。
base58check介绍:
Base58 导出的字符串没有校验机制,这样,在传播过程中,如果漏写了几个字符,会检测不出来。所以使用了改进版的算法 Base58Check。
实现是:在encode前,在输入流尾部加入输入内容的hash值(4个字节)。然后再对输入流进行 Base58Encode。
参考代码:
info.block123.btc.kit.Base58
info.block123.btc.kit.Base58Check
四、私钥格式化
为了方便存储和使用,私钥必须要进行格式化输出。
btc私钥格式介绍:
种类版本描述
Hex 16进制byte数组16进制byte数组
WIF 5开头Base58Check编码
WIF-compressed K or L开头Base58Check编码前,在byte数组后加0x01字节
代码实现:
/**
* 未加工的密钥格式化
*/
@Override
public String format(byte[] keyBytes) {
if ( PRIV_KEY_HEX.equals(keyType)) {
return BtcKit.toHexString(keyBytes);
}
if ( PRIV_KEY_WIF.equals(keyType)) {
byte version = (byte)0x80;
return Base58Check.encode(version, keyBytes);
}
if ( PRIV_KEY_WIFC.equals(keyType)) {
byte[] cBytes = new byte[ keyBytes.length + 1];
System.arraycopy(keyBytes, 0, cBytes, 0, keyBytes.length);
cBytes[ cBytes.length - 1] = 0x01;
byte version = (byte)0x80;
return Base58Check.encode(version, cBytes);
}
return null;
}
@Override
public byte[] parse(String src) {
if ( PRIV_KEY_HEX.equals(keyType)) {
return BtcKit.hexStringToByte(src);
}
if ( PRIV_KEY_WIF.equals(keyType)) {
return Base58Check.decode(src);
}
if (PRIV_KEY_WIFC.equals(keyType)) {
byte[] rawBytes = Base58Check.decode(src);
byte[] result = new byte[rawBytes.length-1];
System.arraycopy(rawBytes, 0, result, 0, result.length);
return result;
}
return null;
}
五、私钥推算出比特币地址
比特币地址可以由私钥推算出来。下面为代码实现:
@Test
public void testPrivKeyToAddress() {
String formatPrivKey = "5JG9hT3beGTJuUAmCQEmNaxAuMacCTfXuw1R3FCXig23RQHMr4K";
KeyFormat format = new PrivKeyFormat(KeyFormat.PRIV_KEY_WIF);
byte[] privKeyBytes = format.parse(formatPrivKey);
byte[] pubKeyBytes = ECKey.publicKeyFromPrivate(BtcKit.byte32toBigInteger(privKeyBytes), false);
String address = BtcKit.getBtcAddress(pubKeyBytes);
System.out.println("计算后的地址:" + address);
Assert.assertEquals(address, "1thMirt546nngXqyPEz532S8fLwbozud8");
}
PS:如果觉得对你有帮助,欢迎打赏
btc地址:1BYgnJ1Xv561L3qFrFJasVzEGs1JC93QV1