0

I have a simple question. I need to rewrite a sha256 checksum method from java to C#

So I have this Java cod to work with :

Canonicalizer c14Canonicalizer = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_EXCL_WITH_COMMENTS);
byte[] byteArray = c14Canonicalizer.canonicalizeSubtree(doc);

// At this point, the byteArray in Java and the data in C# matches up.
// That is, after the java bytes are converted to unsigned bytes using
// java.lang.Byte.toUnsignedInt()

MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(byteArray);
byte byteData[] = md.digest();

(byteArray is, you guessed it, a byte array :D)

From what I can find, the update() and digest() method should basicly be substitutet for the TransformBlock() and TransformFinalBlock() methods in the respective HashAlgorithm derived class (in this case SHA256).

So I've tried with something similar to this in C#:

var data = Encoding.UTF8.GetBytes(xmlString);

// At this point, the byteArray in Java and the data in C# matches up.
// That is, after the java bytes are converted to unsigned bytes using
// java.lang.Byte.toUnsignedInt()

using (var sha256 = SHA256.Create())
{
    byte[] shaBytes = new byte[data.Length];
    data.CopyTo(shaBytes, 0);

    sha256.TransformBlock(shaBytes, 0, shaBytes.Length, shaBytes, 0);

    sha256.TransformFinalBlock(shaBytes, 0, shaBytes.Length);
    return sha256.Hash;
}

(and again, data is a byte array)

However, the bytes do not match up. Am I missing something here?

(well of course I am, otherwise it would work, right? :S )

UPDATE

To give you some more info to go on, I have matched the bytes between the Java and C# code before running the code you see above. And then they do match. However, the bytes in the C# code comes from a UTF8-Encoded string while the Java bytes comes from a c14Canonicalizer.canonicalizeSubtree() method.

I'll update the above code examples to include their origins.

UPDATE

For what it's worth, the Java md.digest() method returns the following bytes:

-86, 44, 95, 84, 3, 50, 7, -119, -36, 46, 39, 32, -120, 7, 10, -86, -101, 110, -93, -72, -13, -93, -42, 111, 0, 59, -85, -63, -15, -98, -17, -52

when converted that translates to

170,44,95,84,3,50,7,137,220,46,39,32,136,7,10,170,155,110,163,184,243,163,214,111,0,59,171,193,241,158,239,204

while the C# code returns

72,108,14,47,15,200,209,10,68,87,17,220,67,226,162,123,69,186,130,167,239,250,180,178,75,101,39,195,32,171,156,178

when using sha256.ComputeHash()

Shazi
  • 1,490
  • 1
  • 10
  • 22
  • Please fallow this question: http://stackoverflow.com/questions/1521249/generating-an-xml-document-hash-in-c-sharp – mkysoft Sep 15 '16 at 19:47
  • 1
    What does an independent tool (read: guaranteed working) like `sha256sum` print? – rustyx Sep 15 '16 at 20:01
  • @RustyX I've tried on http://onlinemd5.com, and the result is equal to the result from the Java code. – Shazi Sep 15 '16 at 20:20
  • @mkysoft I have looked into the question, and what they are suggesting is what I am doing here. The xml in the C# code comes from a call to `XmlDsigExcC14NWithCommentsTransform.GetOutput()` and the bytes from that result is a match with the bytes from javas `Canonicalizer.canonicalizeSubtree()` – Shazi Sep 15 '16 at 21:03

3 Answers3

0

Did you try the ComputeHash method ?

i.e :

var byteArray = Encoding.ASCII.GetBytes("hello");
var sha = SHA256.Create();
byte[] outputBytes = sha.ComputeHash(byteArray);
var result = BitConverter.ToString(outputBytes).Replace("-", "").ToLower();

EDIT

Can you try this ?

