【问题标题】:Faster MD5 alternative?更快的 MD5 替代方案?
【发布时间】:2010-09-22 06:29:01
【问题描述】:

我正在开发一个程序,它可以在整个驱动器中搜索给定文件。目前,我计算已知文件的 MD5 哈希值,然后递归扫描所有文件,寻找匹配项。

唯一的问题是 MD5 在处理大文件时速度非常慢。有没有更快的替代方案,我可以使用同时保留非常小的误报概率?

所有代码都在 C# 中。

谢谢。

更新

我读到即使 MD5 也可以很快,磁盘 I/O 应该是限制因素。这让我相信我的代码可能不是最优的。这种方法有什么问题吗?

        MD5 md5 = MD5.Create();
        StringBuilder sb = new StringBuilder();
        try
        {
            using (FileStream fs = File.Open(fileName, FileMode.Open, FileAccess.Read))
            {
                foreach (byte b in md5.ComputeHash(fs))
                    sb.Append(b.ToString("X2"));
            }
            return sb.ToString();
        }
        catch (Exception)
        {
            return "";
        }

【问题讨论】:

标签: c# md5 hash


【解决方案1】:

只是线性读取文件?读取整个文件,计算 md5 哈希,然后比较哈希似乎毫无意义。

按顺序读取文件,一次读取几个字节,可以让您在读取 ​​4 个字节后丢弃绝大多数文件。而且您可以节省计算散列函数的所有处理开销,这在您的情况下不会给您任何东西。

如果您已经拥有驱动器中所有文件的哈希值,比较它们是有意义的,但如果您必须即时计算它们,那么哈希似乎没有任何优势.

我在这里遗漏了什么吗?在这种情况下,散列能给你带来什么?

【讨论】:

  • 不幸的是,当程序运行时我无法访问原始文件,因此存储哈希(实际上是许多哈希)是我可以比较的唯一方法。
  • 至少,如果您可以存储散列加上前几个字节(最好大于 4,因为这通常是文件格式幻数的大小),那么您可以丢弃绝大多数情况只打开了文件并读取了几个字节。
【解决方案2】:

首先考虑一下真正的瓶颈是什么:哈希函数本身还是磁盘访问速度?如果您受到磁盘的限制,那么更改散列算法不会给您带来太多好处。根据您的描述,我暗示您总是在扫描整个磁盘以找到匹配项 - 考虑先构建索引,然后仅将给定的哈希与索引匹配,这会快得多。

【讨论】:

    【解决方案3】:

    我希望您仅在文件大小已经匹配时才检查 MD5 匹配。

    另一个优化是对前 1K(或其他一些任意但相当小的数字)进行快速校验和,并在处理整个文件之前确保它们匹配。

    当然,所有这些都假设您只是在寻找特定文件的匹配/不匹配决定。

    【讨论】:

    • +1 表示切片。当第一个字节不同时,无需散列 37 个演出。
    【解决方案4】:

    使用 MD5 比较文件有一个小问题:已知的文件对不同但具有 相同 MD5。

    这意味着您可以使用MD5来判断文件是否不同(如果MD5不同,则文件必须不同),但不能使用MD5来判断文件是否equal(如果文件相等,则 MD5 必须相同,但如果 MD5 相等,则文件可能相等也可能不相等。

    您应该使用尚未破坏的哈希函数(如 SHA-1),或者(如 @SoapBox 所述)仅使用 MD5 作为快速查找候选者进行更深入比较的方法。

    参考资料:

    【讨论】:

    • 正确,但这通常适用于散列。当哈希为 n 位长时,只有 2^n 个可能的哈希值。但是不同文件的数量是无限的。因此,具有相同哈希值的不同文件对的数量也是可数无限的。
    • @Ingo:是的,但是对于 MD5,我们知道如何创建一对具有相同哈希值的文件(不仅如此,还有几个这样的对是已知的)。对于尚未破解的密码哈希,我们不能故意创建这样的一对,并且偶然创建它的概率极小,小到我们可以将其视为根本不可能(至少在那之前)哈希也被破坏了)。
    • 他的用例似乎并不关心攻击,所以可能没关系。
    【解决方案5】:

    无论加密要求如何,都存在哈希冲突的可能性,因此无法使用哈希函数保证两个文件相同。

    前段时间我写了类似的代码,通过首先索引所有文件,然后丢弃任何不同大小的文件,我运行得非常快。然后对剩余的条目执行快速哈希比较(在每个文件的一部分上)(在此步骤中比较字节被证明不太有用 - 许多文件类型具有在文件开头具有相同字节的公共标头)。然后使用 MD5 检查在此阶段之后留下的所有文件,最后如果 MD5 匹配,则对整个文件进行字节比较,以确保内容相同。

    【讨论】:

    • 听起来不错,合乎逻辑 - 感谢您的参与。
    【解决方案6】:

    使用 MD5CryptoServiceProvider 和 BufferedStream

            using (FileStream stream = File.OpenRead(filePath))
            {
                using (var bufferedStream = new BufferedStream(stream, 1024 * 32))
                {
                    var sha = new MD5CryptoServiceProvider();
                    byte[] checksum = sha.ComputeHash(bufferedStream);
                    return BitConverter.ToString(checksum).Replace("-", String.Empty);
                }
            }
    

    【讨论】:

    • -1:这不会使过程更快。为了使其更快,只有 5 年以前的、被接受和高度支持的答案 可以工作。
    猜你喜欢
    • 2018-12-11
    • 2012-07-05
    • 2012-01-23
    • 2013-07-13
    • 2011-02-27
    • 2017-05-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多