我应该选择哪个加密哈希函数?

151

.NET框架提供了6种不同的哈希算法:

  • MD5:16字节(哈希500MB的时间:1462毫秒)
  • SHA-1:20字节(1644毫秒)
  • SHA256:32字节(5618毫秒)
  • SHA384:48字节(3839毫秒)
  • SHA512:64字节(3820毫秒)
  • RIPEMD:20字节(7066毫秒)

每个函数的性能不同;MD5是最快的,而RIPEMD是最慢的。

MD5的优点是它适合内置的Guid类型;并且它是类型3 UUID的基础SHA-1哈希是类型5 UUID的基础。这使得它们非常易于用于标识。

然而,MD5容易受到碰撞攻击的影响,SHA-1也容易受到影响,但程度较轻。

在什么条件下应该使用哪种哈希算法?

我真正想知道的问题是:

  • MD5不可信吗?在没有恶意意图且没有第三方有任何恶意意图的情况下,您是否预期会出现任何碰撞(即两个任意byte[]产生相同的哈希值)

  • RIPEMD比SHA1好多少?(如果有的话)计算时间慢了5倍,但哈希大小与SHA1相同。

  • 在对文件名(或其他短字符串)进行哈希时,获得非恶意碰撞的几率有多大? (例如,具有相同MD5哈希的2个随机文件名)(使用MD5 / SHA1 / SHA2xx)通常而言,非恶意碰撞的几率有多大?

这是我使用的基准测试:

    static void TimeAction(string description, int iterations, Action func) {
        var watch = new Stopwatch();
        watch.Start();
        for (int i = 0; i < iterations; i++) {
            func();
        }
        watch.Stop();
        Console.Write(description);
        Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
    }

    static byte[] GetRandomBytes(int count) {
        var bytes = new byte[count];
        (new Random()).NextBytes(bytes);
        return bytes;
    }
    

    static void Main(string[] args) {

        var md5 = new MD5CryptoServiceProvider();
        var sha1 = new SHA1CryptoServiceProvider();
        var sha256 = new SHA256CryptoServiceProvider();
        var sha384 = new SHA384CryptoServiceProvider();
        var sha512 = new SHA512CryptoServiceProvider();
        var ripemd160 = new RIPEMD160Managed();

        var source = GetRandomBytes(1000 * 1024);

        var algorithms = new Dictionary<string,HashAlgorithm>();
        algorithms["md5"] = md5;
        algorithms["sha1"] = sha1;
        algorithms["sha256"] = sha256;
        algorithms["sha384"] = sha384;
        algorithms["sha512"] = sha512;
        algorithms["ripemd160"] = ripemd160;

        foreach (var pair in algorithms) {
            Console.WriteLine("Hash Length for {0} is {1}", 
                pair.Key, 
                pair.Value.ComputeHash(source).Length);
        }

        foreach (var pair in algorithms) {
            TimeAction(pair.Key + " calculation", 500, () =>
            {
                pair.Value.ComputeHash(source);
            });
        }

        Console.ReadKey();
    }

15
你提到MD5适合GUID(16字节)格式,这表明你对问题有基本的误解。哈希不保证唯一,但很少出现(如果在加密方面使用,则很难伪造),并且是从其哈希的事物派生而来,而GUID则是唯一的,但与其标识的内容无关。它们用于非常不同的目的。 - Barry Wark
2
只是一个方便的实现细节,并不相关。我知道你无法将无穷大放入16字节中。使用任何哈希算法都可能发生碰撞。 - Sam Saffron
5
在实践中,GUID通常是唯一的。但理论上,如果你不断生成GUID,最终会出现重复。 - Sam Saffron
3
即使适合,您也确实不应该将哈希值塞入GUID中。最简单的例子是:两个副本的相同文件应具有不同的GUID,但相同的哈希值。一个人名字的前8个字母也可以很好地放入16个字节中。 - dbkk
2
@user2332868 SHA-1的破解对于“意外”碰撞的概率没有影响。当恶意意图对您的使用构成威胁时,我认为盲目选择任何哈希函数都是错误的,您需要花时间针对您的具体情况进行风险/成本分析。 - Andrey Tarantsov
显示剩余9条评论
9个回答

