3

从下面列出的链接中阅读文章后:

http://news.ycombinator.com/item?id=910203

我现在正试图证明和理解为什么下面列出的哈希是不安全的,程序员不应该练习。

H(k || m) --> SHA1("密钥" + "name=bob,withdraw=$200")

H(m || k) --> SHA1("name=bob,withdraw=$200" + "secret-key")

正如文章所述,第一个示例完全被致命地破坏了。SHA1(以及 MD5 和许多其他哈希)是共享一种称为 Merkle-Damgaard 的通用设计的机器,这意味着它们以块长度的块处理消息,并使用这些块来置换内部状态。输出 SHA1 是该状态的“最终”内容。但是没有什么可以真正“确定” SHA1 状态。如果您在电线上看到 SHA1 值,您可以继续使用附加数据启动 Merkle-Damgaard 机器。这意味着您可以使用附加到末尾的任意数据来铸造新消息,这些数据看起来是真实的。这种攻击非常容易实施;它需要大约 20 行 Ruby 代码。

第二个例子也坏了,它是这篇博文的主题。如果你在消息之后加上密钥,你就不能继续用数据驱动散列,因为你无法猜测的秘密就在它的末尾。

我在 C# 中编写了一个简单的哈希函数,试图证明作者声称的内容,但不知何故,无论我添加/填充或在消息的后面/前面添加什么,我似乎都无法做到这一点。

        string strRet;
        // hash contains the SHA 1 value of SHA1 (key + temp)
        string hash = "ce0037fbbff7a1b68b5794bd73dcc7d63338f115";

        try
        {
            string key = "password";
            string temp = "name=bob,withdraw=$200";

            for (int i = 0; i < 1000; i++)
            {
                byte[] buffer = Encoding.ASCII.GetBytes(temp);
                SHA1CryptoServiceProvider cryptoTransformSHA1 = new SHA1CryptoServiceProvider();
                strRet = BitConverter.ToString(cryptoTransformSHA1.ComputeHash(buffer)).Replace("-", "");
                strRet = strRet.ToLower();
                MessageBox.Show(strRet);

                if (strRet.Equals(hash))
                {
                    MessageBox.Show("Hash are equal !");
                    MessageBox.Show(temp);
                }

                temp = key + temp + "2";
            }

            MessageBox.Show("The End !");

        }
        catch (Exception)
        {
            MessageBox.Show("There is a Error !");
        }

有人可以通过提供一个指定的示例来指导我,我可以散列、理解和证明作者在文章中对这两种散列方法的主张。提前感谢您提供的任何帮助。

4

2 回答 2

12

让我们退后一步。首先,我们的意思是H(k|m)什么?这是为了什么?

这里的目标如下。Alice 和 Bob 共享一个密钥。他们如何分享它,我们不知道。不知何故,爱丽丝和鲍勃商定了一个秘密密钥,而其他人都不知道。

Alice 希望向 Bob 发送一条消息。Alice 不关心是否有人可以阅读该消息,但 Alice 非常关心 Bob 知道 Alice 写了该消息。

他们提出了以下方案。Alice 将创建一条消息,该消息由密钥和消息的其余部分组成。然后她会对整个事情进行哈希处理。然后,她将不带密钥的消息连同散列一起发送给 Bob。

Bob 尝试验证消息是否来自 Alice。他将密钥放在消息前面并对其进行哈希处理。如果 Bob 得到相同的哈希值,那么 Bob 就知道发送消息的人拥有密钥。他知道那不是他,所以一定是爱丽丝。

这种方案并不安全。马洛里希望向鲍勃发送一条虚假信息,并欺骗他认为这是来自爱丽丝的。

有一天,Alice 拿出她的秘密密钥“123SesameStreet”和一条消息“亲爱的 Bob,我爱你!”,她将它们一起附加到“123SesameStreet,亲爱的 Bob,我爱你!” 她将其散列到“398942358092”并发送散列和消息“亲爱的鲍勃,我爱你!” 给鲍勃。

马洛里在消息到达鲍勃之前拦截了它。Mallory 不知道密钥,但她知道消息和哈希。Mallory 将 SHA1 算法设置为状态为 398942358092,然后运行字符“开个玩笑,我恨你!”,并从 92358023934 中得到一个哈希值。现在 Mallory 发送新的哈希值和消息“亲爱的 Bob,我爱你!开玩笑的,我恨你!给鲍勃。

这工作的精确度如何?这是交易。基本上,SHA1 的工作原理就像这个过于简化的草图:

int hash = 0;
foreach(char c in message)
    hash = MakeNextHash(hash, c);

也就是说,你从零开始。然后你散列第一个字符和数字 0。然后你用第二个字符散列那个散列。这会产生一个新的哈希值。然后你用第三个字符散列,做第三个散列。继续这样做,直到你的字符用完为止;您制作的最后一个哈希是整个消息的哈希。

