【问题标题】:Generating an XML document hash in C#在 C# 中生成 XML 文档哈希
【发布时间】:2018-09-14 01:17:37
【问题描述】:

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

例如,我会散列根的子节点并将散列存储为根的属性:

<RootNode Hash="abc123">
    <!-- Content to hash here -->
</RootNode>

【问题讨论】:

  • 空格如何在您想要的散列中发挥作用?
  • 我对此持怀疑态度——一方面,我只关心数据,而不关心格式。另一方面,识别任何更改可能有助于检查是否有人在玩文件。

标签: c# xml hash canonicalization


【解决方案1】:

.NET 有实现XML digital signature specclasses。签名可以添加到原始 XML 文档中(即“封装签名”),或单独存储/传输。

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

【讨论】:

  • 我喜欢这个解决方案,因为正如您所指出的,它已经实现并且是一个标准。
【解决方案2】:

您可以使用密码学名称空间:

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 创建一个散列。

【讨论】:

  • 另请参阅 System.Security.Cryptography.MD5、System.Security.Cryptography.SHA1、System.Security.Cryptography.SHA256 等,并在此处查看比较:en.wikipedia.org/wiki/Cryptographic_hash_function
  • Encoding.Default 是操作系统当前 ANSI 代码页的编码。因此,您的代码将根据区域和语言选项 - 高级选项卡中的设置给出不同的结果。
  • wcoenen 有一个非常公平的观点。使用 Encoding.ASCII 或 Encoding..
【解决方案3】:

添加对 System.Security 的 .NET 引用,并使用 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;
}

【讨论】:

    【解决方案4】:

    我最近不得不为工作中的部分 XML 文档实现哈希“校验和”(我们使用 XElement)。基本性能测试表明,与不使用查找表相比,使用查找表创建十六进制字符串哈希时,我的机器上的运行时速度提高了约 3 倍。

    这是我的实现:

    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://stackoverflow.com/a/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();
        }
    }
    

    这里有几个单元测试(使用 NUnit 框架):

    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));
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-10-15
      • 2016-08-13
      • 1970-01-01
      • 2017-09-20
      • 1970-01-01
      • 2020-12-21
      • 1970-01-01
      • 2020-11-30
      • 1970-01-01
      相关资源
      最近更新 更多