比特币学习(一):私钥与地址

对于比特币而言,公钥对应着btc地址,私钥相当于密码,拥有私钥意味着掌握了这个地址,所以了解私钥是非常重要的。

我希望通过代码实现比特币私钥、地址的生成来学习了解这方面的知识。本文的代码很多来自开源项目bitcoinj,实现的语言为java。

一、私钥与公钥的关系

通过椭圆曲线算法,我们可以得到一个密钥对,一个为私钥,一个为公钥。私钥和公钥都是256位32个字节的byte数组。

下面通过代码来实现:

ECKey key = new ECKey();

byte[] privKeyBytes = key.getPrivKeyBytes();

byte[] pubKeyBytes = key.getPubKey();

ECKey封装了椭圆曲线算法,初始化ECKey,即可获得一个密钥对。

二、公钥转换为btc地址

由于数组不便于使用,所以公钥需要进行格式化,格式化后的结果即为btc地址。

先说下原理:

比特币学习(一):私钥与地址_第1张图片

下面通过代码实现:

/**

* 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

你可能感兴趣的:(比特币学习(一):私钥与地址)