比特币私钥、公钥、地址

随机生成私钥

私钥是 256 位的二进制数,以 64 位 hex 显示,例如
bef05ca99c4bb9d17f9f164a5bffd48ee2f99f866a3621dd9a4be62412c28148

public static byte[] privateKey() {
	byte[] privateKey = new byte[32];
	random.nextBytes(privateKey);
	return privateKey;
}

从私钥到公钥

{K = k * G} secp256k1 标准的椭圆曲线,以私钥 k 为起点,将曲线上已定义的生成点 G 相乘获得另一点,也就是公钥 K = (x, y)。

K = bef05ca99c4bb9d17f9f164a5bffd48ee2f99f866a3621dd9a4be62412c28148 * G
x = c2a0eef93156029532c9b6d33dfd4d09abc3fa0454bc1580230682c9d197f974
y = 0ccafcd456bbac903010082d251b83f8d10013d49e75b1c681c2189c92955f35

公钥有两种格式,压缩与未压缩。坐标 x 对应两个 y 值,分别为奇数和偶数,因此可以将 y 压缩为一个字节:02 表示偶数,03 表示奇数。
未压缩的公钥:04 + x + y
已压缩的公钥:02 + x 或 03 + x
(bitcoinj 里面地址是由压缩格式公钥计算出来的。)

<dependency>
	<groupId>com.madgag.spongycastle</groupId>
	<artifactId>core</artifactId>
	<version>1.58.0.0</version>
</dependency>
private static final ECDomainParameters CURVE;

private static final X9ECParameters CURVE_PARAMS = CustomNamedCurves.getByName("secp256k1");

static {
	FixedPointUtil.precompute(CURVE_PARAMS.getG(), 12);
	CURVE = new ECDomainParameters(CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN(),
			CURVE_PARAMS.getH());
}

private static byte[] fromPrivate(byte[] privKeyBytes) {
	BigInteger privKey = new BigInteger(1, privKeyBytes);
	if (privKey.bitLength() > CURVE.getN().bitLength()) {
		privKey = privKey.mod(CURVE.getN());
	}
	ECPoint multiply = new FixedPointCombMultiplier().multiply(CURVE.getG(), privKey);
	byte[] uncompressed = multiply.getEncoded(false);
	byte[] compressed = multiply.getEncoded(true);
	return compressed;
}

从公钥 K 到比特币地址

  1. ripemd160(sha256(K)) 结果长 20 字节
  2. 对 1 添加版本前缀,例如比特币地址前缀为 0x00
  3. 对 2 做两次 sha256 并取前 4 字节
  4. 对 2 + 3 做 base58 编码

base58check: 2,3,4

base58 即 [0-9a-zA-Z] - [0OIl] 数字 0、大写字母 O I、小写字母 l

byte[] hashedPubKey = Utils.ripeMD160(Utils.sha256(pubBytes));
base58Check(hashedPubKey);

private static String base58Check(byte[] input) throws Exception {
	byte[] data = new byte[1 + input.length];
	data[0] = 0;
	System.arraycopy(input, 0, data, 1, input.length);

	byte[] checksum = Utils.sha256(data);
	checksum = Utils.sha256(checksum);

	byte[] address = new byte[data.length + 4];
	System.arraycopy(data, 0, address, 0, data.length);
	System.arraycopy(checksum, 0, address, data.length, 4);

	return Base58.encode(address);
}

参考

  1. bitcoinj
  2. Mastering Bitcoin

posted on 2018-06-05 18:04  ppcoin  阅读(5759)  评论(0编辑  收藏  举报