gpt4 book ai didi

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

转载 作者:IT王子 更新时间:2023-10-29 03:53:26 25 4
gpt4 key购买 nike

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

支付提供商以伪代码的形式给出了两个正确计算的验证码示例。所有 key 都是十六进制。

方法一

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/

最佳答案

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

我想对 byte[] 消息输入执行 HMAC-SHA256

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

我想执行 HMAC-SHA256 但我有一个十六进制字符串输入

在 .NET 5 及更高版本中,像这样使用 System.Convert.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);
}

我正在使用一个奇怪的 API 服务,它有点像 HMAC,但它是自定义的

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


HMAC-SHA256 是如何工作的(如果您需要知道如何...)

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

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

Notation: Assume A + B concatenates byte array A and B. You mayalternatively see A || B notation used in more academic settings.

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

因此代码可以分解为这些步骤(以上面的为指导):

  1. 创建一个空缓冲区 byte[] innerData,其长度为 innerKey.Length + message.Length(再次假设字节数组)
  2. innerKeymessage复制到byte[] innerData
  3. 计算 innerData 的 SHA256 并将其存储在 byte[] innerHash
  4. 创建一个空缓冲区 byte[] data 的长度 outerKey.Length + innerHash.Length
  5. 复制 outerKeyinnerHash(来自第 3 步)
  6. 计算 data 的最终哈希并将其存储在 byte[] result 中并返回。

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

n.b. There is likely (read: most certainly) a better way to do this using the the new ReadOnlySpan<T> API.

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

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;
}

辅助函数

string -> byte[]

您有纯 ASCII 或 UTF8 文本,但需要它是 byte[]
使用 ASCIIEncoding UTF8Encoding 或您正在使用的任何奇特编码。

private static byte[] StringEncode(string text)
{
var encoding = new System.Text.ASCIIEncoding();
return encoding.GetBytes(text);
}
byte[] -> 十六进制 string

你有一个 byte[] ,但你需要它是一个十六进制 string

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

你有一个十六进制 string, but you need it to be a byte[]`。

.NET 5 及更高版本

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

.NET 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.b. If you need a performance tuned version on .NET Framework 4.x, you can alternatively backport the .NET 5+ version (by replacing ReadOnlySpan<byte> with byte[]). It uses proper lookup tables and conscious about hot-code paths. You can reference the .NET 5 (MIT licensed) System.Convert code on Github.


为了完整起见,这里是使用“方法 1”和“方法 2”回答问题的 final方法

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

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

“方法二”(人工计算)

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

关于c# - 使用 c# 计算 HMACSHA256 以匹配支付提供商示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12185122/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com