150
在密码学中,哈希函数提供了三个独立的功能:
1. 碰撞抗拒性:有多难为别人找到两个(任意的)相同哈希值的消息。
2. 预像防护:已知一个哈希值,有多难找到另一个与之相同的消息?也被称为单向哈希函数。
3. 第二预像防护:已知一个消息,有多难找到另一个与之相同的哈希值。
这些属性是相关但独立的。例如,碰撞抗拒性意味着第二预像防护,但反过来则不成立。对于任何给定的应用程序,您都需要不同的要求,需要一个或多个这些属性。用于在服务器上保护密码的哈希函数通常只需要预像防护,而消息摘要则需要全部三个。
已经证明MD5不具备碰撞抗拒性,然而,这并不排除它在不需要碰撞抗拒性的应用程序中使用。实际上,在速度和较小的密钥尺寸方面,MD5仍然经常被使用。尽管如此,由于其缺陷,研究人员建议在新的场景中使用其他哈希函数。
SHA1存在一个漏洞,可以在理论上少于一个相同长度的安全哈希函数所需的2^80步骤中找到碰撞。该攻击正在不断修订,目前可以在约2^63步中完成——仅限于当前计算能力的范围内(截至2009年4月)。因此,NIST正在逐步淘汰SHA1的使用,并建议在2010年之后采用SHA2系列。
SHA2是在SHA1之后创建的新一代哈希函数。目前没有已知的对SHA2函数的攻击。SHA256、384和512都是SHA2系列的一部分,只是使用了不同的密钥长度。
RIPEMD I 我不能过多地评论它,除了注意到它并没有像SHA系列那样广泛地使用,因此没有接受密码学研究人员的严格审查。仅出于这个原因,我会建议使用SHA函数而非RIPEMD I。在您所使用的实现中,它似乎也很慢,这使得它的实用性降低。

总之,没有一种最好的函数-它完全取决于你需要它做什么。注意每个函数的缺陷,这样你就能够选择适合场景的正确哈希函数。


⚠️ 警告

2022年8月

不要在加密应用中使用SHA-1或MD5。这两种算法已被攻破(MD5可以在30秒内被一个手机破解)。



1
对于某些应用程序,甚至非加密级别的哈希函数可能是合适的。作者没有提到它是特定用于密码、挑战 - 响应认证、访问令牌,还是只是索引一堆字符串/文件。然而,性能是作者关注的问题... - Seva Alekseyev

124

所有哈希函数都是“破解”的

鸽巢原理 表明,无论你如何努力,你都不能将超过 2 只鸽子放在 2 个洞中(除非你把鸽子切开)。同样地,你不能将 2^128 +1 个数字放入 2^128 个槽中。所有哈希函数都会产生有限大小的哈希值,这意味着如果你搜索 “有限大小” +1 序列,你总能找到冲突。只是这样做不可行。无论是 MD5 还是 Skein

MD5/SHA1/Sha2xx 不可能发生碰撞

所有哈希函数都会产生碰撞,这是一种必然现象。偶然遇到这些碰撞就相当于赢得星际彩票。也就是说,没有人赢得星际彩票,因为彩票不是那么运作的。你永远不会偶然遇到 MD5/SHA1/SHA2XXX 哈希值的碰撞。每个语言中每本字典中的每个单词都会散列成不同的值。在整个星球上,每台计算机上的每个路径名都有不同的 MD5/SHA1/SHA2XXX 哈希值。你可能会问我怎么知道这一点。嗯,正如我之前所说,没有人赢得星际彩票。

但是...MD5 是“破解”的

