C# C语言中的哈希摘要/数组比较

C# C语言中的哈希摘要/数组比较,c#,security,timing,string-comparison,C#,Security,Timing,String Comparison,我正在编写一个需要验证HMAC-SHA256校验和的应用程序。我目前拥有的代码如下所示: static bool VerifyIntegrity(string secret, string checksum, string data) { // Verify HMAC-SHA256 Checksum byte[] key = System.Text.Encoding.UTF8.GetBytes(secret); byte[] val

我正在编写一个需要验证HMAC-SHA256校验和的应用程序。我目前拥有的代码如下所示:

    static bool VerifyIntegrity(string secret, string checksum, string data)
    {
        // Verify HMAC-SHA256 Checksum
        byte[] key = System.Text.Encoding.UTF8.GetBytes(secret);
        byte[] value = System.Text.Encoding.UTF8.GetBytes(data);
        byte[] checksum_bytes = System.Text.Encoding.UTF8.GetBytes(checksum);
        using (var hmac = new HMACSHA256(key))
        {
            byte[] expected_bytes = hmac.ComputeHash(value);
            return checksum_bytes.SequenceEqual(expected_bytes);
        }
    }
public static bool CompareArraysExhaustively(byte[] first, byte[] second)
{
    if (first.Length != second.Length)
    {
        return false;
    }
    bool ret = true;
    for (int i = 0; i < first.Length; i++)
    {
        ret = ret & (first[i] == second[i]);
    }
    return ret;
}
checksum_bytes.Zip( expected_bytes, (a,b) => a == b ).Aggregate( true, (a,r) => a && r );
我知道这很容易受到影响


标准库中是否有消息摘要比较功能?我意识到我可以编写自己的时间硬化比较方法,但我必须相信这已经在其他地方实现了。

它如何容易受到定时攻击?在有效或无效摘要的情况下,代码的工作时间相同。计算摘要/检查摘要看起来是检查这一点的最简单方法。

它如何容易受到定时攻击?在有效或无效摘要的情况下,代码的工作时间相同。计算摘要/检查摘要看起来是检查这一点的最简单方法。

编辑:原始答案如下-在我看来仍然值得一读,但关于计时攻击

您引用的页面提供了一些关于编译器优化的有趣观点。假设校验和的大小不是特别机密,您知道两个字节数组的长度相同,那么如果长度不同,您可以立即返回,您可以尝试以下方法:

    static bool VerifyIntegrity(string secret, string checksum, string data)
    {
        // Verify HMAC-SHA256 Checksum
        byte[] key = System.Text.Encoding.UTF8.GetBytes(secret);
        byte[] value = System.Text.Encoding.UTF8.GetBytes(data);
        byte[] checksum_bytes = System.Text.Encoding.UTF8.GetBytes(checksum);
        using (var hmac = new HMACSHA256(key))
        {
            byte[] expected_bytes = hmac.ComputeHash(value);
            return checksum_bytes.SequenceEqual(expected_bytes);
        }
    }
public static bool CompareArraysExhaustively(byte[] first, byte[] second)
{
    if (first.Length != second.Length)
    {
        return false;
    }
    bool ret = true;
    for (int i = 0; i < first.Length; i++)
    {
        ret = ret & (first[i] == second[i]);
    }
    return ret;
}
checksum_bytes.Zip( expected_bytes, (a,b) => a == b ).Aggregate( true, (a,r) => a && r );
另一方面,您可以对实际哈希进行Base64编码,而不是在字节数组上使用SequenceEqual,并查看其是否匹配:

static bool VerifyIntegrity(string secret, string checksum, string data)
{
    // Verify HMAC-SHA256 Checksum
    byte[] key = Encoding.UTF8.GetBytes(secret);
    byte[] value = Encoding.UTF8.GetBytes(data);
    using (var hmac = new HMACSHA256(key))
    {
        return checksum == Convert.ToBase64String(hmac.ComputeHash(value));
    }
}

我不知道在这个框架内还有什么更好的。为数组或常规ICollection实现编写一个专门的SequenceEqual运算符并不难,因为它首先检查长度是否相等。。。但是考虑到散列的长度很短,我就不担心了。

编辑:原始答案如下-在我看来仍然值得一读,但是关于定时攻击

