使用c#计算HMACSHA256以匹配支付提供商示例

Kje*_*sen 45 c# security cryptography

对于支付提供商,我需要使用HMAC-SHA256计算基于散列的消息验证代码.这给我带来了很多麻烦.

支付提供商提供两个以伪代码正确计算的认证代码的示例.所有键均为十六进制.

方法1

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

方法2

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

在做了一些研究之后,我尝试编写代码来做这件事,但我不断得出不同的结果.

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;
    }
Run Code Online (Sandbox Code Playgroud)

这是我得到的结果:

Expected: b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
Actual 1: 421ce16f2036bb9f2a3770c16f01e9220f0232d45580584ca41768fd16c15fe6
Actual 2: 290f14398bf8c0959dfc963e2fd9c377534c6fec1983025d2ab192382f132b92
Run Code Online (Sandbox Code Playgroud)

因此,如果没有这两种方法,我可以获得提供者示例所需的结果.

我在这里错过了什么?是编码吗?我的hexDecode搞砸了吗?

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

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

Jos*_*den 122

我已经为你的问题做了一个完整的解决方案(因为这可能就是你所寻找的).它使用方法1和2计算正确的哈希值.

概观

该计划可分为三个部分:

  1. 散列函数 - 这些是用于计算byte[]输入 的散列的实际函数
  2. 编码助手 - 这些与哈希十六进制函数(#3)一起使用,并帮助转换以下内容:
    • string - > byte[]
    • byte[] - >十六进制 string
    • hex string- > byte[](谢谢@bobince!)
  3. 散列十六进制函数 - 这些是辅助函数,因此您可以使用十六进制字符串作为输入来使用散列函数(#1).这些使用编码助手(#2)来做到这一点.

0.使用语句

在开始之前,请确保您拥有以下使用语句,这样您就不会因为不包含这些语句而导致大量错误.

using System;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

SHA256(方法2)

现在使用大量SHA散列(您的方法2)来计算散列,它涉及更多一点.这与没有十六进制解码的伪代码基本相同,byte[]而是用于输入.这看起来像:

MAC = SHA256( outerKey + SHA256( innerKey + message ) )
Run Code Online (Sandbox Code Playgroud)

而不是你的:

MAC = SHA256( hexDecode(outerKey) + SHA256( hexDecode(innerKey) + message ) )
Run Code Online (Sandbox Code Playgroud)

哪里outerKey,innerKeymessage,都是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;
}
Run Code Online (Sandbox Code Playgroud)

2.助手功能

在我们开始使用哈希十六进制函数之前,您需要一些函数来帮助在概述之间进行转换.

string - > byte[]

字符串编码假定文本是纯ASCII并且似乎有用(现在).但是,如果您需要使用花哨符号进行编码,则可能需要使用UTF8.如果是这种情况,请ASCIIEncoding使用UTF8Encoding或使用您正在使用的任何编码切换出来.

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

byte[] - >十六进制 string

所有这一切都取一个字节数组并将其转换为小写的十六进制字符串.很简单.

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

hex 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;
}
Run Code Online (Sandbox Code Playgroud)

3.哈希十六进制函数

如前所述,这些是辅助函数,它们使用带有十六进制数据和字符串的散列函数.它们非常不言自明:

用于HMAC的Hex散列

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

SHA的Hex散列

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

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();
}
Run Code Online (Sandbox Code Playgroud)

如果一切正常并且运行没有错误,您应该得到以下输出,显示所有哈希都是正确的(ref是预期的哈希):

Ref : b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
HMAC: b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
SHA : b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
Run Code Online (Sandbox Code Playgroud)

结论

最后,为了确保一切正常,可以在以下网址找到代码:http:
//pastebin.com/xAAuZrJX

  • 很棒的答案。我为此给予你一笔赏金(24 小时内,有某种延迟)。 (2认同)

Chr*_*row 30

这是一个字符串扩展方法,用于为给定字符串获取相当标准的HMAC SHA 256令牌:

用法:

myMessageString.HmacSha256Digest(mySecret)
Run Code Online (Sandbox Code Playgroud)

字符串扩展方法:

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();
}
Run Code Online (Sandbox Code Playgroud)

  • 我尝试使用这个,虽然它更简洁,但它不适用于上面提供的示例 1。`byte[] keyBytes = encoding.GetBytes(secret);` 不适用于示例中提供的十六进制密钥。它需要是“byte[] keyBytes = HexDecode(secret);”,其中 HexDecode 显示在示例答案中,并且最初显示在 @bobince 提供的详细答案中。 (2认同)

Sav*_*dar 5

您可以对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);
Run Code Online (Sandbox Code Playgroud)

这是ByteToString方法:

public static string ByteToString(byte[] buff)
    {
        string sbinary = "";

        for (int i = 0; i < buff.Length; i++)
        {
            sbinary += buff[i].ToString("X2"); // hex format
        }
        return (sbinary);
    }
Run Code Online (Sandbox Code Playgroud)