XmlDocument doc = new XmlDocument();
doc.LoadXml("xmlString");
XmlDsigExcC14NWithCommentsTransform c14n = new XmlDsigExcC14NWithCommentsTransform();
c14n.LoadInnerXml(doc.ChildNodes);
Stream s = (Stream)c14n.GetOutput(typeof(Stream));
var sha = SHA256.Create();
byte[] outputBytes = sha.ComputeHash(s);
Quentin Roger
  • 6,410
  • 2
  • 23
  • 36
  • Yes sir, I have, to no avail. – Shazi Sep 15 '16 at 19:35
  • Can you share the result of md.digest(); function ? – Quentin Roger Sep 15 '16 at 19:41
  • 1
    @QuentinRoger Shazi calcalate hash for xml document with ALGO_ID_C14N_EXCL_WITH_COMMENTS algorithm. You can not calculate same has with ComputeHash. We need to use Cryptography. Check this question: http://stackoverflow.com/questions/1521249/generating-an-xml-document-hash-in-c-sharp – mkysoft Sep 15 '16 at 19:46
  • @mkysoft Thanks for the tip, I'll look into it. – Shazi Sep 15 '16 at 19:51
  • @Quentin Roger Tried your code. However it throws a nullreferenceexception on `c14n.LoadInnerXml(doc.ChildNodes);` However if I change `c14n.LoadInnerXml(doc.ChildNodes); ` to `c14n.LoadInput(doc);` then it works, although i get the wrong result. – Shazi Sep 15 '16 at 20:38
0

I found the issue. The problem was the characters used for linebreaks in the xml-string. in my xml \r\n is used for linebreaks, what needed to be done was to change it to \n which seems to be what java uses.

I found the answer here where Gerben Rampaart had noticed the same thing on different online sha256-calculators and ken2k knew what the difference was

Once I had done that SHA256.TransformFinalBlock()worked like a charm.

The final solution looks something like this:

public byte[] GetDocumentHash(XmlDocument doc)
{
    string formattedXml;
    Transform canonicalTransform = new XmlDsigExcC14NWithCommentsTransform();
    canonicalTransform.LoadInput(doc);

    using (Stream canonicalStream = (Stream)canonicalTransform.GetOutput(typeof(Stream)))
    using (var stringWriter = new EncodingStringWriter(Encoding.UTF8))
    using (var xmlTextWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { NewLineChars = "\n", CloseOutput = false, Encoding = Encoding.UTF8, Indent = true, OmitXmlDeclaration = true }))
    {
        XmlDocument newDoc = new XmlDocument();
        newDoc.Load(canonicalStream);
        newDoc.WriteTo(xmlTextWriter);
        xmlTextWriter.Flush();
        formattedXml = stringWriter.GetStringBuilder().ToString();
    }

    byte[] bytesToCalculate = Encoding.UTF8.GetBytes(formattedXml);

    using (var sha256 = SHA256.Create())
    {
        byte[] shaBytes = new byte[bytesToCalculate.Length];
        bytesToCalculate.CopyTo(shaBytes, 0);

        sha256.TransformFinalBlock(shaBytes, 0, shaBytes.Length);
        return sha256.Hash;
    }
}

There's probably a lot of refactoring and refining needed, but it gets the job done.

A big thank you to all of you who helped me!

Community
  • 1
  • 1
Shazi
  • 1,490
  • 1
  • 10
  • 22
0

Below sample may be giving same result. Because you are making same operation long way. In your code, you are getting cleaned xml from XmlDsigExcC14NWithCommentsTransform then calculate hash. Below example calculate directly.

XmlDocument doc = new XmlDocument();
doc.LoadXml("<a><xmlString>mkysoft</xmlString></a>");
XmlDsigExcC14NWithCommentsTransform c14n = new XmlDsigExcC14NWithCommentsTransform();
c14n.LoadInput(doc);
var digest = c14n.GetDigestedOutput(SHA256.Create());
mkysoft
  • 5,392
  • 1
  • 21
  • 30
  • I tried that approach actually, but it will still interpret new lines as \r\n which will give a different digest than if you change it to \n yourself. Hence why i need the xml to go through the XmlWriter before getting the digest – Shazi Sep 16 '16 at 10:28