SHA1Managed.ComputeHash在不同的服务器上偶尔会有所不同

时间:2012-09-28 16:55:47

标签: c# encoding hash sha1

背景(您可以跳过此部分)

我有大量数据(大约3 MB),需要在数百台机器上保持最新。有些机器运行C#,有些运行Java。数据可能随时发生变化,需要在几分钟内传达给客户。数据以4个负载平衡服务器以Json格式提供。这4台服务器运行的是带有Mvc 3和C#4.0的ASP.NET 4.0。

在4台服务器上运行的代码具有散列算法,该算法散列Json响应,然后将散列转换为字符串。此哈希值将提供给客户端。然后,每隔几分钟,客户端使用散列ping服务器,如果散列已过期,则返回新的Json对象。如果散列仍然是当前的,则返回带有emptry主体的304。

有时,4个框生成的哈希值在框中不一致,这意味着客户端不断下载数据(每个请求可能会遇到不同的服务器)。

Code Snipet

以下是用于生成哈希的代码。

internal static HashAlgorithm Hasher { get; set; }
...
Hasher = new SHA1Managed();
...
Convert.ToBase64String(Hasher.ComputeHash(Encoding.ASCII.GetBytes(jsonString)));

为了尝试调试问题,我把它拆分成这样:

Prehash = PreHashBuilder.ToString();
ASCIIBytes = Encoding.ASCII.GetBytes(Prehash);
HashedBytes = Hasher.ComputeHash(ASCIIBytes);
Hash = Convert.ToBase64String(HashedBytes);

然后我添加了一个吐出上述值的路线,并使用Beyond Compare来比较差异。

使用以下命令将字节数组转换为BeyondCompare使用的字符串格式:

private static string GetString(byte[] bytes)
{
    StringBuilder sb = new StringBuilder();
    foreach (byte b in bytes)
    {
        sb.Append(b);
    }
    return sb.ToString();
} 

正如您所看到的,字节数组作为字节序列显示在字面上。它没有被转换为'。

问题

我发现Prehash和ASCIIBytes值相同,但HashedBytes值不同 - 这意味着Hash也不同。

我在4个服务器盒上重启了几次IIS WebSite,当它们有不同的哈希值时,比较了BeyondCompare中的值。在每一个案例中,它都是" HashedBytes"值不同(SHA1Managed.ComputeHash(...)的结果)

问题

我做错了什么? ComputeHash函数的输入是相同的。 SHA1Managed机器是否依赖?这是不可能的,因为4台机器有一半的时间具有相同的哈希值。

我搜索过StackOverFlow和Bing但是却找不到其他人遇到此问题。我能找到的最接近的是那些编码有问题的人,但我想我已经证明编码不是问题。

输出

我希望不要把所有东西都放在这里,因为它有多长时间,但这里是我正在比较的转储的狙击手:

哈希:o1ZxBaVuU6OhE6De96wJXUvmz3M =
HashedBytes:163861135165110831631611916022224717299375230207115个
ASCIIBytes:1151169710310146991111094779114100101114831011141181059910147115101114118105991014611511899591151051031101171129511510111411810599101114101102101114101110991011159598979910710111010011111410010111411510111411810599101951185095117114108611041161161125847471051159897991071011101004610910211598101115116971031014699111109477911410010111483101114118105991014711510111411810599101461151189947118505911510510311011711295115101114118105991011141011021011141011109910111595989799107101110100112971211091011101161151161111141011151011141 .... Prehash:...

当我比较不同服务器上的两个页面时,ASCII字节相同但HashedBytes不相同。我用于字节的转储方法没有转换,它只是按顺序转储每个字节。我可以用'来划分字节。'我想。

跟进 我已经对b.ToString(CultureInfo.InvariantCulture)进行了更改,并使HashAlgorithm成为局部变量而不是静态属性。我正在等待代码部署到服务器。

3 个答案:

答案 0 :(得分:10)

我一直在尝试复制该问题,但是一旦我将SHA1Managed属性设置为局部变量而不是全局静态,我就无法这样做。

问题在于多线程。我的代码是线程安全的,除了我标记为静态的SHA1Managed类。我假设SHA1Managed.ComputeHash在下面是线程安全的,但显然它不是标记为内部静态。

重复一遍,如果标记为内部静态,则SHA1Managed.ComputeHash不是线程安全的。

MSDN声明:

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

我不知道为什么内部静态的行为与公共静态不同。

我会将@pst标记为答案,并添加注释以澄清问题,但@pst发表了评论,因此我无法将其标记为答案。

感谢您的所有意见。

答案 1 :(得分:0)

您的GetString方法可能会在不同文化的计算机上产生不同的结果,因为StringBuilder.Append(byte)调用byte.ToString(CultureInfo.CurrentCulture)。尝试

private static string GetString(byte[] bytes)
{
    StringBuilder sb = new StringBuilder();
    foreach (byte b in bytes)
    {
        sb.Append(b.ToString(CultureInfo.InvariantCulture));
    }
    return sb.ToString();
} 

但是使用不使用字节值的十进制字符串表示的方法会更好。

答案 2 :(得分:0)

问题是你的代码可能会搞乱前导0,使用以下作为你的数组来比较字符串代码。它将产生可靠的结果,专门用于将字节数组转换为字符串,以便它们可以在机器之间传输。

using System.Runtime.Remoting.Metadata.W3cXsd2001;

public byte[] StringToBytes(string value)
{
    SoapHexBinary soapHexBinary = SoapHexBinary.Parse(value);
    return soapHexBinary.Value;
}

public string BytesToString(byte[] value)
{
    SoapHexBinary soapHexBinary = new SoapHexBinary(value);
    return soapHexBinary.ToString();
}

另外,我建议您检查JSON是否不同,因为这会产生完全不同的哈希值。例如,某些文化将数字“一千六百七十七”表示为1,600.71 000.7,甚至1 600,7(请参阅this Wikipedia页面)。