参考:助记词(Mnemonics)生成种子,以及Public Key, Private key https://my.oschina.net/gavinzheng731/blog/1847838
【区块链 | ETH】Web3j创建钱包的2种方式的对比https://blog.csdn.net/qq_28505809/article/details/99857208
基本大意,根据BIP39,11位二进制数转化成一个单词,如果有不足的位数,使用私钥hash的结果来填充,于是助记词和私钥之间就可以相互转换了。
贴上代码
/**
* pk is 256 bits, the mn words will be 24
*/
@Override
public List<String> getWords(byte[] pk) throws Exception {
ArrayList<String> ret = new ArrayList<String>();
byte[] out = CreateCredentialsUtil.pk2mn("SHA-256", "", pk);
int n;
n = (out[0] & 0xff) << 3 | (out[1] & 0xff) >> 5;
ret.add(words.get(n));
n = (out[1] & 0x1f) << 6 | (out[2] & 0xff) >> 2;
ret.add(words.get(n));
n = (out[2] & 0x03) << 9 | (out[3] & 0xff) << 1 | (out[4] & 0xff) >> 7;
ret.add(words.get(n));
n = (out[4] & 0x7f) << 4 | (out[5] & 0xff) >> 4;
ret.add(words.get(n));
n = (out[5] & 0x0f) << 7 | (out[6] & 0xff) >> 1;
ret.add(words.get(n));
n = (out[6] & 0x01) << 10 | (out[7] & 0xff) << 2 | (out[8] & 0xff) >> 6;
ret.add(words.get(n));
n = (out[8] & 0x3f) << 5 | (out[9] & 0xff) >> 3;
ret.add(words.get(n));
n = (out[9] & 0x07) << 8 | (out[10] & 0xff) ;
ret.add(words.get(n));
// round 2
n = (out[11] & 0xff) << 3 | (out[12] & 0xff) >> 5;
ret.add(words.get(n));
n = (out[12] & 0x1f) << 6 | (out[13] & 0xff) >> 2;
ret.add(words.get(n));
n = (out[13] & 0x03) << 9 | (out[14] & 0xff) << 1 | (out[15] & 0xff) >> 7;
ret.add(words.get(n));
n = (out[15] & 0x7f) << 4 | (out[16] & 0xff) >> 4;
ret.add(words.get(n));
n = (out[16] & 0x0f) << 7 | (out[17] & 0xff) >> 1;
ret.add(words.get(n));
n = (out[17] & 0x01) << 10 | (out[18] & 0xff) << 2 | (out[19] & 0xff) >> 6;
ret.add(words.get(n));
n = (out[19] & 0x3f) << 5 | (out[20] & 0xff) >> 3;
ret.add(words.get(n));
n = (out[20] & 0x07) << 8 | (out[21] & 0xff) ;
ret.add(words.get(n));
// round 3
n = (out[22] & 0xff) << 3 | (out[23] & 0xff) >> 5;
ret.add(words.get(n));
n = (out[23] & 0x1f) << 6 | (out[24] & 0xff) >> 2;
ret.add(words.get(n));
n = (out[24] & 0x03) << 9 | (out[25] & 0xff) << 1 | (out[26] & 0xff) >> 7;
ret.add(words.get(n));
n = (out[26] & 0x7f) << 4 | (out[27] & 0xff) >> 4;
ret.add(words.get(n));
n = (out[27] & 0x0f) << 7 | (out[28] & 0xff) >> 1;
ret.add(words.get(n));
n = (out[28] & 0x01) << 10 | (out[29] & 0xff) << 2 | (out[30] & 0xff) >> 6;
ret.add(words.get(n));
n = (out[30] & 0x3f) << 5 | (out[31] & 0xff) >> 3;
ret.add(words.get(n));
n = (out[31] & 0x07) << 8 | (out[32] & 0xff) ;
ret.add(words.get(n));
return ret;
}
@Override
public byte[] getPk(List<String> input) {
byte[] ret = new byte[32];
int[] in = new int[24];
for (int i = 0; i < 24; ++i) {
in[i] = words.indexOf(input.get(i));
}
// round 1
ret[0] = (byte) ((in[0] & 0x7ff) >> 3);
ret[1] = (byte) ((in[0] & 0x07) << 5 | (in[1] & 0x7ff) >> 6);
ret[2] = (byte) ((in[1] & 0x3f) << 2 | (in[2] & 0x7ff) >> 9);
ret[3] = (byte) ((in[2] & 0x1ff) >> 1);
ret[4] = (byte) ((in[2] & 0x1) << 7 | (in[3] & 0x7ff) >> 4);
ret[5] = (byte) ((in[3] & 0xf) << 4 | (in[4] & 0x7ff) >> 7);
ret[6] = (byte) ((in[4] & 0x7f) << 1 | (in[5] & 0x7ff) >> 10);
ret[7] = (byte) ((in[5] & 0x3ff) >> 2);
ret[8] = (byte) ((in[5] & 0x3) << 6 | (in[6] & 0x7ff) >> 5);
ret[9] = (byte) ((in[6] & 0x1f) << 3 | (in[7] & 0x7ff) >> 8);
ret[10] = (byte) ((in[7] & 0xff));
// round 2
ret[11] = (byte) ((in[8] & 0x7ff) >> 3);
ret[12] = (byte) ((in[8] & 0x07) << 5 | (in[9] & 0x7ff) >> 6);
ret[13] = (byte) ((in[9] & 0x3f) << 2 | (in[10] & 0x7ff) >> 9);
ret[14] = (byte) ((in[10] & 0x1ff) >> 1);
ret[15] = (byte) ((in[10] & 0x1) << 7 | (in[11] & 0x7ff) >> 4);
ret[16] = (byte) ((in[11] & 0xf) << 4 | (in[12] & 0x7ff) >> 7);
ret[17] = (byte) ((in[12] & 0x7f) << 1 | (in[13] & 0x7ff) >> 10);
ret[18] = (byte) ((in[13] & 0x3ff) >> 2);
ret[19] = (byte) ((in[13] & 0x3) << 6 | (in[14] & 0x7ff) >> 5);
ret[20] = (byte) ((in[14] & 0x1f) << 3 | (in[15] & 0x7ff) >> 8);
ret[21] = (byte) ((in[15] & 0xff));
// round 3
ret[22] = (byte) ((in[16] & 0x7ff) >> 3);
ret[23] = (byte) ((in[16] & 0x07) << 5 | (in[17] & 0x7ff) >> 6);
ret[24] = (byte) ((in[17] & 0x3f) << 2 | (in[18] & 0x7ff) >> 9);
ret[25] = (byte) ((in[18] & 0x1ff) >> 1);
ret[26] = (byte) ((in[18] & 0x1) << 7 | (in[19] & 0x7ff) >> 4);
ret[27] = (byte) ((in[19] & 0xf) << 4 | (in[20] & 0x7ff) >> 7);
ret[28] = (byte) ((in[20] & 0x7f) << 1 | (in[21] & 0x7ff) >> 10);
ret[29] = (byte) ((in[21] & 0x3ff) >> 2);
ret[30] = (byte) ((in[21] & 0x3) << 6 | (in[22] & 0x7ff) >> 5);
ret[31] = (byte) ((in[22] & 0x1f) << 3 | (in[23] & 0x7ff) >> 8);
// ret[32] = (byte) ((in[23] & 0xff));
return ret;
}
单词文件,请到https://github.com/bitcoin/bips/tree/master/bip-0039自行寻找。
最后写点小收获,java的BigInteger, 转成byte[]时,如果第一个byte是负数,那么会在其前加上一个byte,值是0。这样,256位的私钥,本来应该变成32个byte,却变成了33个byte,这样处理,是因为如果用byte数组生成大整数,如果最高位为1,那么生成的整数是负的。但如果这些byte变成字串,则没关系。比如BigInteger(“FFF”,16)并不会变成一个负数。
以上方法,助记词和私钥可以相互转化,另有一种从助记词生成私钥的方法,(但反向不行)
参考 java使用bip39创建以太坊钱包https://www.jianshu.com/p/14b9d2908a66
区块链知识普及之 BIP39 助记词生成过程详解https://www.jianshu.com/p/3db04e987a6c