将字节数组转换为 BigInteger 不正确
Convert byte array to BigInteger incorrect
自从我尝试模拟我的客户端和我的服务器之间的连接处理数据包,我一直在与字节数组和 BigInteger 斗争。
当我的客户端连接到服务器时,服务器发送了一个响应,其中包含一个 RSA public 密钥,然后是一个十六进制数据包,就像这样:
C70001A100408031313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131E5AA194C3DBA71F9A38AF85D43C130A462BDCBACF85ED702B5E2FECB47197DAA433E7B5ACF5EF09A07C5DDE79B7A2EED76B91090CB981E13DBE316997FCF5DC51BE40B2D02D912457D97924B94094227F8BC65DD61FE78060B55BEAAA31C64E3B36863B407F7183DABA9D98F0C9061DED36AD16407F70C4925951BAA8807EC95
C70001A1004080
后的64字节是RSApublic指数。
RSA 指数后的 128 字节是模数。
然后我尝试提取指数和模数以重新生成 RSA public 密钥。我的步骤:
if (socket == null) socket = new Socket(loginHost, loginPort);
if (is == null) is = socket.getInputStream();
if (os == null) os = socket.getOutputStream();
int packageLength = 199;
byte[] response = IOUtils.toByteArray(is, packageLength);
byte[] rsaPubExponentBytes = Arrays.copyOfRange(response, 7, 71);
byte[] rsaModuloBytes = Arrays.copyOfRange(response, 71, packageLength);
BigInteger rsaPubExponentNumber = new BigInteger(1, rsaPubExponentBytes);
BigInteger rsaModuloNumber = new BigInteger(1, rsaModuloBytes);
PublicKey pub = null;
try {
RSAPublicKeySpec spec = new RSAPublicKeySpec(rsaModuloNumber, rsaPubExponentNumber);
KeyFactory factory = KeyFactory.getInstance("RSA");
pub = factory.generatePublic(spec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
}
我终于有了一把public钥匙:
Sun RSA public key, 1024 bits
modulus: 161275860318613570660794349609651113455378591213521007305334206887210873414776462027746458127536797075994278751416175612148660222076731030937875746805281690156725876857482174279314968596913890067397869829419019179316569148699704292044128937649866682741942026327643537075317389490824101005022833230463069842581
public exponent: 2576402308106616697565204803576809648025446765525597158856684355852417401857269811228595453373248109634555141377011045066015451991315223244608818828620081
与来自服务器的 RSA 比较
Sun RSA public key, 1024 bits
modulus: 105278801605955351183032861925660079240049371193037037019863031780531990060755731057393990664319512505978954889835933668032824049054190807125138251307247771506918060803959346902566451812689470408398611041302277800747341495594297701677567604614145074223483256879948522462707345079591319551080307454838134385381
public exponent: 2576402308106616697565204803576809648025446765525597158856684355852417401857269811228595453373248109634555141377011045066015451991315223244608818828620081
那么模数是错误的。我通过将 BigInteger 转换为字节数组并用 hex
显示它来仔细检查
System.out.println(bytesToHex(rsaPubExponentNumber.toByteArray()));
System.out.println(bytesToHex(rsaModuloNumber.toByteArray()));
private String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
return sb.toString().replaceAll(" ", "");
}
结果是:
指数:
31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131
模数
00E5AA194C3DBA71F9A38AF85D43C130A462BDCBACF85ED702B5E2FECB47197DAA433E7B5ACF5EF09A07C5DDE79B7A2EED76B91090CB981E13DBE316997FCF5DC51BE40B2D02D912457D97924B94094227F8BC65DD61FE78060B55BEAAA31C64E3B36863B407F7183DABA9D98F0C9061DED36AD16407F70C4925951BAA8807EC95
模数现在以 00 开头并且生成了 public 密钥,但密钥不正确。
我也试过:
BigInteger exponent = new BigInteger(exponentHexString, 16);
BigInteger modulus = new BigInteger(modulusHexString, 16);
但是,结果还是一样。我该如何解决这个问题?
让我们检查一下您的代码和数据包流结构。
您将以下二进制流编码为十六进制字符串:
C70001A100408031313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131E5AA194C3DBA71F9A38AF85D43C130A462BDCBACF85ED702B5E2FECB47197DAA433E7B5ACF5EF09A07C5DDE79B7A2EED76B91090CB981E13DBE316997FCF5DC51BE40B2D02D912457D97924B94094227F8BC65DD61FE78060B55BEAAA31C64E3B36863B407F7183DABA9D98F0C9061DED36AD16407F70C4925951BAA8807EC95
根据您的代码:
- 从索引 7 到 71 的字节是您的 指数 字节,
- 从索引 71 到流结尾的字节是您的 模数 字节。
IIUC,你想从上面的字节串中提取指数和模数。
您面临的问题源于这样一个事实,即在密码学中,数字通常是 无符号整数 ,具有协议定义的字节顺序,而 Java' s BigInteger
s 是 big-endian,signed 整数。
为了将任意整数(signed/unsigned、big-endian/little-endian)转换为BigInteger
,必须进行一些操作。
在您的情况下,只需 reverse the byte arrays 就足够了,如评论中所建议的:
int packageLength = 199;
byte[] response = IOUtils.toByteArray(is, packageLength);
byte[] rsaPubExponentBytes = Arrays.copyOfRange(response, 7, 71);
byte[] rsaModuloBytes = Arrays.copyOfRange(response, 71, packageLength);
// missing step!
reverse(rsaPubExponentBytes);
reverse(rsaModuloBytes);
BigInteger rsaPubExponentNumber = new BigInteger(1, rsaPubExponentBytes);
BigInteger rsaModuloNumber = new BigInteger(1, rsaModuloBytes);
// ...
public static void reverse(byte[] arr) {
for (int i = 0; i < arr.length / 2) {
int tmp = arr[i]
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = tmp;
}
}
我在处理我的一个项目时 运行 遇到了类似的问题。您可能会发现我的图书馆 SRP-6 Variables 很有帮助:
byte[] stream = new Hex("C70001A100408031313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131E5AA194C3DBA71F9A38AF85D43C130A462BDCBACF85ED702B5E2FECB47197DAA433E7B5ACF5EF09A07C5DDE79B7A2EED76B91090CB981E13DBE316997FCF5DC51BE40B2D02D912457D97924B94094227F8BC65DD61FE78060B55BEAAA31C64E3B36863B407F7183DABA9D98F0C9061DED36AD16407F70C4925951BAA8807EC95").asArray();
BigInteger exponent =
new SRP6CustomIntegerVariable(
Bytes.wrapped(Arrays.copyOfRange(stream, 7, 71)),
ByteOrder.LITTLE_ENDIAN
).asNonNegativeBigInteger();
BigInteger modulus =
new SRP6CustomIntegerVariable(
Bytes.wrapped(Arrays.copyOfRange(stream, 71, stream.length)),
ByteOrder.LITTLE_ENDIAN
).asNonNegativeBigInteger();
自从我尝试模拟我的客户端和我的服务器之间的连接处理数据包,我一直在与字节数组和 BigInteger 斗争。
当我的客户端连接到服务器时,服务器发送了一个响应,其中包含一个 RSA public 密钥,然后是一个十六进制数据包,就像这样:
C70001A100408031313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131E5AA194C3DBA71F9A38AF85D43C130A462BDCBACF85ED702B5E2FECB47197DAA433E7B5ACF5EF09A07C5DDE79B7A2EED76B91090CB981E13DBE316997FCF5DC51BE40B2D02D912457D97924B94094227F8BC65DD61FE78060B55BEAAA31C64E3B36863B407F7183DABA9D98F0C9061DED36AD16407F70C4925951BAA8807EC95
C70001A1004080
后的64字节是RSApublic指数。
RSA 指数后的 128 字节是模数。
然后我尝试提取指数和模数以重新生成 RSA public 密钥。我的步骤:
if (socket == null) socket = new Socket(loginHost, loginPort);
if (is == null) is = socket.getInputStream();
if (os == null) os = socket.getOutputStream();
int packageLength = 199;
byte[] response = IOUtils.toByteArray(is, packageLength);
byte[] rsaPubExponentBytes = Arrays.copyOfRange(response, 7, 71);
byte[] rsaModuloBytes = Arrays.copyOfRange(response, 71, packageLength);
BigInteger rsaPubExponentNumber = new BigInteger(1, rsaPubExponentBytes);
BigInteger rsaModuloNumber = new BigInteger(1, rsaModuloBytes);
PublicKey pub = null;
try {
RSAPublicKeySpec spec = new RSAPublicKeySpec(rsaModuloNumber, rsaPubExponentNumber);
KeyFactory factory = KeyFactory.getInstance("RSA");
pub = factory.generatePublic(spec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
}
我终于有了一把public钥匙:
Sun RSA public key, 1024 bits
modulus: 161275860318613570660794349609651113455378591213521007305334206887210873414776462027746458127536797075994278751416175612148660222076731030937875746805281690156725876857482174279314968596913890067397869829419019179316569148699704292044128937649866682741942026327643537075317389490824101005022833230463069842581
public exponent: 2576402308106616697565204803576809648025446765525597158856684355852417401857269811228595453373248109634555141377011045066015451991315223244608818828620081
与来自服务器的 RSA 比较
Sun RSA public key, 1024 bits
modulus: 105278801605955351183032861925660079240049371193037037019863031780531990060755731057393990664319512505978954889835933668032824049054190807125138251307247771506918060803959346902566451812689470408398611041302277800747341495594297701677567604614145074223483256879948522462707345079591319551080307454838134385381
public exponent: 2576402308106616697565204803576809648025446765525597158856684355852417401857269811228595453373248109634555141377011045066015451991315223244608818828620081
那么模数是错误的。我通过将 BigInteger 转换为字节数组并用 hex
显示它来仔细检查System.out.println(bytesToHex(rsaPubExponentNumber.toByteArray()));
System.out.println(bytesToHex(rsaModuloNumber.toByteArray()));
private String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
return sb.toString().replaceAll(" ", "");
}
结果是:
指数:
31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131
模数
00E5AA194C3DBA71F9A38AF85D43C130A462BDCBACF85ED702B5E2FECB47197DAA433E7B5ACF5EF09A07C5DDE79B7A2EED76B91090CB981E13DBE316997FCF5DC51BE40B2D02D912457D97924B94094227F8BC65DD61FE78060B55BEAAA31C64E3B36863B407F7183DABA9D98F0C9061DED36AD16407F70C4925951BAA8807EC95
模数现在以 00 开头并且生成了 public 密钥,但密钥不正确。
我也试过:
BigInteger exponent = new BigInteger(exponentHexString, 16);
BigInteger modulus = new BigInteger(modulusHexString, 16);
但是,结果还是一样。我该如何解决这个问题?
让我们检查一下您的代码和数据包流结构。
您将以下二进制流编码为十六进制字符串:
C70001A100408031313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131E5AA194C3DBA71F9A38AF85D43C130A462BDCBACF85ED702B5E2FECB47197DAA433E7B5ACF5EF09A07C5DDE79B7A2EED76B91090CB981E13DBE316997FCF5DC51BE40B2D02D912457D97924B94094227F8BC65DD61FE78060B55BEAAA31C64E3B36863B407F7183DABA9D98F0C9061DED36AD16407F70C4925951BAA8807EC95
根据您的代码:
- 从索引 7 到 71 的字节是您的 指数 字节,
- 从索引 71 到流结尾的字节是您的 模数 字节。
IIUC,你想从上面的字节串中提取指数和模数。
您面临的问题源于这样一个事实,即在密码学中,数字通常是 无符号整数 ,具有协议定义的字节顺序,而 Java' s BigInteger
s 是 big-endian,signed 整数。
为了将任意整数(signed/unsigned、big-endian/little-endian)转换为BigInteger
,必须进行一些操作。
在您的情况下,只需 reverse the byte arrays 就足够了,如评论中所建议的:
int packageLength = 199;
byte[] response = IOUtils.toByteArray(is, packageLength);
byte[] rsaPubExponentBytes = Arrays.copyOfRange(response, 7, 71);
byte[] rsaModuloBytes = Arrays.copyOfRange(response, 71, packageLength);
// missing step!
reverse(rsaPubExponentBytes);
reverse(rsaModuloBytes);
BigInteger rsaPubExponentNumber = new BigInteger(1, rsaPubExponentBytes);
BigInteger rsaModuloNumber = new BigInteger(1, rsaModuloBytes);
// ...
public static void reverse(byte[] arr) {
for (int i = 0; i < arr.length / 2) {
int tmp = arr[i]
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = tmp;
}
}
我在处理我的一个项目时 运行 遇到了类似的问题。您可能会发现我的图书馆 SRP-6 Variables 很有帮助:
byte[] stream = new Hex("C70001A100408031313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131E5AA194C3DBA71F9A38AF85D43C130A462BDCBACF85ED702B5E2FECB47197DAA433E7B5ACF5EF09A07C5DDE79B7A2EED76B91090CB981E13DBE316997FCF5DC51BE40B2D02D912457D97924B94094227F8BC65DD61FE78060B55BEAAA31C64E3B36863B407F7183DABA9D98F0C9061DED36AD16407F70C4925951BAA8807EC95").asArray();
BigInteger exponent =
new SRP6CustomIntegerVariable(
Bytes.wrapped(Arrays.copyOfRange(stream, 7, 71)),
ByteOrder.LITTLE_ENDIAN
).asNonNegativeBigInteger();
BigInteger modulus =
new SRP6CustomIntegerVariable(
Bytes.wrapped(Arrays.copyOfRange(stream, 71, stream.length)),
ByteOrder.LITTLE_ENDIAN
).asNonNegativeBigInteger();