在C#中,对XML文档进行哈希的最佳方法是什么?我想要对XML文档进行哈希,以便我可以知道它是否与生成时手动更改。 我不是出于安全考虑使用此功能——如果有人更改XML并更改哈希以匹配,则没有问题。
例如,我会哈希根节点的子节点,并将哈希值存储为根节点的属性:
<RootNode Hash="abc123">
<!-- Content to hash here -->
</RootNode>
在C#中,对XML文档进行哈希的最佳方法是什么?我想要对XML文档进行哈希,以便我可以知道它是否与生成时手动更改。 我不是出于安全考虑使用此功能——如果有人更改XML并更改哈希以匹配,则没有问题。
例如,我会哈希根节点的子节点,并将哈希值存储为根节点的属性:
<RootNode Hash="abc123">
<!-- Content to hash here -->
</RootNode>
System.Security.Cryptography.MACTripleDES hash = new System.Security.Cryptography.MACTripleDES(Encoding.Default.GetBytes("mykey"));
string hashString = Convert.ToBase64String(hash.ComputeHash(Encoding.Default.GetBytes(myXMLString)));
你只需要使用一个密钥来创建哈希加密程序,然后使用XML的字符串表示形式来创建哈希。
添加一个.NET引用到System.Security,并使用XmlDsigC14NTransform。以下是一个例子...
/* http://www.w3.org/TR/xml-c14n
Of course is cannot detect these are the same...
<color>black</color> vs. <color>rgb(0,0,0)</color>
...because that's dependent on app logic's interpretation of XML data.
But otherwise it gets the following right...
•Normalization of whitespace in start and end tags
•Lexicographic ordering of namespace and attribute
•Empty element conversion to start-end tag pair
•Retain all whitespace between tags
And more.
*/
public static string XmlHash(XmlDocument myDoc)
{
var t = new System.Security.Cryptography.Xml.XmlDsigC14NTransform();
t.LoadInput(myDoc);
var s = (Stream)t.GetOutput(typeof(Stream));
var sha1 = SHA1.Create();
var hash = sha1.ComputeHash(s);
var base64String = Convert.ToBase64String(hash);
s.Close();
return base64String;
}
using System.Xml.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Linq;
/// <summary>
/// Provides a way to easily compute SHA256 hash strings for XML objects.
/// </summary>
public static class XMLHashUtils
{
/// <summary>
/// Precompute a hexadecimal lookup table for runtime performance gain, at the cost of memory and startup performance loss.
/// SOURCE: https://dev59.com/eHRB5IYBdhLWcg3wcm2d#18574846
/// </summary>
static readonly string[] hexLookupTable = Enumerable.Range(0, 256).Select(integer => integer.ToString("x2")).ToArray();
static readonly SHA256Managed sha256 = new SHA256Managed();
/// <summary>
/// Computes a SHA256 hash string from an XElement and its children.
/// </summary>
public static string Hash(XElement xml)
{
string xmlString = xml.ToString(SaveOptions.DisableFormatting); // Outputs XML as single line
return Hash(xmlString);
}
/// <summary>
/// Computes a SHA256 hash string from a string.
/// </summary>
static string Hash(string stringValue)
{
byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(stringValue));
return BytesToHexString(hashBytes);
}
/// <summary>
/// Converts a byte array to a hexadecimal string using a lookup table.
/// </summary>
static string BytesToHexString(byte[] bytes)
{
int length = bytes.Length;
StringBuilder sb = new StringBuilder(length * 2); // Capacity fits hash string length
for (var i = 0; i < length; i++)
{
sb.Append(hexLookupTable[bytes[i]]); // Using lookup table for faster runtime conversion
}
return sb.ToString();
}
}
using NUnit.Framework;
using System.Linq;
using System.Xml.Linq;
public class XMLHashUtilsTest
{
/// <summary>
/// Outputs XML: <root><child attribute="value" /></root>
/// where <child /> node repeats according to childCount
/// </summary>
XElement CreateXML(int childCount)
{
return new XElement("root", Enumerable.Repeat(new XElement("child", new XAttribute("attribute", "value")), childCount));
}
[Test]
public void HashIsDeterministic([Values(0,1,10)] int childCount)
{
var xml = CreateXML(childCount);
Assert.AreEqual(XMLHashUtils.Hash(xml), XMLHashUtils.Hash(xml));
}
[Test]
public void HashChanges_WhenChildrenAreDifferent([Values(0,1,10)] int childCount)
{
var xml1 = CreateXML(childCount);
var xml2 = CreateXML(childCount + 1);
Assert.AreNotEqual(XMLHashUtils.Hash(xml1), XMLHashUtils.Hash(xml2));
}
[Test]
public void HashChanges_WhenRootNameIsDifferent([Values("A","B","C")]string nameSuffix)
{
var xml1 = CreateXML(1);
var xml2 = CreateXML(1);
xml2.Name = xml2.Name + nameSuffix;
Assert.AreNotEqual(XMLHashUtils.Hash(xml1), XMLHashUtils.Hash(xml2));
}
[Test]
public void HashChanges_WhenRootAttributesAreDifferent([Values("A","B","C")]string attributeName)
{
var xml1 = CreateXML(1);
var xml2 = CreateXML(1);
xml2.Add(new XAttribute(attributeName, "value"));
Assert.AreNotEqual(XMLHashUtils.Hash(xml1), XMLHashUtils.Hash(xml2));
}
}
public static string GetHashSHA1(this byte[] data)
{
using (var sha1 = new System.Security.Cryptography.SHA256CryptoServiceProvider())
{
return string.Concat(sha1.ComputeHash(data).Select(x => x.ToString("X2")));
}
}
读取文件字节后我使用的代码是 => System.IO.File.ReadAllBytes(filePath)