在 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.

不要用额外的 00ff 字节填充数字的前面。

  1. 80 存储为 00 00 00 03 00 00 80 有一个额外的前导 00 字节。

  2. -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,或者小于零。

  1. 您要存储号码 123。

  2. 你知道这个数字肯定会以 100 为模。即123 % 100.

  3. 您应该改为存储 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 而不是空数组。