您引用的页面提供了一些关于编译器优化的有趣观点。假设校验和的大小不是特别机密,您知道两个字节数组的长度相同,那么如果长度不同,您可以立即返回,您可以尝试以下方法:

    static bool VerifyIntegrity(string secret, string checksum, string data)
    {
        // Verify HMAC-SHA256 Checksum
        byte[] key = System.Text.Encoding.UTF8.GetBytes(secret);
        byte[] value = System.Text.Encoding.UTF8.GetBytes(data);
        byte[] checksum_bytes = System.Text.Encoding.UTF8.GetBytes(checksum);
        using (var hmac = new HMACSHA256(key))
        {
            byte[] expected_bytes = hmac.ComputeHash(value);
            return checksum_bytes.SequenceEqual(expected_bytes);
        }
    }
public static bool CompareArraysExhaustively(byte[] first, byte[] second)
{
    if (first.Length != second.Length)
    {
        return false;
    }
    bool ret = true;
    for (int i = 0; i < first.Length; i++)
    {
        ret = ret & (first[i] == second[i]);
    }
    return ret;
}
checksum_bytes.Zip( expected_bytes, (a,b) => a == b ).Aggregate( true, (a,r) => a && r );
另一方面,您可以对实际哈希进行Base64编码,而不是在字节数组上使用SequenceEqual,并查看其是否匹配:

static bool VerifyIntegrity(string secret, string checksum, string data)
{
    // Verify HMAC-SHA256 Checksum
    byte[] key = Encoding.UTF8.GetBytes(secret);
    byte[] value = Encoding.UTF8.GetBytes(data);
    using (var hmac = new HMACSHA256(key))
    {
        return checksum == Convert.ToBase64String(hmac.ComputeHash(value));
    }
}

我不知道在这个框架内还有什么更好的。为数组或常规ICollection实现编写一个专门的SequenceEqual运算符并不难,因为它首先检查长度是否相等。。。但是考虑到散列很短,我就不担心了。

如果你担心SequenceEqual的时间,你可以用这样的东西来代替它:

    static bool VerifyIntegrity(string secret, string checksum, string data)
    {
        // Verify HMAC-SHA256 Checksum
        byte[] key = System.Text.Encoding.UTF8.GetBytes(secret);
        byte[] value = System.Text.Encoding.UTF8.GetBytes(data);
        byte[] checksum_bytes = System.Text.Encoding.UTF8.GetBytes(checksum);
        using (var hmac = new HMACSHA256(key))
        {
            byte[] expected_bytes = hmac.ComputeHash(value);
            return checksum_bytes.SequenceEqual(expected_bytes);
        }
    }
public static bool CompareArraysExhaustively(byte[] first, byte[] second)
{
    if (first.Length != second.Length)
    {
        return false;
    }
    bool ret = true;
    for (int i = 0; i < first.Length; i++)
    {
        ret = ret & (first[i] == second[i]);
    }
    return ret;
}
checksum_bytes.Zip( expected_bytes, (a,b) => a == b ).Aggregate( true, (a,r) => a && r );

这将返回与SequenceEquals相同的结果,但在给出答案之前始终检查每个元素,这将减少通过计时攻击暴露任何内容的机会。

如果您担心SequenceEquals的计时,您可以始终使用以下内容替换它:

    static bool VerifyIntegrity(string secret, string checksum, string data)
    {
        // Verify HMAC-SHA256 Checksum
        byte[] key = System.Text.Encoding.UTF8.GetBytes(secret);
        byte[] value = System.Text.Encoding.UTF8.GetBytes(data);
        byte[] checksum_bytes = System.Text.Encoding.UTF8.GetBytes(checksum);
        using (var hmac = new HMACSHA256(key))
        {
            byte[] expected_bytes = hmac.ComputeHash(value);
            return checksum_bytes.SequenceEqual(expected_bytes);
        }
    }
public static bool CompareArraysExhaustively(byte[] first, byte[] second)
{
    if (first.Length != second.Length)
    {
        return false;
    }
    bool ret = true;
    for (int i = 0; i < first.Length; i++)
    {
        ret = ret & (first[i] == second[i]);
    }
    return ret;
}
checksum_bytes.Zip( expected_bytes, (a,b) => a == b ).Aggregate( true, (a,r) => a && r );

这会返回与SequenceEquals相同的结果,但在给出答案之前总是检查每个元素,这会减少通过计时攻击泄露任何信息的机会。

我认为他的问题是,如果差异在哈希中出现的较早,SequenceEqual返回false的速度会更快,如果差异出现的较晚,则返回false的时间会更长。好的,他可以把它改成i=0;iome基本评测,在Windows 7/Server 2008上,ComparararLayShausively性能完美。@Jon,假设一旦ret变为false,它将保持false,那么使用int i=0会不会更快;i<1.长度;i++{if first[i]!=second[i]返回false;}返回true;或者由此产生的不稳定的计时会更有害吗?@Paul:关键是不要让它更快。阅读问题链接到的页面。