将 SHA256 从 Java 转换为 C#
Converting SHA256 from Java to C#
我有一个简单的问题。
我需要将 sha256 校验和方法从 java 重写为 C#
所以我有这个 Java 鳕鱼可以使用 :
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,你猜对了,一个字节数组:D)
据我所知,update() 和 digest() 方法基本上应该替代相应的 HashAlgorithm 派生的 TransformBlock() 和 TransformFinalBlock() 方法 class(在本例中为 SHA256) .
所以我在 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;
}
(同样,数据是一个字节数组)
但是,字节不匹配。我在这里遗漏了什么吗?
(我当然是,否则它会起作用,对吧?:S)
更新
为了给您提供更多信息,我已经匹配了 Java 和 运行 您在上面看到的代码之前的 C# 代码之间的字节。然后他们匹配。但是,C# 代码中的字节来自 UTF8 编码的字符串,而 Java 字节来自 c14Canonicalizer.canonicalizeSubtree() 方法。
我将更新上面的代码示例以包含它们的来源。
更新
对于它的价值,Java md.digest() 方法 returns 以下字节:
-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
转换为
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
而 C# 代码 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
使用 sha256.ComputeHash()
时
您尝试过 ComputeHash 方法吗?
即:
var byteArray = Encoding.ASCII.GetBytes("hello");
var sha = SHA256.Create();
byte[] outputBytes = sha.ComputeHash(byteArray);
var result = BitConverter.ToString(outputBytes).Replace("-", "").ToLower();
编辑
你能试试这个吗?
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);
我发现了问题。问题在于 xml 字符串中用于换行符的字符。在我的 xml \r\n 中用于换行符,需要做的是将其更改为 \n 这似乎是 java 使用的。
我找到了答案 here Gerben Rampaart 在不同的在线 sha256 计算器上注意到了同样的事情,ken2k 知道其中的区别
一旦我这样做了SHA256.TransformFinalBlock()
就像一个魅力。
最终解决方案如下所示:
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;
}
}
可能需要进行大量的重构和改进,但它完成了工作。
非常感谢所有帮助过我的人!
以下示例可能给出相同的结果。因为你正在做同样的操作很长的路要走。在您的代码中,您正在从 XmlDsigExcC14NWithCommentsTransform 中清除 xml,然后计算哈希值。下面例子直接计算。
XmlDocument doc = new XmlDocument();
doc.LoadXml("<a><xmlString>mkysoft</xmlString></a>");
XmlDsigExcC14NWithCommentsTransform c14n = new XmlDsigExcC14NWithCommentsTransform();
c14n.LoadInput(doc);
var digest = c14n.GetDigestedOutput(SHA256.Create());
我有一个简单的问题。 我需要将 sha256 校验和方法从 java 重写为 C#
所以我有这个 Java 鳕鱼可以使用 :
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,你猜对了,一个字节数组:D)
据我所知,update() 和 digest() 方法基本上应该替代相应的 HashAlgorithm 派生的 TransformBlock() 和 TransformFinalBlock() 方法 class(在本例中为 SHA256) .
所以我在 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;
}
(同样,数据是一个字节数组)
但是,字节不匹配。我在这里遗漏了什么吗?
(我当然是,否则它会起作用,对吧?:S)
更新
为了给您提供更多信息,我已经匹配了 Java 和 运行 您在上面看到的代码之前的 C# 代码之间的字节。然后他们匹配。但是,C# 代码中的字节来自 UTF8 编码的字符串,而 Java 字节来自 c14Canonicalizer.canonicalizeSubtree() 方法。
我将更新上面的代码示例以包含它们的来源。
更新
对于它的价值,Java md.digest() 方法 returns 以下字节:
-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
转换为
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
而 C# 代码 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
使用 sha256.ComputeHash()
您尝试过 ComputeHash 方法吗?
即:
var byteArray = Encoding.ASCII.GetBytes("hello");
var sha = SHA256.Create();
byte[] outputBytes = sha.ComputeHash(byteArray);
var result = BitConverter.ToString(outputBytes).Replace("-", "").ToLower();
编辑
你能试试这个吗?
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);
我发现了问题。问题在于 xml 字符串中用于换行符的字符。在我的 xml \r\n 中用于换行符,需要做的是将其更改为 \n 这似乎是 java 使用的。
我找到了答案 here Gerben Rampaart 在不同的在线 sha256 计算器上注意到了同样的事情,ken2k 知道其中的区别
一旦我这样做了SHA256.TransformFinalBlock()
就像一个魅力。
最终解决方案如下所示:
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;
}
}
可能需要进行大量的重构和改进,但它完成了工作。
非常感谢所有帮助过我的人!
以下示例可能给出相同的结果。因为你正在做同样的操作很长的路要走。在您的代码中,您正在从 XmlDsigExcC14NWithCommentsTransform 中清除 xml,然后计算哈希值。下面例子直接计算。
XmlDocument doc = new XmlDocument();
doc.LoadXml("<a><xmlString>mkysoft</xmlString></a>");
XmlDsigExcC14NWithCommentsTransform c14n = new XmlDsigExcC14NWithCommentsTransform();
c14n.LoadInput(doc);
var digest = c14n.GetDigestedOutput(SHA256.Create());