【问题标题】:Converting SHA256 from Java to C#将 SHA256 从 Java 转换为 C#
【发布时间】:2025-12-03 08:10:02
【问题描述】:

我有一个简单的问题。 我需要将 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 派生类(在本例中为 SHA256)中的 TransformBlock() 和 TransformFinalBlock() 方法。

所以我在 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() 方法返回以下字节:

-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# 代码返回时

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

【问题讨论】:

  • 请不要回答这个问题:*.com/questions/1521249/…
  • sha256sum 这样的独立工具(阅读:保证工作)打印什么?
  • @RustyX 我试过onlinemd5.com,结果和Java代码的结果一样。
  • @mkysoft 我已经研究了这个问题,他们的建议就是我在这里所做的。 C# 代码中的 xml 来自对 XmlDsigExcC14NWithCommentsTransform.GetOutput() 的调用,该结果中的字节与 javas Canonicalizer.canonicalizeSubtree() 中的字节匹配

标签: java c# sha256


【解决方案1】:

你试过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);

【讨论】:

  • 是的,先生,我有,但无济于事。
  • 你能分享一下 md.digest() 的结果吗?功能?
  • @QuentinRoger Shazi 使用 ALGO_ID_C14N_EXCL_WITH_COMMENTS 算法计算 xml 文档的哈希值。您不能用 ComputeHash 计算相同的值。我们需要使用密码学。检查这个问题:*.com/questions/1521249/…
  • @mkysoft 感谢您的提示,我会调查一下。
  • @Quentin Roger 尝试了您的代码。但是它会在 c14n.LoadInnerXml(doc.ChildNodes); 上引发 nullreferenceexception 但是,如果我将 c14n.LoadInnerXml(doc.ChildNodes); 更改为 c14n.LoadInput(doc); 则它可以工作,尽管我得到了错误的结果。
【解决方案2】:

我发现了问题。问题是用于 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;
    }
}

可能需要进行大量重构和改进,但它可以完成工作。

非常感谢所有帮助过我的人!

【讨论】:

    【解决方案3】:

    以下示例可能会给出相同的结果。因为你正在做同样的操作很长的路要走。在您的代码中,您从 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());
    

    【讨论】:

    • 我实际上尝试了这种方法,但它仍会将新行解释为 \r\n ,这将提供与您自己将其更改为 \n 不同的摘要。因此,为什么我需要 xml 在获取摘要之前通过 XmlWriter