我正在编写需要验证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);
}
}
标准库中是否有消息摘要比较功能?我意识到我可以编写自己的时间强化比较方法,但我必须相信这已经在其他地方实现了。
答案 0 :(得分:2)
您引用的页面提供了一些有关编译器优化的有趣观点。鉴于您知道两个字节数组的长度相同(假设校验和的 size 并不特别保密,如果长度不同,您可以立即返回)您可能尝试< / em>这样的事情:
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;
}
现在仍然不会花费相同的时间用于所有输入 - 例如,如果两个阵列都在L1缓存中,则它可能比必须从主存储器获取更快。但是,我怀疑从安全角度来看这不太可能导致重大问题。
这可以吗?谁知道。根据两个操作数,不同的处理器和不同版本的CLR可能需要不同的时间用于&
操作。基本上这与您引用的页面的结论相同 - 它可能与我们以便携方式获得的一样好,但它需要在您尝试运行的每个平台上进行验证。
至少上述代码仅使用相对简单的操作。我个人会避免在这里使用LINQ操作,因为在某些情况下可能会进行偷偷摸摸的优化。我不认为会在这种情况下 - 或者他们很容易被击败 - 但你至少必须思考关于他们。使用上面的代码,源代码和IL之间至少存在一个相当紧密的关系 - 让“仅”JIT编译器和处理器优化需要担心:)
原始回答
这有一个重要问题:为了提供校验和,你必须有一个字符串,其UTF-8编码形式与校验和相同。有很多字节序列不能代表UTF-8编码的文本。基本上,尝试使用UTF-8将任意二进制数据编码为文本是一个坏主意。
另一方面,Base64基本上是设计的: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);
byte[] checksumBytes = Convert.FromBase64String(checksum);
using (var hmac = new HMACSHA256(key))
{
byte[] expectedBytes = hmac.ComputeHash(value);
return checksumBytes.SequenceEqual(expectedBytes);
}
}
另一方面,不是在字节数组上使用SequenceEqual,而是可以Base64 编码实际的哈希值,看看是否匹配:
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));
}
}
我不知道框架内有什么更好的东西。为数组(或通用SequenceEqual
实现)编写一个专门的ICollection<T>
运算符并不难,首先检查相等的长度...但是考虑到哈希很短,我不会担心这一点。
答案 1 :(得分:2)
如果您担心SequenceEqual的时间安排,您可以随时用以下内容替换它:
checksum_bytes.Zip( expected_bytes, (a,b) => a == b ).Aggregate( true, (a,r) => a && r );
这会返回与SequenceEquals相同的结果,但在给出答案之前总是检查每个元素,这样可以减少通过计时攻击揭示任何内容的机会。
答案 2 :(得分:0)
它如何容易受到时间攻击?在有效或无效摘要的情况下,您的代码的工作时间相同。并且计算摘要/检查摘要看起来是检查此问题的最简单方法。