使用C#来匹配支付服务提供商例如计算HMACSHA256(Calculating HMACSHA25

2019-06-27 13:15发布

对于支付服务提供商,我需要计算基于散列的消息认证码,使用HMAC-SHA256。 这引起了我相当多的麻烦。

支付提供商给出的伪代码的orrectly计算认证码的两个例子。 所有按键都在十六进制。

方法1

key = 57617b5d2349434b34734345635073433835777e2d244c31715535255a366773755a4d70532a5879793238235f707c4f7865753f3f446e633a21575643303f66
message = "amount=100&currency=EUR"
MAC = HMAC-SHA256( hexDecode(key), message )
result = b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905

方法2

message = "amount=100&currency=EUR"
Ki = 61574d6b157f757d02457573556645750e0341481b127a07476303136c005145436c7b46651c6e4f4f040e1569464a794e534309097258550c17616075060950
Ko = 0b3d27017f151f17682f1f193f0c2f1f64692b227178106d2d096979066a3b2f2906112c0f760425256e647f032c2013243929636318323f667d0b0a1f6c633a
MAC = SHA256( hexDecode(Ko) + SHA256( hexDecode(Ki) + message ) )
result = b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905

我试着写的代码要做到这一点,做一些研究之后,但我一直想出不同的结果。

private static void Main(string[] args)
    {
        var key = "57617b5d2349434b34734345635073433835777e2d244c31715535255a366773755a4d70532a5879793238235f707c4f7865753f3f446e633a21575643303f66";
        var ki = "61574d6b157f757d02457573556645750e0341481b127a07476303136c005145436c7b46651c6e4f4f040e1569464a794e534309097258550c17616075060950";
        var ko = "0b3d27017f151f17682f1f193f0c2f1f64692b227178106d2d096979066a3b2f2906112c0f760425256e647f032c2013243929636318323f667d0b0a1f6c633a";
        var mm = "amount=100&currency=EUR";

        var result1 = CalcHMACSHA256Hash(HexDecode(key), mm);

        var result2 = CalcSha256Hash(string.Format("{0}{1}", HexDecode(ko), CalcSha256Hash(HexDecode(ki) + mm)));

        Console.WriteLine("Expected: b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905");
        Console.WriteLine("Actual 1: " + result1);
        Console.WriteLine("Actual 2: " + result2);

        Console.WriteLine("------------------------------");
        Console.ReadKey();

    }

    private static string HexDecode(string hex)
    {
        var sb = new StringBuilder();
        for (int i = 0; i <= hex.Length - 2; i += 2)
        {
            sb.Append(Convert.ToString(Convert.ToChar(Int32.Parse(hex.Substring(i, 2), System.Globalization.NumberStyles.HexNumber))));
        }
        return sb.ToString();
    }

    private static string CalcHMACSHA256Hash(string plaintext, string salt)
    {
        string result = "";
        var enc = Encoding.Default;
        byte[]
        baText2BeHashed = enc.GetBytes(plaintext),
        baSalt = enc.GetBytes(salt);
        System.Security.Cryptography.HMACSHA256 hasher = new HMACSHA256(baSalt);
        byte[] baHashedText = hasher.ComputeHash(baText2BeHashed);
        result = string.Join("", baHashedText.ToList().Select(b => b.ToString("x2")).ToArray());
        return result;
    }


    public static string CalcSha256Hash(string input)
    {
        SHA256 sha256 = new SHA256Managed();
        byte[] sha256Bytes = Encoding.Default.GetBytes(input);
        byte[] cryString = sha256.ComputeHash(sha256Bytes);
        string sha256Str = string.Empty;
        for (int i = 0; i < cryString.Length; i++)
        {
            sha256Str += cryString[i].ToString("x2");
        }
        return sha256Str;
    }

这是结果我得到:

Expected: b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
Actual 1: 421ce16f2036bb9f2a3770c16f01e9220f0232d45580584ca41768fd16c15fe6
Actual 2: 290f14398bf8c0959dfc963e2fd9c377534c6fec1983025d2ab192382f132b92

因此,与没有上述两种方法,我可以得到提供商例如想要的结果。

