通过使用hashCode()的输出作为SHA256的输入,在生成一组对象的哈希时,任何安全强度的损失

时间:2014-10-02 17:20:09

标签: java cryptography hashcode sha256 information-theory

我有一组对象,我希望从这个对象集合中生成一个哈希值(使用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位来更新散列等。

据我所知,在调用update方法后执行散列算法。不是128位(4个对象)将存储在缓冲区中,然后使用这128位作为输入执行散列算法。

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

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

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

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

2 个答案:

答案 0 :(得分:7)

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

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

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

答案 1 :(得分: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)来使其更通用,但要注意序列化陷阱。