当前位置: 首页 > 知识库问答 >
问题:

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

何安宜
2023-03-14

对于支付提供商,我需要使用HMAC-SHA256计算基于哈希的消息身份验证码。这给我带来了不少麻烦。

支付提供商给出了两个在伪码中正确计算身份验证码的示例。所有键均为十六进制。

key = 57617b5d2349434b34734345635073433835777e2d244c31715535255a366773755a4d70532a5879793238235f707c4f7865753f3f446e633a21575643303f66
message = "amount=100&currency=EUR"
MAC = HMAC-SHA256( hexDecode(key), message )
result = b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
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/

共有3个答案

邬弘化
2023-03-14

您可以将此方法用于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);

以下是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);
    }
边霄
2023-03-14

以下是一种字符串扩展方法,用于为给定字符串获取相当标准的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();
}
利俊迈
2023-03-14

编辑:您可能正在寻找一种快速而简单的方法来执行HMAC-SHA256,而不是深入到更精细的细节中。最初的问题询问了下面将进一步解释的更详细的细节。

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

在里面NET 5及以上版本,请使用系统。转换FromHexString(谢谢@proximab)就是这样。如果你在预科班。NET 5中,滚动到“Helper functions”,其中有其他解决方案。

using System;
using System.Security.Cryptography;
...
private static byte[] HashHMACHex(string keyHex, string messageHex)
{
    var key = Convert.FromHexString(hexKey);
    var message = Convert.FromHexString(messageHex);
    var hash = new HMACSHA256(key);
    return hash.ComputeHash(message);
}

继续阅读。您可能希望使用下面的“方法2”作为参考点,并将其调整为您的服务希望您实现HMAC以防消息篡改。

在这里,我们将手动计算HMAC-SHA256(这回答了原始问题中的“方法2”)。

假设outerKeyinnerKey消息已经是字节数组,我们执行以下操作:

表示法:假设A B连接字节数组A和B。您也可以在更多学术设置中看到A B表示法。

HMAC = SHA256( outerKey + SHA256( innerKey + message  )   )
              .          .       `------------------´ .  .
               \          \           `innerData`    /  /
                \          `------------------------´  /   
                 \               `innerHash`          /
                  `----------------------------------´
                               `data`

因此,可以将代码分解为以下步骤(以上述步骤为指导):

  1. 创建一个空缓冲区[byte[]innerData(字节[]内部数据)innerKey的长度。消息长度。长度(再次假设字节数组)

为了进行字节复制,我使用了Buffer。BlockCopy()函数,因为它显然比其他一些方式(源)更快。

n、 b.使用新的ReadOnlySpan,可能有更好的方法(请阅读:最肯定的方法)来实现这一点

我们可以将这些步骤转化为以下内容:

using System;
using System.Security.Cryptography;
...
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;
}

您有纯ASCII或UTF8文本,但需要它是字节[]
使用ASciencoding或UTF8Encoding,或您正在使用的任何外来编码。

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

您有一个字节[],但必须是十六进制字符串。

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

您有一个十六进制字符串,但必须是字节[]`。

.净值5及以上

private static byte[] HexDecode(string hex) =>
    System.Convert.FromHexString(hex);

之前净5(感谢@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;
}

n、 如果你需要一个性能优化的版本。NET Framework 4。x、 您也可以对进行后端口。NET 5版本(通过替换ReadOnlySpan

为了完整起见,以下是使用“方法1”和“方法2”回答问题的最终方法

“方法1”(使用.NET库)

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

“方法2”(手动计算)

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

我们可以使用控制台应用程序快速执行健全性检查:

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

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

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

您应该正确排列所有哈希:

Expected: b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
Method 1: b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
Method 2: b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905

此答案的原始代码可访问:http://pastebin.com/xAAuZrJX

 类似资料:
  • 我在运行测试脚本后收到一个错误。我已经在其他测试脚本中运行了相同的方法,但我使用的这个脚本几乎与其他测试脚本相同。你能帮助我为什么会出现这个错误吗? 这是获取excel数据的方法。 这是我的数据提供程序方法

  • 我有一个包含2列测试数据的xls文件。对于每一列,我都希望有一个测试方法。 现在我知道了如何使用dataprovider读取xls文件并将测试数据提供给一个测试方法。但如何使用同一个dataprovider进行多个测试呢? 我有下面的代码,一旦我运行这个代码得到错误,我的代码是: =========================================================默认

  • 本文向大家介绍IIS7.5 提示未在本地计算机上注册“Microsoft.Jet.OleDb.4.0”提供程序,包括了IIS7.5 提示未在本地计算机上注册“Microsoft.Jet.OleDb.4.0”提供程序的使用技巧和注意事项,需要的朋友参考一下 在WIN7 X64平台IIS7.5,使用Asp.net连接access数据库时候,提示:未在本地计算机上注册“Microsoft.Jet.Ole

  • 商家支付回调接口 url POST http://callback_url 回调参数说明 参数 类型 描述 uid string 百度用户ID order_id string 百度网盘订单号 third_order_id string 业务方订单号 pay_no string 支付流水号 pay_time int 支付时间 ts int 当前时间戳 sign string 签名参数(对以上参数按照

  • 同公众号支付

  • 说明 支付宝小程序支付交易SDK。 官方文档:https://opendocs.alipay.com/open/204/105465/ 类 请求参数类 APP支付参数 类名:\Yurun\PaySDK\AlipayApp\MiniApp\Params\Pay\Request 属性 名称 类型 说明 $method string 接口名称 $notify_url string 支付宝服务器主动通知商