有时候它被破解并不重要

目前还没有已知的针对 MD5 的 预像攻击或第二预像攻击

你可能会问,MD5有什么问题?事实上,第三方可以生成两条消息,一条是恶意的,一条是正常的,它们的哈希值相同。(碰撞攻击) 然而,如果需要预像防护,当前RSA建议不要使用MD5。在涉及安全算法时,人们往往会采取慎重态度。
那么,在.NET中应该使用哪种哈希函数呢?
- 如果需要速度和大小,并且不关心生日攻击或预像攻击,可以使用MD5。 - 请记住,不存在MD5碰撞的机会,恶意碰撞可以被精心制造。尽管到目前为止没有已知的MD5预像攻击,但来自安全专家的建议是不要在需要防范预像攻击的地方使用MD5。SHA1也是如此。 - 对于一些场景,不必考虑预像或碰撞攻击。例如,在硬盘上进行重复文件的初步搜索。 - 如果需要加密安全哈希函数,请使用基于SHA2XX的函数。 - 不要使用SHA1或RIPEMD,除非是为了兼容性场景。RIPMED没有接受SHAX和MD5接受的同样多的审查。SHA1和RIPEMD都容易受到生日攻击的影响。它们在.NET上比MD5慢,且哈希值大小为20字节。使用这些函数是没有意义的,最好忘记它们。 - SHA1碰撞攻击已经降至2^52,SHA1碰撞不久将出现。

要获取有关各种哈希函数的最新信息,请查看哈希函数动物园

但是,还有更多

拥有快速哈希函数可能是一种诅咒。例如:哈希函数的一个非常常见的用途是密码存储。基本上,您计算与已知随机字符串(以防彩虹攻击)组合的密码哈希值,并将该哈希值存储在数据库中。

问题在于,如果攻击者获得数据库转储,他可以使用暴力破解相当有效地猜测密码。他尝试的每个组合只需要几毫秒,并且他可以每秒尝试数十万个密码。

为了解决这个问题,可以使用bcrypt算法,它被设计为缓慢,因此如果攻击使用bcrypt的系统,则攻击者将受到严重的减速。最近,scrypt已经成为头条新闻,并被认为比bcrypt更有效,但我不知道是否有.Net实现。


虽然MD5和SHA-1都已经被削弱,但MD5比SHA-1要弱得多,而速度仅略快。实际上,已经发现并用于真实世界的攻击(伪造CA证书)。但据我所知,尚未发现任何实际的SHA-1碰撞(尽管操作次数已经从蛮力算法中大大减少)。考虑到MD5有多么弱,如果对于MD5而非SHA-1,第二次溢出攻击较早出现,我也不会感到惊讶。因此,如果您需要速度而不需要碰撞抵抗力,那么应使用SHA-1,否则使用SHA-2家族之一。 - Brian Campbell
1
@Brian,很明显,在未来几年内,人们将能够在SHA1上运行碰撞攻击,这将使SHA1的效用与MD5一样。CA证书问题是一种碰撞攻击,同样,在未来几年里,人们将能够在SHA1 CA证书上运行相同的攻击。攻击取决于恶意方创建一个EVIL和一个GOOD证书。MD5上没有已知的原像攻击,并且有碰撞攻击并不会使原像攻击更或更少可能。 - Sam Saffron
这与用哪种哈希算法处理密码的问题相比较,更重要的是进行了什么哈希运算。如果你的盐值已知,则你的数据库会立即容易受到字典攻击;如果你的盐值是程序生成的,而且文件系统被入侵了,那么你(再次)就会面临危险;如果你忽略了使用盐值,你也将遭受攻击。无论如何,存在安全性问题的核心在于哈希运算的内容。至于证书方面,我不会涉及,因为作为程序员我没有处理过它们(例如,创建、理解等)。 - Robert K
在哈希的上下文中,“broken”一词具有特定的含义,而这个答案强调的不是这个含义。这个答案所做的只会导致混淆。 - Joel McBeth
1
这是一个非常好的答案,因为它专注于实用性。哈希不仅用于安全方面(例如为非敏感数据生成缓存查找键或确定序列化对象是否已更改),而且有针对性攻击的可能性几乎为零(永远不要说从来没有),即使攻击成功,也不会产生任何实质性影响。非常好地关注了实际(而不是理论)影响。 - DVK

