在C#中生成XML文档哈希

在C#中散列XML文档的最佳方法是什么? 我想散列一个XML文档,以便我可以判断它是否从生成时手动更改。 我没有使用它来保证安全性 – 如果有人更改了XML,并且更改了哈希以匹配,那就没关系了。

例如,我会对根的子节点进行哈希并将哈希存储为根的属性:

   

.NET具有实现XML数字签名规范的类 。 签名可以添加到原始XML文档内(即“封装签名”),也可以单独存储/传输。

由于您不需要安全性,因此它可能有点过分,但它具有已经实现的优点,并且是不依赖于语言或平台的标准。

您可以使用加密名称空间:

 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的字符串reqpresentation创建散列。

添加.NET引用到System.Security,并使用XmlDsigC14NTransform。 这是一个例子……

 /* http://www.w3.org/TR/xml-c14n Of course is cannot detect these are the same... black vs. rgb(0,0,0) ...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; } 

我最近不得不在工作时为部分XML文档实现哈希“校验和”(我们使用XElement)。 最初的性能测试显示,当使用查找表创建hex字符串哈希时,我的机器上的运行时加速约为3倍。

这是我的实施:

 using System.Xml.Linq; using System.Security.Cryptography; using System.Text; using System.Linq; ///  /// Provides a way to easily compute SHA256 hash strings for XML objects. ///  public static class XMLHashUtils { ///  /// Precompute a hexadecimal lookup table for runtime performance gain, at the cost of memory and startup performance loss. /// SOURCE: https://stackoverflow.com/a/18574846 ///  static readonly string[] hexLookupTable = Enumerable.Range(0, 256).Select(integer => integer.ToString("x2")).ToArray(); static readonly SHA256Managed sha256 = new SHA256Managed(); ///  /// Computes a SHA256 hash string from an XElement and its children. ///  public static string Hash(XElement xml) { string xmlString = xml.ToString(SaveOptions.DisableFormatting); // Outputs XML as single line return Hash(xmlString); } ///  /// Computes a SHA256 hash string from a string. ///  static string Hash(string stringValue) { byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(stringValue)); return BytesToHexString(hashBytes); } ///  /// Converts a byte array to a hexadecimal string using a lookup table. ///  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(); } } 

并且inheritance了几个unit testing(使用NUnit框架):

 using NUnit.Framework; using System.Linq; using System.Xml.Linq; public class XMLHashUtilsTest { ///  /// Outputs XML:  /// where  node repeats according to childCount ///  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)); } }