문제

C#에서 XML 문서를 해싱하는 가장 좋은 방법은 무엇입니까? XML 문서를 해시하여 생성 된 시점에서 수동으로 변경되었는지 알 수 있습니다. 나는 이것을 보안에 사용하지 않습니다. 누군가 XML을 변경하고 해시를 일치하도록 변경해도 괜찮습니다.

예를 들어, 나는 루트의 어린이 노드를 해시하고 해시를 루트의 속성으로 저장합니다.

<RootNode Hash="abc123">
    <!-- Content to hash here -->
</RootNode>
도움이 되었습니까?

해결책

.NET가 있습니다 클래스 그것을 구현합니다 XML 디지털 서명 사양. 서명은 원래 XML 문서 (예 : "Enveloved Signature") 내부에 추가하거나 별도로 저장/전송 될 수 있습니다.

보안이 필요하지 않기 때문에 약간 과잉 일 수 있지만 이미 구현되고 언어 나 플랫폼에 의존하지 않는 표준이라는 이점이 있습니다.

다른 팁

암호화 이름 공간을 사용할 수 있습니다.

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에 대한 .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;
}

최근에는 직장에서 부분 XML 문서에 대한 해시 "체크섬"을 구현해야했습니다 (Xlement를 사용). 초보적인 성능 테스트는 HEX String 해시를 만들기 위해 조회 테이블을 사용할 때 내 컴퓨터에서 ~ 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));
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top