真正的 SHA1 算法使用大于单个字符的块和大于 int 的状态,但基本上就是这样。它一次一个块地转换一堆状态,使用前一个状态作为下一个状态的输入。

因此,如果我告诉您“这是一个字符串 M。此外,字符串 KM 具有哈希 H(K|M)。” 那么显然你可以为任何 Z 计算出 KMZ 的哈希 H(K|M|Z) ,即使你不知道 K。你只是说:

int hash = HKM;
foreach(char c in Z)
    hash = MakeNextHash(hash, c);

结果是 H(K|M|Z),即使你不知道 K。

所以,你看这是怎么回事。Bob 将密钥添加到消息中,并通过 SHA1 算法运行它,然后他得到正确的哈希值。所以他已经验证了消息来自 Alice,而实际上有一半来自 Mallory。

这就是为什么钥匙必须放在最后的原因。您必须将密钥放在消息之后,而不是之前。你会想。事实证明,虽然现在的攻击不像密钥优先方案那样微不足道,但它仍然不安全。H(m|k)方案也不好。

为什么不?

假设 Mallory 截获了一条消息 M 和一个哈希 H,即 H(M|K),其中 K 是密钥。她阻止消息到达。

Mallory 很容易计算 H(M)。困难的部分是马洛里推导出破坏性消息 N 使得 H(N) = H(M)。她是如何做到的,我们还不知道,但人们普遍认为存在这样的技术,只是我们还没有找到。

Mallory 知道 H(N|K) 与 H(M|K) 相同,推理与之前相同——因为要计算 H(N|K),我们要做:

int hash = HN;
foreach(char c in key)
    ....

计算出 H(N|K)。Mallory 不需要知道 K 来生成消息 N,使得 H(N|K) 是 H(M|K)。

因此,现在 Mallory 将 N 和 H(M|K) / H(N|K) ——它们是相同的——发送给 Bob。Bob 将 K 附加到 N,并验证消息来自 Alice,而实际上它来自 Mallory。

它变得更糟。假设 Mallory 捕获了一百万条消息 M1、M2……和一百万条在 Alice 和 Bob 之间传递的哈希 H(M1|K)、H(M2|K)……。Mallory 需要制作一条消息 N,使 H(N) 与H(M1)、H(M2)、H(M3) 中的任何一个匹配……她的工作变得轻松了一百万倍。她找到这样的消息 N 使得 H(N) 匹配,例如 H(M1234),然后将 N 和 H(M1234|K) 发送给 Bob。Bob 没有注意到他之前看到过该哈希,并认为这是来自 Alice 的消息。

它变得更糟。让我们稍微改变一下方案,看看它会变得更糟。Carol 有一条消息,她想通过 Alice 发送给 Bob。消息 M 是“嘿 Bob,这是 Carol。让你和我和 Alice 下周一起吃午饭。如果 Alice 同意,她会用她的身份验证器向你发送这条消息。” Carol 不知道密钥 K,但 Alice 知道。Alice 同意消息 M,因此她计算 H(M|K) 并将 M 和 H(M|K) 发送给 Bob。

现在马洛里想制造麻烦,所以她搜索两条消息 B(良性)和 D(危险),使得 H(B) 等于 H(D),并且 Alice 会同意 B 但不会同意使用 D。这比搜索与Alice 的给定消息匹配的消息 N 容易得多,因为现在 Mallory 可以选择两条消息。发现碰撞的工作要容易几个数量级。

Mallory 找到这两条消息并将 B 发送给 Alice。Alice 同意该消息,计算 H(B|K),并将 H(B|K) 和 B 发送给 Bob。Mallory 截获消息 B 并将其替换为 D。H(B|K) 和 H(D|K) 与之前的推理相同。Bob 收到消息 D 并验证 H(D|K) 与他发送的散列匹配,因此他知道 Alice 批准了该消息,即使她没有。

还没有人找到让 SHA1 可靠地产生这样的碰撞的方法,但几乎每个人都相信我们会解决这个问题。

故事的第一个寓意是不要使用这些技术中的任何一种作为消息验证者。第一个是微不足道的,第二个很可能会在我们的一生中被打破。

第二个道德是永远不允许潜在的攻击者选择您将使用您的密钥处理的消息。

于 2011-10-25T06:28:01.560 回答
2

您可以链接http://rdist.root.org/2009/10/29/stop-using-unsafe-keyed-hashes-use-hmac/这是此声明的来源。

它说这种散列方案比它需要的要弱,并不是说它实际上可以用 SHA-1 破解。

该方案仅在底层散列函数有任何弱点(第一次攻击的第二个原像,第二次攻击的冲突)时才容易受到攻击。据我所知,SHA-1 没有发现其中任何一个的实际漏洞,而 MD5 在第二次攻击的情况下被破坏。

由于冲突是哈希函数中最容易发现的漏洞,因此除非必要,否则最好使用不易受到冲突攻击的构造。这就是推荐使用 HMAC 的原因。

于 2011-10-25T07:29:46.040 回答