BigInteger是Java程序员在密码学中进行大数运算基础工具,使用过程中经常出现的错误是正负数问题和16进制转换问题。具体来说就是:
一)大数最高位为1的情况下,字节数组与BigInteger的转换问题
以以下例子说明,比如:
byte[] data new byte[1];
data[0] = (byte)0xC7
使用new BigInteger(byte[] data)方法把byte数组转换为BigInteger,并不处理符号,最高位是1时做负数处理:
BigInteger bi = new BigInteger(data);
//以下输出为-39
System.out.println(bi.toString(16)); /
//以下输出为xw==。也就是0xC7的base64编码
System.out.println(Base64.getEncoder().encodeToString(bi.toByteArray()));
为了解决以上问题,需要使用new BigInteger(int sign, byte[] data)进行转换,针对最高位为1的情况,会自动进行补0处理。
BigInteger bi = new BigInteger(1,data);
//以下输出为C7,也就是说转换成16进制串时,会自动去除表示正数的00
System.out.println(bi.toString(16));
//以下输出为AMc=。也就是0x00C7的base64编码
System.out.println(Base64.getEncoder().encodeToString(bi.toByteArray()));
所以,从字节数组转换为BigInteger时,使用BigInteger第二个构造函数,并且sign=1。
二)BigInteger与16进制数转换
使用BigInteger(String data, 16),可以把16进制数转换为大数,并且自动处理最高位为1的情况。比如:
String data = "C7";
//以下输出为C7,自动在最高位补0,处理正负数。
System.out.println(new BigInteger(data, 16).toString(16));
//以下代码可以验证是否做了补0处理。输出仍为AMc=
System.out.println(Base64.getEncoder().encodeToString(new BigInteger(data, 16).toByteArray()));
BigInteger.toString(16),可以把大数转换为16进制串。使用该转换存在一个问题,就是并不做偶数位对齐,比如:
String data = "07";
//以下输出为7
System.out.println(new BigInteger(data, 16).toString(16));
一般国密算法中的二进制串都是偶数字节的,所以如果出现奇数字节的二进制数,使用BigInteger就会出现很多错误。为了保证输出的是偶数字节,就需要使用toByteArray方法,比如:
//以下输出为Bw==,也就是07的base64编码
System.out.println(Base64.getEncoder().encodeToString(new BigInteger(data, 16).toByteArray()));
综合以上常见问题,在使用BigInteger进行大数运算时注意以下两点:
1)从字节数组转换为大数时,要使用双参的构造函数,并且设置符号参数sign为1
2)从大数转换成16进制数时,先转换为字节数组,然后再从中剔除高位的补0。