在 Java 中创建 'mpint' 值(安全 Shell (SSH) 协议体系结构 - RFC 4251)
Creating 'mpint' value in Java (The Secure Shell (SSH) Protocol Architecture - RFC 4251)
我正在尝试使用 RFC4251 中指定的 BigInteger 创建 mpint
字符串:
mpint
Represents multiple precision integers in two's complement format,
stored as a string, 8 bits per byte, MSB first. Negative numbers
have the value 1 as the most significant bit of the first byte of
the data partition. If the most significant bit would be set for
a positive number, the number MUST be preceded by a zero byte.
Unnecessary leading bytes with the value 0 or 255 MUST NOT be
included. The value zero MUST be stored as a string with zero
bytes of data.
By convention, a number that is used in modular computations in
Z_n SHOULD be represented in the range 0 <= x < n.
Examples:
value (hex) representation (hex)
----------- --------------------
0 00 00 00 00
9a378f9b2e332a7 00 00 00 08 09 a3 78 f9 b2 e3 32 a7
80 00 00 00 02 00 80
-1234 00 00 00 02 ed cc
-deadbeef 00 00 00 05 ff 21 52 41 11
一切都差不多清楚了,但是如何解释“不得包含值为 0 或 255 的不必要的前导字节。”?
第二个问题是关于这一行的:"By convention, a number that is used in modular computations in Z_n SHOULD be represented in the range 0 <= x < n."。怎么解释?
编辑:
我的第一个建议是:
/**
* Write 'mpint' to output stream including length.
*
* @param dos output stream
* @param bi the value to be written
*/
public static void writeMPInt(DataOutputStream dos, BigInteger bi) throws IOException {
byte[] twos = bi.toByteArray();
dos.writeInt(twos.length);
dos.write(twos);
}
根据上述规则,此方法是否有效?
Unnecessary leading bytes with the value 0 or 255 MUST NOT be included.
不要用额外的 00
或 ff
字节填充数字的前面。
80
存储为 00 00 00 03 00 00 80
有一个额外的前导 00
字节。
-deadbeef
存储为 00 00 00 06 ff ff 21 52 41 11
有一个额外的前导 ff
字节。
在这两种情况下,数字在技术上都是正确的,但有不必要的前导字节。
By convention, a number that is used in modular computations in Z_n SHOULD be represented in the range 0 <= x < n.
Z_n 是为 integers modulo n. (see Modular arithmetic)
编写粗体数学符号的 ASCII 方式
这意味着,您不应该存储一个数字x
大于要使用的模数n
,或者小于零。
您要存储号码 123。
你知道这个数字肯定会以 100 为模。即123 % 100
.
您应该改为存储 23。
Does this method is valid according mentioned above rules?
不,write()
不会检查您的字节数组中的值是否符合上述规则。
我不完全同意 Jay Jun 的观点,因为我第一次没有理解它,所以我会尝试给出一个不同的(希望更简单)答案(在代码中):
Unnecessary leading bytes with the value 0 or 255 MUST NOT be included.
以下代码片段将输出值的 long(8 字节):0x80 和 -0xdeadbeef
//long has 8 bytes
long l = 0x80L;
System.out.println("0x80 = "+String.format("%02X ", (l>>56) & 0xFF)+" "+String.format("%02X ", (l>>48) & 0xFF)+" "+String.format("%02X ", (l>>40) & 0xFF)+" "+String.format("%02X ", (l>>32) & 0xFF)+String.format("%02X ", (l>>24) & 0xFF)+" "+String.format("%02X ", (l>>16) & 0xFF)+" "+String.format("%02X ", (l>>8) & 0xFF)+" "+String.format("%02X ", (l) & 0xFF));
l = -0xdeadbeefL;
System.out.println("-0xdeadbeef = "+String.format("%02X ", (l>>56) & 0xFF)+" "+String.format("%02X ", (l>>48) & 0xFF)+" "+String.format("%02X ", (l>>40) & 0xFF)+" "+String.format("%02X ", (l>>32) & 0xFF)+String.format("%02X ", (l>>24) & 0xFF)+" "+String.format("%02X ", (l>>16) & 0xFF)+" "+String.format("%02X ", (l>>8) & 0xFF)+" "+String.format("%02X ", (l) & 0xFF));
输出为:
0x80L = 00 00 00 00 00 00 00 80
-0xdeadbeefL = FF FF FF FF 21 52 41 11
我们可以看到 0x80L 是正数并且有 7 个前导 0 字节,但不能包含只有 6 个字节,因为 80(二进制值 = 10000000)的前导位是 1,因此根据:
If the most significant bit would be set for a positive number, the number MUST be preceded by a zero byte
我们必须在 0x80 之前加上一个 0 字节。因此结果是“00 80”。
反之亦然,对于 -0xdeadbeef,我们有 4 个前导 255 字节,但不能只包含 3 个,因为“21”的最高有效位是“0”,因此我们需要在“21 52 41 11”之前有 1 个前导 255 字节" 结果是 "ff 21 42 41 11"
所有这些前导字节问题都在使用 BigInteger 时得到处理:
byteArray = new byte[]{(byte) 0x80};
bigInteger = new BigInteger(+1, byteArray);
byteArray = bigInteger.toByteArray();
byteArray = new byte[]{(byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef};
bigInteger = new BigInteger(-1, byteArray);
byteArray = bigInteger.toByteArray();
所以回到问题:
Does this method is valid according mentioned above rules?
我会说是.
PS:BigInteger 没有正确处理的唯一规则是:
The value zero MUST be stored as a string with zero bytes of data
byteArray = new byte[]{};
bigInteger = new BigInteger(0, byteArray);
byteArray = bigInteger.toByteArray();
实际上 return 1 个字节,值为 0 而不是空数组。
我正在尝试使用 RFC4251 中指定的 BigInteger 创建 mpint
字符串:
mpint
Represents multiple precision integers in two's complement format, stored as a string, 8 bits per byte, MSB first. Negative numbers have the value 1 as the most significant bit of the first byte of the data partition. If the most significant bit would be set for a positive number, the number MUST be preceded by a zero byte. Unnecessary leading bytes with the value 0 or 255 MUST NOT be included. The value zero MUST be stored as a string with zero bytes of data.
By convention, a number that is used in modular computations in Z_n SHOULD be represented in the range 0 <= x < n.
Examples:
value (hex) representation (hex) ----------- -------------------- 0 00 00 00 00 9a378f9b2e332a7 00 00 00 08 09 a3 78 f9 b2 e3 32 a7 80 00 00 00 02 00 80 -1234 00 00 00 02 ed cc -deadbeef 00 00 00 05 ff 21 52 41 11
一切都差不多清楚了,但是如何解释“不得包含值为 0 或 255 的不必要的前导字节。”?
第二个问题是关于这一行的:"By convention, a number that is used in modular computations in Z_n SHOULD be represented in the range 0 <= x < n."。怎么解释?
编辑:
我的第一个建议是:
/**
* Write 'mpint' to output stream including length.
*
* @param dos output stream
* @param bi the value to be written
*/
public static void writeMPInt(DataOutputStream dos, BigInteger bi) throws IOException {
byte[] twos = bi.toByteArray();
dos.writeInt(twos.length);
dos.write(twos);
}
根据上述规则,此方法是否有效?
Unnecessary leading bytes with the value 0 or 255 MUST NOT be included.
不要用额外的 00
或 ff
字节填充数字的前面。
80
存储为00 00 00 03 00 00 80
有一个额外的前导00
字节。-deadbeef
存储为00 00 00 06 ff ff 21 52 41 11
有一个额外的前导ff
字节。
在这两种情况下,数字在技术上都是正确的,但有不必要的前导字节。
By convention, a number that is used in modular computations in Z_n SHOULD be represented in the range 0 <= x < n.
Z_n 是为 integers modulo n. (see Modular arithmetic)
编写粗体数学符号的 ASCII 方式这意味着,您不应该存储一个数字x
大于要使用的模数n
,或者小于零。
您要存储号码 123。
你知道这个数字肯定会以 100 为模。即
123 % 100
.您应该改为存储 23。
Does this method is valid according mentioned above rules?
不,write()
不会检查您的字节数组中的值是否符合上述规则。
我不完全同意 Jay Jun 的观点,因为我第一次没有理解它,所以我会尝试给出一个不同的(希望更简单)答案(在代码中):
Unnecessary leading bytes with the value 0 or 255 MUST NOT be included.
以下代码片段将输出值的 long(8 字节):0x80 和 -0xdeadbeef
//long has 8 bytes
long l = 0x80L;
System.out.println("0x80 = "+String.format("%02X ", (l>>56) & 0xFF)+" "+String.format("%02X ", (l>>48) & 0xFF)+" "+String.format("%02X ", (l>>40) & 0xFF)+" "+String.format("%02X ", (l>>32) & 0xFF)+String.format("%02X ", (l>>24) & 0xFF)+" "+String.format("%02X ", (l>>16) & 0xFF)+" "+String.format("%02X ", (l>>8) & 0xFF)+" "+String.format("%02X ", (l) & 0xFF));
l = -0xdeadbeefL;
System.out.println("-0xdeadbeef = "+String.format("%02X ", (l>>56) & 0xFF)+" "+String.format("%02X ", (l>>48) & 0xFF)+" "+String.format("%02X ", (l>>40) & 0xFF)+" "+String.format("%02X ", (l>>32) & 0xFF)+String.format("%02X ", (l>>24) & 0xFF)+" "+String.format("%02X ", (l>>16) & 0xFF)+" "+String.format("%02X ", (l>>8) & 0xFF)+" "+String.format("%02X ", (l) & 0xFF));
输出为:
0x80L = 00 00 00 00 00 00 00 80
-0xdeadbeefL = FF FF FF FF 21 52 41 11
我们可以看到 0x80L 是正数并且有 7 个前导 0 字节,但不能包含只有 6 个字节,因为 80(二进制值 = 10000000)的前导位是 1,因此根据:
If the most significant bit would be set for a positive number, the number MUST be preceded by a zero byte
我们必须在 0x80 之前加上一个 0 字节。因此结果是“00 80”。
反之亦然,对于 -0xdeadbeef,我们有 4 个前导 255 字节,但不能只包含 3 个,因为“21”的最高有效位是“0”,因此我们需要在“21 52 41 11”之前有 1 个前导 255 字节" 结果是 "ff 21 42 41 11"
所有这些前导字节问题都在使用 BigInteger 时得到处理:
byteArray = new byte[]{(byte) 0x80};
bigInteger = new BigInteger(+1, byteArray);
byteArray = bigInteger.toByteArray();
byteArray = new byte[]{(byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef};
bigInteger = new BigInteger(-1, byteArray);
byteArray = bigInteger.toByteArray();
所以回到问题:
Does this method is valid according mentioned above rules?
我会说是.
PS:BigInteger 没有正确处理的唯一规则是:
The value zero MUST be stored as a string with zero bytes of data
byteArray = new byte[]{};
bigInteger = new BigInteger(0, byteArray);
byteArray = bigInteger.toByteArray();
实际上 return 1 个字节,值为 0 而不是空数组。