使用 CryptoStream 重用实例时 HMACSHA512 不同的哈希值

HMACSHA512 different hashes when reuse instance with CryptoStream

我有一个用例,我想在第一个 HMAC 和一些数据组合的地方创建第二个 HMAC。

我想了解为什么测试用例 3 正在创建一个 错误的 hmac, 这令人困惑,因为 1 和 2 正在创建相同的哈希值。

代码:

void Main()
{
    { // 0. Create a HMAC 
        byte[] keyHmac = new byte[] { 255 };
        var hmac = new HMACSHA512(keyHmac); // 1st HMACSHA512 ctor
        var result = hmac.ComputeHash(new byte[] { 1, 2, 3 });
        Console.Out.WriteLine($"0. - {Convert.ToBase64String(result)}");
    }

    Console.Out.WriteLine("\r\nThe following MACs should be all equal!\r\n");

    { // 1. Create a HMAC than add a byte to the result and create a second HMAC
        byte[] keyHmac = new byte[] { 255 };
        var hmac = new HMACSHA512(keyHmac); // 1st HMACSHA512 ctor
        var result = hmac.ComputeHash(new byte[] { 1, 2, 3 });
        result = hmac.ComputeHash(result.Concat(new byte[] { 7 }).ToArray());
        Console.Out.WriteLine($"1. - {Convert.ToBase64String(result)}");
    }

    { // 2. Create a HMAC than add a byte to the result and create a second HMAC with a new instance
        byte[] keyHmac = new byte[] { 255 };
        var hmac = new HMACSHA512(keyHmac);  // 1st HMACSHA512 ctor
        var result = hmac.ComputeHash(new byte[] { 1, 2, 3 });
        hmac = new HMACSHA512(keyHmac);  // 2nd HMACSHA512 ctor
        result = hmac.ComputeHash(result.Concat(new byte[] { 7 }).ToArray());
        Console.Out.WriteLine($"2. - {Convert.ToBase64String(result)}");
    }

    { // 3. Create a HMAC from a stream than add a byte to the result and create a second HMAC
        byte[] keyHmac = new byte[] { 255 };
        var hmac = new HMACSHA512(keyHmac);
        using (var resultStream = new MemoryStream())
        {
            using (var hmacStream = new CryptoStream(resultStream, hmac, CryptoStreamMode.Write))
            {
                new MemoryStream(new byte[] { 1, 2, 3 }).CopyTo(hmacStream);
            }
        }

        var result = hmac.Hash;
        result = hmac.ComputeHash(result.Concat(new byte[] { 7 }).ToArray());

        Console.Out.WriteLine($"3. - {Convert.ToBase64String(result)}");
    }


    { // 4. Create a HMAC from a stream than add a byte to the result and create a second HMAC with a new instance
        byte[] keyHmac = new byte[] { 255 };
        var hmac = new HMACSHA512(keyHmac);
        using (var resultStream = new MemoryStream())
        {
            using (var hmacStream = new CryptoStream(resultStream, hmac, CryptoStreamMode.Write))
            {
                new MemoryStream(new byte[] { 1, 2, 3 }).CopyTo(hmacStream);
            }
        }

        var result = hmac.Hash;
        hmac = new HMACSHA512(keyHmac);
        result = hmac.ComputeHash(result.Concat(new byte[] { 7 }).ToArray());

        Console.Out.WriteLine($"4. - {Convert.ToBase64String(result)}");
    }
}

输出:

0. - J0x6KRHzGh1nTLL+a+pL8H9PJyl1b9/rL7D0j3S1DBpMduct37uMi0mBFEOdkfrLs2Ipn39yoV6GaRoEK+hU7A==

The following MACs should be all equal!

1. - mVd7YQ7AbmRfH57AprAuU1vlSuOucvg+NbUFl7eNurPuvGS/Xrko2Kz3d9vUGXr0P287dOgEKQJDNfMkN2xi5Q==
2. - mVd7YQ7AbmRfH57AprAuU1vlSuOucvg+NbUFl7eNurPuvGS/Xrko2Kz3d9vUGXr0P287dOgEKQJDNfMkN2xi5Q==
3. - DVncxk/dEYhmmpK5qEnVg0Pc0/MUe8APbAiyZrh+ba35oGv2TGCkFco3gFVZ2gl+h3DpcqP7VbmuthBmCvSKlg==
4. - mVd7YQ7AbmRfH57AprAuU1vlSuOucvg+NbUFl7eNurPuvGS/Xrko2Kz3d9vUGXr0P287dOgEKQJDNfMkN2xi5Q==

那是因为在除 3 之外的所有方法中,您在第一次哈希计算和第二次哈希计算之间重新初始化 hmac。要在 3 中做同样的事情,试试这个:

{
    // 3. Create a HMAC from a stream than add a byte to the result and create a second HMAC
    byte[] keyHmac = new byte[] {255};
    var hmac = new HMACSHA512(keyHmac);
    using (var resultStream = new MemoryStream()) {
       using (var hmacStream = new CryptoStream(resultStream, hmac, CryptoStreamMode.Write)) {
           new MemoryStream(new byte[] {1, 2, 3}).CopyTo(hmacStream);
        }
    }

    var result = hmac.Hash;
    hmac = new HMACSHA512(keyHmac);
    result = hmac.ComputeHash(result.Concat(new byte[] {7}).ToArray());

    Console.Out.WriteLine($"{Convert.ToBase64String(result)}");
}

在第 3 种情况下,您混合调用 TransformBlock/TransformFinalBlock(由 CryptoStream 使用)和调用 ComputeHash。这两种方法似乎都干扰了对方所拥有的状态机的假设。

使用一种或另一种方法都很好,但将它们混合使用不会太热。

如果您使用的不是 CryptoStream,而是 HashAlgorithm.ComputeHash(Stream) 您的第三种情况应该有效。