为什么在散列之前在java字节数组中删除前导全零字节

时间:2015-01-26 16:30:28

标签: arrays hash cryptography srp-protocol

这个问题是关于字节数组在java中进行哈希处理之前要进行的操作。

我正在尝试理解为什么在多个srp加密库中,前导零字节(如果有的话)在被散列之前被丢弃。

例如:这是来自Bouncy Castle

/**
 * Return the passed in value as an unsigned byte array.
 *
 * @param value value to be converted.
 * @return a byte array without a leading zero byte if present in the signed encoding.
 */
public static byte[] asUnsignedByteArray(int length, BigInteger value)
{
    byte[] bytes = value.toByteArray();
    if (bytes.length == length)
    {
        return bytes;
    }

    int start = bytes[0] == 0 ? 1 : 0;
    int count = bytes.length - start;

    if (count > length)
    {
        throw new IllegalArgumentException("standard length exceeded for value");
    }

    byte[] tmp = new byte[length];
    System.arraycopy(bytes, start, tmp, tmp.length - count, count);
    return tmp;
}

或者这是来自nimbus SRP:

public static byte[] toUnsignedByteArray(final BigInteger bigInteger) {

    byte[] bytes = bigInteger.toByteArray();
    byte[] result = toUnsignedByteArray(bytes);

    // remove leading zero if any
    if (bytes[0] == 0) {

        byte[] tmp = new byte[bytes.length - 1];

        System.arraycopy(bytes, 1, tmp, 0, tmp.length);

        return tmp;
    }
    return bytes;
}

买入的例子基本上是领先的零点。这些库中的方法是调用“toUnsignedByteArray”,虽然我不明白为什么删除前导零将使字节数组无符号。即它只丢弃零字节,然后下一个字节可能是负的,即下一个字节成为最左边的字节(在Big Indian中),字节中最左边的位是符号位,可以根据字节设置或取消设置,所以如果我正确理解了字节数组的结构,那么首先不应该将这些方法调用到“toUnsignedByteArray”。然而,最重要的问题是为什么我们需要在零全部为零的情况下丢弃该零字节

以下是来自srp rfc 5054附录A的测试向量的示例。我们从A和B计算U.其中B的零字节恰好是二进制的全零,即如果我们将B打印为字节数组,我们将获得以下值

public static final B = new BigInteger("BD0C61512C692C0CB6D041FA01BB152D4916A1E77AF46AE105393011BAF38964DC46A0670DD125B95A981652236F99D9B681CBF87837EC996C6DA04453728610D0C6DDB58B318885D7D82C7F8DEB75CE7BD4FBAA37089E6F9C6059F388838E7A00030B331EB76840910440B1B27AAEAEEB4012B7D7665238A8E3FB004B117B58", 16);
  

[0,-67,12,97,81,44,105,44,12,-74,-48,65,-6,1,-69,21,   45,73,22,-95,-25,122,-12,106,-31,5,57,48,17,-70,-13,   -119,100,-36,70,-96,103,13,-47,37,-71,90,-104,22,82,35,111,-103,-39,-74,-127, -53,-8,120,55,-20,-103,108,109,-96,   68,83,114,-122,16,-48,-58,-35,-75,-117,49,-120,-123,-41,   -40,44,127,-115,-21,117,-50,123,-44,-5,-86,55,8,-98,111,-100,96,89,-13,-120 ,-125,-114,122,0,3,11,51,30,-73,104,64,-111,4,64,-79,-78,122,-82,-82,-21, 64,18,-73,-41,102,   82,56,-88,-29,-5,0,75,17,123,88]

     

以二进制打印的字节零:00000000

现在我明白,由于某些原因,删除该字节很重要(虽然我不确定)我的意思是,因为那些测试向量正确计算这两个库应该正确编程吗?但是我不明白为什么我们需要丢弃那个前导零字节。它有什么问题。如果我删除那个前导的zeor字节并尝试从没有前导零字节的字节数组创建另一个BigInteger,那么在这种情况下我将得到一个完全不同的数字甚至是负数。因此,删除该零字节不会给我任何场景。欢迎任何解释。

1 个答案:

答案 0 :(得分:2)

" unsigned"在名字中可能有点误导;它不是丢弃0字节使其无符号,它只是假设BigInteger包含无符号数。

在这些情况下丢弃的0字节不会更改该值,就像010011的值相同。

出于各种原因放弃零是很重要的:

  1. 不浪费不必要的0字节空间。
  2. 在比较字节数组时使表示保持一致。
  3. (并且在你指的上下文中最相关)前面有一个额外0的字节数组的散列与没有额外0的字节数组的散列不一样。散列函数毕竟不知道这是一个数字,在这种情况下0是没有意义的。想象一下,如果这是一个文件,字节为0:1:2:3 vs一个字节为1:2:3的文件。你不会期望长度不同的文件的哈希是相同的。
  4. 另请注意,是否要从开头或结尾删除0字节取决于整数表示的endianness

    更新:澄清删除0字节:

    虽然从任何旧字节数组的开头或结尾删除0字节会更改值,但在您引用的情况下,我们正在谈论表示一个整数。如果0字节具有重要性,例如如果要对某些二进制数据进行往返,则将该二进制数据加载到BigInteger类中是不合适的。我参考我原来的例子,你不会认为101是不同的数字(虽然你会认为它们是不同的字符串)?

    更新:关于字节序的澄清:

    整数可以在内存中以不同的方式表示。如果您看到数字20(普通十进制),您知道2指的是十位数,但这只是一个约定。我们可能会向后写二十个02,并将最大的单位放在数字的末尾。类似地,在计算机中,数字的顺序可以是我们通常熟悉它们的方式,或者它们可以是#34;向后"。鉴于此,不会影响数字值的0可能在字节数组的开头或结尾处,我们必须知道何时处理字节数组的方式应该是"阅读"。