Arrays.equals() vs MessageDigest.isEqual()

English 简体中文 ภาษาไทย Tiếng Việt
Summary

When comparing byte arrays, developers often choose between Arrays.equals() and MessageDigest.isEqual(), each with distinct characteristics. Arrays.equals() prioritizes efficiency by failing fast, returning immediately upon the first differing byte, but this non-constant time behavior can expose systems to timing attacks, especially in security-sensitive contexts like key or hash comparisons. Conversely, MessageDigest.isEqual() performs a time-constant comparison, iterating through all bytes regardless of early mismatches, which prioritizes security over raw speed. Therefore, the choice hinges on the application's needs: opt for Arrays.equals() when efficiency is paramount, but always use MessageDigest.isEqual() for cryptographic or security-critical comparisons to mitigate timing attack risks.

同时使用Arrays.equals()MessageDigest.isEqual() 来比较两个数组的相等性。在许多情况下,它们可以互换使用。但是,它们确实有一些区别,导致在实际应用中使用场景不同。

一个区别是,传递给MessageDigest.isEqual()的数组不能为null,而Arrays.equals()则可以。

这两种方法之间的一个主要区别是,Arrays.equals()不是恒定时间算法,而MessageDigest.isEqual()是恒定时间算法。这意味着,当比较两个数组时,数组逐字节比较,Arrays.equals()将立即返回第一个不相等的字节。MessageDigest.isEqual()将比较数组中的所有字节,无论第一个不相等的字节在哪里。

Arrays.equals()的实现是:

public static boolean equals(byte[] a, byte[] a2) {
    if (a==a2)
        return true;
    if (a==null || a2==null)
        return false;

    int length = a.length;
    if (a2.length != length)
        return false;

    for (int i=0; i<length; i++)
        if (a[i] != a2[i])
            return false;

    return true;
}

从for循环可以看出,Arrays.equals()是一种快速失败方法。如果第一个字节不相等,它将立即返回。这通常意味着效率高,但安全性低。实际上,从实现来看,此方法的设计是为了提高效率。它们进行了一些检查以快速确定两个数组是否相等,而无需实际比较数组的内容。

MessageDigest.isEqual()的实现是:

public static boolean isEqual(byte[] digesta, byte[] digestb) {
    if (digesta.length != digestb.length) {
        return false;
    }

    int result = 0;
    // time-constant comparison
    for (int i = 0; i < digesta.length; i++) {
        result |= digesta[i] ^ digestb[i];
    }
    return result == 0;
}

从for循环可以看出,数组被完全比较。这意味着安全性高,但效率低。

在高安全要求的系统中,Arrays.equals()可能容易受到恒定时间攻击。例如,如果使用Arrays.equals()进行哈希或密钥比较,攻击者可以伪造哈希或密钥并与实际哈希或密钥进行比较。他们可以测量Arrays.equals()返回所需的时间,以了解有多少字节与伪造的哈希或密钥匹配。经过数百或数千次尝试后,他们可能会泄露实际的哈希和密钥。有关此攻击如何工作的详细信息,您可以阅读A Lesson In Timing Attacks (or, Don’t use MessageDigest.isEquals)

因此,当您决定使用哪种方法来比较两个数组时,请仔细考虑应用程序是关注安全性还是关注效率。如果需要效率,那么使用Arrays.equals();但是,如果您正在开发安全产品,那么请谨慎并继续使用MessageDigest.isDigest()。

记住,黑客是一群“无所不用其极;不择手段”的人。

JAVA SECURITY ARRAYS.EQUAL() MESSAGEDIGEST.ISEQUAL()

  RELATED

  COMMENTS

0

No comment for this article.