我缺少的是在这里吗? 难道编码? 是我hexDecode搞砸了?

从支付服务提供商测试工具: http://tech.dibs.dk/dibs_api/other_features/hmac_tool/

PHP代码示例: http://tech.dibspayment.com/dibs_api/other_features/mac_calculation/

Answer 1:

我做了你的问题的完整解决方案(因为这可能是你要找的)。 它计算同时使用你的方法1和2的正确的哈希值。

概观

该程序可以被组织到三个部分:

  1. 散列函数 -这些是实际的功能,将使用计算散列byte[]为输入
  2. 编码助手 -这些都与哈希函数的十六进制用(#3),并转换以下帮助:
    • string - > byte[]
    • byte[] - >十六进制string
    • 十六进制string - > byte[]感谢@bobince!)
  3. 哈希函数的十六进制 -这些都是辅助功能,使您可以使用使用十六进制字符串(#1)散列函数的输入来代替。 这些使用的编码佣工(#2)做到这一点。

0 using语句

在开始之前,请确保您有以下using语句,这样你就不会从不包括他们获得一吨的错误。

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

1.散列函数

HMAC-SHA256(方法1)

这将计算HMAC-SHA256(你的方法1)。 正如你所看到的,它比方法2 简单得多 ,但给出了相同的结果。

private static byte[] HashHMAC(byte[] key, byte[] message)
{
    var hash = new HMACSHA256(key);
    return hash.ComputeHash(message);
}

SHA256(方法2)

我们计算散列使用一吨沙(你的方法2)哈希,它是更复杂一点。 这是基本相同,而不六角解码的伪代码,并使用byte[]为输入来代替。 这将是这样的:

MAC = SHA256( outerKey + SHA256( innerKey + message ) )

而不是你的:

MAC = SHA256( hexDecode(outerKey) + SHA256( hexDecode(innerKey) + message ) )

outerKeyinnerKey ,和message都是byte[]秒。 当然,在这种情况下,所有的按键都已经从十六进制字符串解码,但它可能也被byte[]太。

因此,代码可以被细分为下列步骤操作:

  1. 用于内数据创建缓冲区并将其存储在byte[] innerData
  2. 复制innerKeymessagebyte[] innerData
  3. 现在计算的SHA256散列innerData并将其存储在byte[] innerHash
  4. 对于最终的和整个散列值,在创建的缓冲器它byte[] data
  5. 的复制outerKeyinnerHash ,先前计算的散列(从#3),所述data
  6. 计算的最终散列data并将其存储在result并返回。

要做到我使用的字节复制Buffer.BlockCopy()函数,因为它显然比其他一些方法(更快源 )。 这些步骤则可以写成这样的代码:

private static byte[] HashSHA(byte[] innerKey, byte[] outerKey, byte[] message)
{
    var hash = new SHA256Managed();

    // Compute the hash for the inner data first
    byte[] innerData = new byte[innerKey.Length + message.Length];
    Buffer.BlockCopy(innerKey, 0, innerData, 0, innerKey.Length);
    Buffer.BlockCopy(message, 0, innerData, innerKey.Length, message.Length);
    byte[] innerHash = hash.ComputeHash(innerData);

    // Compute the entire hash
    byte[] data = new byte[outerKey.Length + innerHash.Length];
    Buffer.BlockCopy(outerKey, 0, data, 0, outerKey.Length);
    Buffer.BlockCopy(innerHash, 0, data, outerKey.Length, innerHash.Length);
    byte[] result = hash.ComputeHash(data);

    return result;
}

2.辅助函数

在我们得到的散列函数十六进制,你需要一些功能,以帮助在概述说事之间的转换。

string - > byte[]

字符串编码假定该文本是纯ASCII,似乎工作(现在)。 不过,如果你需要用花哨的符号编码,你可能会需要使用UTF8来代替。 如果是这样的话,那么转出ASCIIEncodingUTF8Encoding或任何编码,你正在使用。

private static byte[] StringEncode(string text)
{
    var encoding = new ASCIIEncoding();
    return encoding.GetBytes(text);
}

byte[] - >十六进制string

所有这样做是采取一个字节数组,并将其转向小写十六进制字符串。 很简单。

private static string HashEncode(byte[] hash)
{
    return BitConverter.ToString(hash).Replace("-", "").ToLower();
}

进制string - > byte[]

最后是一个十六进制字符串为一个字节数组的转换。 这来自@ bobince的答案,所以它不是我的。 给予信贷,信贷到期。

private static byte[] HexDecode(string hex)
{
    var bytes = new byte[hex.Length / 2];
    for (int i = 0; i < bytes.Length; i++)
    {
        bytes[i] = byte.Parse(hex.Substring(i * 2, 2), NumberStyles.HexNumber);
    }
    return bytes;
}

3.哈希函数十六进制

正如前面所说的,这些都是与散列函数工作与十六进制数据和字符串,而不是辅助功能。 他们是不言自明:

十六进制散列为HMAC

private static string HashHMACHex(string keyHex, string message)
{
    byte[] hash = HashHMAC(HexDecode(keyHex), StringEncode(message));
    return HashEncode(hash);
}

十六进制散列为SHA

private static string HashSHAHex(string innerKeyHex, string outerKeyHex, string message)
{
    byte[] hash = HashSHA(HexDecode(innerKeyHex), HexDecode(outerKeyHex), StringEncode(message));
    return HashEncode(hash);
}

4.控制台测试

好了所有的功能包裹在一起,这里是一个控制台程序,将调用的函数,以表明他们实际上是正常工作。

static void Main(string[] args)
{
    string message = "amount=100&currency=EUR";
    string expectedHex = "b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905";
    Console.WriteLine("Ref : " + expectedHex);

    // Test out the HMAC hash method
    string key = "57617b5d2349434b34734345635073433835777e2d244c31715535255a366773755a4d70532a5879793238235f707c4f7865753f3f446e633a21575643303f66";
    string hashHMACHex = HashHMACHex(key, message);
    Console.WriteLine("HMAC: " + hashHMACHex);

    // Test out the SHA hash method
    string innerKey = "61574d6b157f757d02457573556645750e0341481b127a07476303136c005145436c7b46651c6e4f4f040e1569464a794e534309097258550c17616075060950";
    string outerKey = "0b3d27017f151f17682f1f193f0c2f1f64692b227178106d2d096979066a3b2f2906112c0f760425256e647f032c2013243929636318323f667d0b0a1f6c633a";
    string hashSHAHex = HashSHAHex(innerKey, outerKey, message);
    Console.WriteLine("SHA : " + hashSHAHex);

    Console.ReadLine();
}

如果一切正常,它运行没有错误,你应该得到下面的输出显示,所有的哈希值是正确的( ref是期望的散列):

Ref : b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
HMAC: b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
SHA : b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905

结论

最后,只是为了确保一切正常,代码完全可以在这里找到:
http://pastebin.com/xAAuZrJX



Answer 2:

下面是得到一个相当标准的HMAC SHA 256令牌给定的字符串的字符串扩展方法:

用法:

myMessageString.HmacSha256Digest(mySecret)

字符串扩展方法:

public static string HmacSha256Digest(this string message, string secret)
{
    ASCIIEncoding encoding = new ASCIIEncoding();
    byte[] keyBytes = encoding.GetBytes(secret);
    byte[] messageBytes = encoding.GetBytes(message);
    System.Security.Cryptography.HMACSHA256 cryptographer = new System.Security.Cryptography.HMACSHA256(keyBytes);

    byte[] bytes = cryptographer.ComputeHash(messageBytes);

    return BitConverter.ToString(bytes).Replace("-", "").ToLower();
}


Answer 3:

您可以使用HMACSHA256此方法。

string key = "your key";
string message = "your message";
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] keyByte = encoding.GetBytes(key);

HMACSHA256 hmacsha256 = new HMACSHA256(keyByte);

byte[] messageBytes = encoding.GetBytes(message);
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
return ByteToString(hashmessage);


Answer 4:

甲SHA散列计算上的字节序列。 字节是一个完全不同的数据类型为字符。 你不应该使用字符串存储二进制数据,如哈希值。

sb.Append(Convert.ToString(Convert.ToChar(Int32.Parse(hex.Substring(1,2)...

这通过读取每个编码的字节和变成相同的Unicode代码点数量的字符创建一个字符串。 这相当于使用ISO-8859-1(Latin1的)编码,由于以Unicode匹配所述第一256个代码点的该编码的属性解码所述字节0-255。

VAR = ENC Encoding.Default; [...]玄武岩= enc.GetBytes(盐);

字节[] sha256Bytes = Encoding.Default.GetBytes(输入);

这两个字符转换回用系统默认编码字节。 这种编码安装之间变化,但它永远不会是ISO-8859-1 - 甚至是类似西欧的代码页1252在0x80-0x9F范围内的不同的字符。

因此要使用的字节数组不包含由例如十六进制序列隐含的字节。 一个廉价的解决将是使用Encoding.GetEncoding("ISO-8859-1")而不是默认的编码,但真的是你应该使用一个字节数组来存储数据摆在首位,而不是一个字符串,例如:

byte[] key= new byte[] { 0x57, 0x61, 0x7b, 0x5d, 0x23, 0x49, ... };

并传递直接进入ComputeHash

如果你必须从一个十六进制字符串初始化数据,直接解析成一个字节数组,例如:

private static byte[] HexDecode(string hex) {
    var bytes= new byte[hex.Length/2];
    for (int i= 0; i<bytes.Length; i++) {
        bytes[i]= byte.Parse(hex.Substring(i*2, 2), NumberStyles.HexNumber);
    }
    return bytes;
}


Answer 5:

我意识到了问题的答案,但我张贴这种的情况下,其他人需要它。 这里是由支付提供商(DIBS)创建的代码片段:

    /**
    * calculateMac
    * Calculates the MAC key from a Dictionary<string, string> and a secret key
    * @param params_dict The Dictionary<string, string> object containing all keys and their values for MAC calculation
    * @param K_hexEnc String containing the hex encoded secret key from DIBS Admin
    * @return String containig the hex encoded MAC key calculated
    **/
    public static string calculateMac(Dictionary<string, string> paramsDict, string kHexEnc)
    {
        //Create the message for MAC calculation sorted by the key
        var keys = paramsDict.Keys.ToList();
        keys.Sort();
        var msg = "";
        foreach (var key in keys)
        {
            if (key != keys[0]) msg += "&";
            msg += key + "=" + paramsDict[key];
        }

        //Decoding the secret Hex encoded key and getting the bytes for MAC calculation
        var kBytes = new byte[kHexEnc.Length / 2];
        for (var i = 0; i < kBytes.Length; i++)
        {
            kBytes[i] = byte.Parse(kHexEnc.Substring(i * 2, 2), NumberStyles.HexNumber);
        }

        //Getting bytes from message
        var msgBytes = Encoding.Default.GetBytes(msg);

        //Calculate MAC key
        var hash = new HMACSHA256(kBytes);
        var macBytes = hash.ComputeHash(msgBytes);
        var mac = BitConverter.ToString(macBytes).Replace("-", "").ToLower();

        return mac;
    }

http://tech.dibspayment.com/DX/Hosted/HMAC



Answer 6:

谢谢你救了我的时间。

request.Method = "GET";
string signature = "";
string strtime = DateTime.UtcNow.ToString("yyyy-MM-ddTHH\\:mm\\:ssZ");

string secret = "xxxx";

string message = "sellerid:email:" + strtime; 

var encoding = new System.Text.ASCIIEncoding(); 

byte[] keyByte = encoding.GetBytes(secret);

byte[] messageBytes = encoding.GetBytes(message);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
var hash = new HMACSHA256(keyByte);
byte[] signature1 = hash.ComputeHash(messageBytes);
signature = BitConverter.ToString(signature1).Replace("-", "").ToLower();
}

request.Headers.Add("authorization", "HMAC-SHA256" + " " + 
"emailaddress=xxx@xx.com,timestamp=" + strtime + ",signature=" + signature);
HttpWebResponse response = request.GetResponse() as HttpWebResponse;


文章来源: Calculating HMACSHA256 using c# to match payment provider example