0

我有一个对象集合,我希望从这个对象集合中生成一个哈希值(使用 SHA256)。

散列这些对象的过程是零知识证明系统的一部分,证明者生成证明,稍后由验证者验证。这个证明实际上是一个哈希函数的输出。

这些对象都包含 3 或 4 个大的 BigInteger 值(大约 2048 位)。对象的数量是可变的,但它会在 4 到 10 之间。

我编写了以下方法来从可变数量的对象生成哈希值:

public byte[] buildHashFromHashcodes(final Object... listOfObjects) {

    for (Object obj : listOfObjects) {

        if (obj == null) {
            throw new Exception(
                "Input objects cannot be null");
        }

        _md.update(intToBytes(obj.hashCode()));
    }

    return _md.digest();
}

private byte[] intToBytes(final int intValue) {
    return new byte[] {(byte) (intValue >> 24),
            (byte) (intValue >> 16), (byte) (intValue >> 8),
            (byte) intValue };
}

我的问题与此代码中 hashCode 方法的使用有关。具体来说,我试图确定使用 hashCode 方法是否会削弱系统的安全性,因为它只生成一个 32 位的数字,因此在每次迭代期间,哈希仅更新 32 位的信息。所以我不确定在这个过程中信息的丢失是否真的削弱了系统。

这些对象的 hashCode 方法的实现使用大的 BigInteger 值来生成它们的哈希码,但是该数字在返回之前会被截断为 int。

我的部分担忧源于这样一个事实,即某些对象的哈希码之间相对可能会发生冲突。但是话又说回来,哈希在循环内会更新几次,因此单个冲突不会是一个大问题。

让我们假设对象集合中有 4 个对象。在循环的第一次迭代中,将使用 32 位来更新哈希,在第二次迭代中,将使用另外 32 位来更新它,等等。

据我了解,哈希算法是在调用更新方法之后执行的。不是将 128 位(4 个对象)存储在缓冲区中,然后使用这 128 位作为输入执行散列算法。

因此,我们是否可以说在最终更新后哈希将处于的状态总数为 (2^32) * (2^32) * (2^32) * (2^32)?(在实践中这当然不会发生,因为它会在某些时候被截断)。

我相信使用 hashCode 是一种安全的方法,因为在每次迭代期间都会调用 update 方法。

为了避免对象之间发生冲突的风险,另一种方法是使用每个对象的 toString() 方法,该方法返回一个包含每个对象的全部熵的字符串(大的 BigInteger 数字的值包含在字符串)。这意味着在循环的每次迭代期间,哈希都会更新更多信息,但我不确定这是否有必要。

那么,我的问题是,在这段代码中使用 hashCode 方法会削弱系统的强度吗?

4

2 回答 2

7

这是一个可怕的想法。密码散列函数的目的是彻底混合输入数据,以便每个输入位影响每个输出位。

通过引入中间hashCodes,您可以使每个输入BigInteger只有机会影响一个 32 位 hashCode。因此,单个 32 位 hashCode 中的冲突会导致最终 hash 完全冲突

因此,要攻击您的方案,攻击者只需找到与您的输入 BigInteger 对象之一具有相同 hashCode 的 BigInteger。这是完全不安全的。

于 2014-10-02T18:51:12.837 回答
1

你应该做的而不是调用hashCode- 它本身就有冲突 - 是以字节为单位散列规范编码。BigInteger但是,每个数字都有自己独特的编码作为字节数组。但是,并非所有对象都具有这样的规范编码,因此您无法为其创建通用方法。

此外,您还需要一些将值分开的方法(例如,11 可以是 1 和 1 的串联或数字 11)。最简单的方法可能是在值前面加上值的大小。

public static byte[] buildHashFromSeparatedCanonicalValues(final BigInteger ... numbers) {
    MessageDigest md;
    try {
        md = MessageDigest.getInstance("SHA-256");
    } catch (NoSuchAlgorithmException e) {
        throw new IllegalStateException("SHA-256 should always be available", e);
    }

    final ByteBuffer lengthBuffer = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE);
    for (BigInteger number : numbers) {

        if (number == null) {
            throw new IllegalArgumentException(
                "Input objects cannot be null");
        }

        final byte[] encodedNumber = number.toByteArray();
        lengthBuffer.putInt(encodedNumber.length);
        lengthBuffer.flip();
        md.update(lengthBuffer);
        lengthBuffer.clear();
        md.update(encodedNumber);
    }

    return md.digest();
}

这是特定于BigInteger值的。您可以通过序列化对象(实现Serializable)来使其更通用,但要注意序列化陷阱。

于 2014-10-03T02:20:23.370 回答