事件还原:
在使用 MnemonicUtils.generateMnemonic 方法生成助记词的代码写完了以后,本地测试很多次发现正常就吧代码推到库上去了。后面测试同事反馈某些私钥生成助记词的时候失败了。
排查了日志后发现失败的情况抛出了如下的异常
java.lang.IllegalArgumentException: The allowed size of ENT is 128-256 bits of multiples of 32
分析步骤:
拿到异常后,首先去搜一下。没有任何发现
吧关键代码提取出来,开始打断点调试。
在第80行打上断点,进去MnemonicUtils.generateMnemonic 方法发现,它会先对传入的byte数组进行校验。
进入这个validateEntropy校验看看
Good 找到了抛出异常的地方了。同时也发现我们传递进去的私钥字节数组的长度为33位?
此时问题变成了为什么这个私钥转换成的字节数组由33位而不是32位?
由第一个图我们可以得到私钥的16进制字符串:
9d38135d9eb270e29ed4fd055a23c1c55486af3bd946f984b1d45d5b5810a249
由debug得出字节数组为:[0, -99, 56, 19, 93, -98, -78, 112, -30, -98, -44, -3, 5, 90, 35, -63, -59, 84, -122, -81, 59, -39, 70, -7, -124, -79, -44, 93, 91, 88, 16, -94, 73]
由上图可知,-99的16进制就是9d ,所以字节数组是多了前面的这个0
解决方案:
在转换数组之后对其长度进行校验,如果是33位的字节数组,就判断首位是否位0,如果是,则将第一位舍弃。
测试OK
点击查看代码
import org.web3j.crypto.MnemonicUtils;
String intPrivateKey="71112194320368789998803347305328030081773433265531236127646905100644415218249";
BigInteger bigPriv=new BigInteger(intPrivateKey);
String hex16Priv=bigPriv.toString(16);
System.out.println("====| "+hex16Priv);
byte[] privArray=bigPriv.toByteArray();
if (privArray.length==33 && privArray[0]==(byte) 0){
byte[] tmpArray=new byte[privArray.length-1];
System.arraycopy(privArray,1,tmpArray,0,tmpArray.length);
privArray=tmpArray;
}
String actualMnemonic= MnemonicUtils.generateMnemonic(privArray);
System.out.println(actualMnemonic);
byte[] privBytes=MnemonicUtils.generateEntropy(actualMnemonic);
String sourceStr=new String(Hex.encode(privBytes));
System.out.println(sourceStr);
Assertions.assertEquals(hex16Priv,sourceStr);