36

更新:

时代已经变了,我们有了SHA3的胜者。我建议使用Keccak(又名SHA3)作为SHA3竞赛的获胜者。

原始答案:

从弱到强的顺序,我会说:

  1. RIPEMD已被破解,不应再使用,可以在这个pdf中看到
  2. MD-5已被破解,不应再使用,用笔记本电脑2分钟即可破解
  3. SHA-1已被破解,不应再使用,已被攻破,攻击技术正在逐渐提高
  4. SHA-2较弱,可能在未来几年内被破解。已发现一些漏洞。请注意,通常情况下,密钥长度越大,哈希函数越难破解。虽然“密钥长度=强度”并不总是成立,但大多数情况下是成立的。因此,SHA-256可能比SHA-512更弱。
  5. Skein没有已知漏洞,是SHA-3的候选者。它相对较新,因此未经过测试。已实现多种语言。
  6. MD6没有已知漏洞,是SHA-3的另一个候选者。可能比Skein更强,但在单核机器上速度较慢。与Skein一样,它也未经过测试。一些注重安全的开发人员正在使用它,在关键任务中使用。

个人而言,我会使用MD6,因为你永远不能太谨慎。如果速度是真正的问题,我会考虑Skein或SHA-256。


6
我不会将Skein和MD6排在列表的最高位置; SHA-3竞赛直到2012年底才结束是有原因的。确信一个哈希函数实际上可能是安全的需要很长时间和很多人眼睛的共同确认,而这两个函数都还没有存在足够长的时间。 - Eric Burnett
我同意你的观点,但我认为社区处于一个奇怪的位置。所有正在使用的哈希函数都非常接近被破解(也许不包括SHA2 256-512),然而我们必须等到2012年才能选择替代品。选择你的毒药: 弱/易破解 还是 未经测试(大多数NIST候选人公开时间不超过6个月)?这是一个艰难的选择。 - Ethan Heilman
6
RIPEMD 已经被破解,但 RIPEMD-128/160/256 是不同的,并且没有被攻破。 - Bwooce
我不知道有任何.NET的Skein高效实现。我找到了SkeinFish和nskein,但都非常慢。 - Cocowalla
@Cocowalla 我想在有人宣布获胜者之前,我们不太可能看到对所有参赛者的全面支持。 - Ethan Heilman
1
我建议在实际标准出现之前先等待使用SHA-3,至少如果您想要真正遵循标准的话。该算法本身有太多选项。 - Paŭlo Ebermann

4

建议查看BLAKE2算法。

据描述,它比MD5更快,至少与SHA-3一样安全。此外,多个软件应用程序都已实现该算法,包括WinRar。


除非许多实现具有硬件支持,否则它可能会更快,这使得SHA-256相当快。 - zaph
2
我同意。截至2019年,Blake2b是迄今为止最好的通用哈希函数。比所有其他替代方案都快得多,而且在安全性方面并不逊色(至少没有任何实质性的不足),并且只需要336字节的RAM(blake2s仅需168字节),哦,它还针对小端CPU进行了优化,这是当今系统中占主导地位的字节序。 - hanshenrik

4

在保护MD5方面,目前没有已知的方法可以生成具有任意MD5哈希值的文件。原始作者必须提前计划好具有工作冲突的文件。因此,如果接收者信任发送者,则MD5是可靠的。如果签名者是恶意的,则MD5可能被破解,但尚不知道它是否容易受到中间人攻击的威胁。


