C#中与PHP中的hash_hmac函数相对应的是什么?

37

我需要在.NET和C#中使用HMAC SHA512生成一个完整性字符串,并将其提供给PHP服务器。

Encoding encoding = Encoding.UTF8;
byte[] keyByte = encoding.GetBytes(key);
HMACSHA512 hmacsha512 = new HMACSHA512(keyByte);
byte[] messageBytes = encoding.GetBytes(message);
byte[]  hashmessage = hmacsha512.ComputeHash(messageBytes);
return(ByteToString(hashmessage).toUpper());

但它与 PHP 的 hash_hmac() 不匹配

$hmac = strtoupper(hash_hmac($pbx_hash, $msg, $binKey));

我尝试在C#中更改编码(UTF8,ASCII,Unicode),但没有成功。

我尝试了许多在网上找到的解决方案,但没有一个能给出相同的字符串 :(

我无法更改PHP代码,并且不知道C#中哪里出了问题。

编辑 这是ByteToString(从评论中复制):

static string ByteToString(byte[] buff)
{
    string sbinary = "";
    for (int i = 0; i < buff.Length; i++)
    {
        sbinary += buff[i].ToString("X2"); /* hex format */
    }
    return (sbinary);
}    
在经过多次尝试后,我发现如果PHP hash_hmac的密钥是字符串而不是字节数组,则会得到相同的结果。似乎问题出在PHP转换函数$binKey = pack("H*", $keyTest)上。

1
请查看以下网址以获取有关HMAC和C#的信息:http://stackoverflow.com/questions/tagged/hmac+c%23 - John Saunders
请查看此链接:http://msdn.microsoft.com/zh-cn/library/system.security.cryptography.hmacsha1.aspx - Abhijit-K
什么是ByteToString - cuongle
3个回答

43

问题必须是密钥/消息数据的实际表示。

请参考以下测试:

PHP

#!/usr/bin/php
<?php
print strtoupper(hash_hmac("sha256", "message", "key"));
?>

输出(通过http://writecodeonline.com/php/实时展示):

6E9EF29B75FFFC5B7ABAE527D58FDADB2FE42E7219011976917343065F58ED4A

C#

->

C#

using System;
using System.Text;
using System.Security.Cryptography;

public class Program
{
    private const string key = "key";
    private const string message = "message";
    private static readonly Encoding encoding = Encoding.UTF8; 

    static void Main(string[] args)
    {
        var keyByte = encoding.GetBytes(key);
        using (var hmacsha256 = new HMACSHA256(keyByte))
        {
            hmacsha256.ComputeHash(encoding.GetBytes(message));

            Console.WriteLine("Result: {0}", ByteToString(hmacsha256.Hash));
        }
    }
    static string ByteToString(byte[] buff)
    {
        string sbinary = "";
        for (int i = 0; i < buff.Length; i++)
            sbinary += buff[i].ToString("X2"); /* hex format */
        return sbinary;
    }    
}

输出结果(通过http://ideone.com/JdpeL实时查看):

Result: 6E9EF29B75FFFC5B7ABAE527D58FDADB2FE42E7219011976917343065F58ED4A

因此,请检查PHP输入数据的字符集/编码。还要检查实际的算法(在$pbx_hash中)。


谢谢,这是ByteToString的代码: static string ByteToString(byte[] buff) { string sbinary = ""; for (int i = 0; i < buff.Length; i++) { sbinary += buff[i].ToString("X2"); // 十六进制格式 } return (sbinary); } 我想它会返回相同的结果。 - Philippe
sehe,这些 BitConverter.ToStringSystem.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary 都是已经内置的类。 - L.B
谢谢,Bitconverter更短,但它不能解决PHP和C#之间的差异问题 :( - Philippe
@Philippe,鉴于您的“ByteToString”函数,我已经重新进行了分析。请查看我从头开始重新编写的答案。 - sehe
你为什么没有使用 encoding.GetString(hmacsha256.Hash) 呢? - Alireza Rinan
显示剩余2条评论

20
    private static string HashHmac(string message, string secret)
    {
        Encoding encoding = Encoding.UTF8;
        using (HMACSHA512 hmac = new HMACSHA512(encoding.GetBytes(secret)))
        {
            var msg = encoding.GetBytes(message);
            var hash = hmac.ComputeHash(msg);
            return BitConverter.ToString(hash).ToLower().Replace("-", string.Empty);
        }
    }

1
我正在使用C#从PHP的“hash_hmac()”示例中工作,并且能够使用上面的答案获得所需的结果。 - mholberger
4
很多API需要哈希的base64版本,你可以通过将现有的“return...”行替换为return Convert.ToBase64String(hash);来简单地得到它。 - adudley
3
请问您为什么需要使用 .Replace("-", string.Empty) 呢?这个方法的作用是将字符串中的破折号(-)替换为空格。 - Simple Guy

4

如上所述,问题出在PHP Pack函数(H*函数用于将密钥转换为字节数组)。 C# Getbytes函数无法给出相同的结果(utf8, asci, unicode...)。 在这里找到的解决方案:http://www.nuronconsulting.com/c-pack-h.aspx 对我来说很好用。 现在,C#中的HMAC与PHP匹配!

public static byte[] PackH(string hex)
{
       if ((hex.Length % 2) == 1) hex += '0';
       byte[] bytes = new byte[hex.Length / 2];
       for (int i = 0; i < hex.Length; i += 2)
       {
             bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
       }
 return bytes;
}

非常感谢大家的帮助。
谢谢。

1
我遇到了完全相同的问题。我尝试使用你的方法,但仍然有问题。我在这里问了一个类似的问题https://dev59.com/CnnZa4cB1Zd3GeqPtLCB。你能告诉我在你的解决方案中你实际上在哪里使用了PackH函数吗?谢谢! - billy jean

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接