问题
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