1
虽然我在这个领域并不是专家,但现在通过暴力破解计算任意MD5哈希值是不是已经非常接近了呢? - mafu
@mafu:回复晚了,但是通过暴力破解计算任何哈希值都是可能的。只是可能需要很长时间。 - Warty
@ItzWarty,我特别指的是所需时间 - 由于MD5相当短,我认为可能只需要在其上投入合理的计算资源(E3或几台带有几张图形卡的廉价计算机网格等),就能够在几天内计算出任意MD5哈希值。 - mafu
@mafu 针对预影像的攻击需要进行2^127次哈希运算才能成功,但这是远非可行的。2^80次哈希运算已经是可行的,但成本已经十分昂贵。 - CodesInChaos

2

你使用哪个取决于你的使用目的。如果你只是想确保文件在传输过程中不会损坏,并且对安全性不是很关心,那么选择快速和小巧的选项。如果你需要数字签名来进行数十亿美元的联邦救助协议,并确保它们不被伪造,那么选择难以欺骗和慢速的选项。


1
很多时候在讨论解决问题的方案时,我会提到我使用 MD5 进行快速识别(对字符串进行哈希),但是他们说“但是 md5 被攻破了... 别用它,用 sha1”... 我不太赞同这种观点,想知道一些较弱的哈希算法是否有根本性的问题,应该避免使用... 例如,正常数据产生碰撞的实际情况。 - Sam Saffron
考虑到MD5在过去15年中为数百万人提供了良好的服务,如果哈希安全性不是至关重要的话,我认为它对您来说是可以接受的。 - mqp
2
@sambo,除了在实际安全/完整性取决于防止碰撞的情况下,MD5几乎适用于任何情况。 - Rex M

2

在md5被彻底破解之前,我想插一嘴说,尽管它对于许多加密来说已经不安全了,但我仍然广泛使用md5。

只要您不关心防止碰撞(您仍然可以在hmac中使用md5),并且您需要速度(有时您需要一个更慢的哈希),那么您仍然可以放心地使用md5。


@Mike 我同意你的看法,这也是我提出这个问题的初衷,弱哈希函数是否存在根本性缺陷,从而不应该使用。 - Sam Saffron
另外,如果数据或所需数据安全性的寿命短于破解期限(如果我没记错的话,现在只需要几分钟),那么MD5是绝对可行的。这在特定情况下非常有用,仍然很有用。 - annakata
@annakata - 请记住,在这种情况下,您还必须避免在多个消息中重复使用密钥,以使其可用。 - Steve Westbrook

0

我不是这方面的专家,但我一直关注安全社区,很多人认为md5哈希已经被破解了。我认为使用哪种哈希取决于数据的敏感程度和具体应用程序。只要密钥足够好且强大,您可能可以使用稍微不那么安全的哈希。


2
哈希函数通常不使用键。 - Ethan Heilman

0

以下是我的建议:

  1. 如果你预计会遭受攻击,最好忘记使用MD5。网络上有许多彩虹表可供使用,而像RIAA这样的公司已经能够生成具有等效哈希值的序列。
  2. 如果可以的话,请使用。在消息中包含消息长度可以使得制作有用的哈希碰撞变得非常困难。
  3. 通常来说,更多的位数意味着更少的碰撞(根据鸽巢原理),速度较慢,但可能更安全(除非你是一个能够找到漏洞的数学天才)。

请参阅此处的一篇论文,详细介绍了如何使用桌面Intel P4计算机在31秒内创建md5碰撞。

http://eprint.iacr.org/2006/105


这条评论已经很老了,似乎被埋没了,但是其中的一句话 - RIAA已知能够生成具有等效哈希值的序列 - 引起了我的注意,我非常好奇这是什么背景。特别是,8年前暴力破解MD5并不像2017年那样简单,所以他们肯定有一个相当充分的理由。 - i336_

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接