隨機生成私鑰
私鑰是 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 到比特幣地址
- ripemd160(sha256(K)) 結果長 20 字節
- 對 1 添加版本前綴,例如比特幣地址前綴為 0x00
- 對 2 做兩次 sha256 並取前 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);
}
