Converting SHA256 from Java to C#

主宰稳场 提交于 2019-11-30 16:20:15

问题


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()


回答1:


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



回答2:


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!




回答3:


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


来源:https://stackoverflow.com/questions/39518616/converting-sha256-from-java-to-c-sharp

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!