使用 c# 计算 HMACSHA256 以匹配支付提供商示例
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12185122/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Calculating HMACSHA256 using c# to match payment provider example
提问by Kjensen
For a payment provider, I need to calculate a hash-based message authentication code, using HMAC-SHA256. That is causing me quite a bit of trouble.
对于支付提供商,我需要使用 HMAC-SHA256 计算基于哈希的消息身份验证代码。这给我带来了很多麻烦。
The payment provider gives two examples of orrectly calculated authentication code in pseudo-code. All keys are in hex.
支付提供商在伪代码中给出了两个正确计算的身份验证代码示例。所有的键都是十六进制的。
Method 1
方法一
key = 57617b5d2349434b34734345635073433835777e2d244c31715535255a366773755a4d70532a5879793238235f707c4f7865753f3f446e633a21575643303f66
message = "amount=100¤cy=EUR"
MAC = HMAC-SHA256( hexDecode(key), message )
result = b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
Method 2
方法二
message = "amount=100¤cy=EUR"
Ki = 61574d6b157f757d02457573556645750e0341481b127a07476303136c005145436c7b46651c6e4f4f040e1569464a794e534309097258550c17616075060950
Ko = 0b3d27017f151f17682f1f193f0c2f1f64692b227178106d2d096979066a3b2f2906112c0f760425256e647f032c2013243929636318323f667d0b0a1f6c633a
MAC = SHA256( hexDecode(Ko) + SHA256( hexDecode(Ki) + message ) )
result = b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
I tried to write the code to do this, after doing some research, but I keep coming up with different results.
在做了一些研究之后,我试图编写代码来做到这一点,但我不断得到不同的结果。
private static void Main(string[] args)
{
var key = "57617b5d2349434b34734345635073433835777e2d244c31715535255a366773755a4d70532a5879793238235f707c4f7865753f3f446e633a21575643303f66";
var ki = "61574d6b157f757d02457573556645750e0341481b127a07476303136c005145436c7b46651c6e4f4f040e1569464a794e534309097258550c17616075060950";
var ko = "0b3d27017f151f17682f1f193f0c2f1f64692b227178106d2d096979066a3b2f2906112c0f760425256e647f032c2013243929636318323f667d0b0a1f6c633a";
var mm = "amount=100¤cy=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;
}
And this is the result I get:
这是我得到的结果:
Expected: b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
Actual 1: 421ce16f2036bb9f2a3770c16f01e9220f0232d45580584ca41768fd16c15fe6
Actual 2: 290f14398bf8c0959dfc963e2fd9c377534c6fec1983025d2ab192382f132b92
So with none of the two methods, I can get the result the provider example wants.
因此,使用这两种方法中的任何一种,我都可以获得提供者示例想要的结果。
What am I missing here? Is it encoding? Is my hexDecode screwed up?
我在这里缺少什么?是编码吗?我的 hexDecode 搞砸了吗?
Test tool from payment provider: http://tech.dibs.dk/dibs_api/other_features/hmac_tool/
支付提供商的测试工具:http: //tech.dibs.dk/dibs_api/other_features/hmac_tool/
PHP sample code: http://tech.dibspayment.com/dibs_api/other_features/mac_calculation/
PHP 示例代码:http: //tech.dibspayment.com/dibs_api/other_features/mac_calculation/
采纳答案by Josh Bowden
I've made a complete solution to your issue (since that is probably what you were looking for). It calculates the correct hash using both your method 1 and 2.
我已经为您的问题提供了完整的解决方案(因为这可能是您正在寻找的)。它使用您的方法 1 和 2 计算正确的哈希值。
Overview
概述
The program can be organized in to three sections:
该计划可以分为三个部分:
- Hash functions- these are the actual functions that will calculate the hashes using
byte[]for input - Encoding helpers- these are used with the the hash hex functions (#3) and help with converting the following:
string->byte[]byte[]-> hexstring- hex
string->byte[](thanks @bobince!)
- Hash hex functions- these are helper functions so that you can use the hash functions (#1) using hex string as input instead. These use the encoding helpers (#2) to do that.
- 哈希函数- 这些是用于计算
byte[]用于输入 的哈希的实际函数 - 编码助手- 这些与哈希十六进制函数(#3)一起使用并帮助转换以下内容:
string->byte[]byte[]-> 十六进制string- 十六进制
string->byte[](感谢@bobince!)
- 散列十六进制函数- 这些是辅助函数,因此您可以使用使用十六进制字符串作为输入的散列函数 (#1)。这些使用编码助手(#2)来做到这一点。
Code
代码
0. Using Statements
0. 使用语句
Before you get started, make sure to that you have the following using statements so that you don't get a ton of errors from not including them.
在开始之前,请确保您拥有以下 using 语句,以免因不包含它们而出现大量错误。
using System;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
1. Hash functions
1. 哈希函数
HMAC-SHA256 (Method 1)
HMAC-SHA256(方法一)
This will calculate the HMAC-SHA256 (your method 1). As you can see, it is much simplerthan method 2 but gives the same result.
这将计算 HMAC-SHA256(您的方法 1)。如您所见,它比方法 2简单得多,但给出的结果相同。
private static byte[] HashHMAC(byte[] key, byte[] message)
{
var hash = new HMACSHA256(key);
return hash.ComputeHash(message);
}
SHA256 (Method 2)
SHA256(方法二)
Now to calculate the hash using a ton of SHA hashing (your method 2), it is a little bit more involved. This is basically the same as your pseudo-code without the hex decoding and uses byte[]for input instead. This would look like:
现在要使用大量 SHA 散列(您的方法 2)来计算散列,它会涉及更多。这与没有十六进制解码的伪代码基本相同,byte[]而是用于输入。这看起来像:
MAC = SHA256( outerKey + SHA256( innerKey + message ) )
Instead of your:
而不是你的:
MAC = SHA256( hexDecode(outerKey) + SHA256( hexDecode(innerKey) + message ) )
Where outerKey, innerKey, and messageare all byte[]s. Of course, in this case, all the keys have already been decoded from hexadecimal strings but it may as well been byte[]s too.
其中outerKey、innerKey和message都是byte[]s。当然,在这种情况下,所有的密钥都已经从十六进制字符串中解码出来了,但也可能是byte[]s。
So the code can be broken down into these steps:
所以代码可以分解为以下步骤:
- Create the buffer for the inner data and store it in
byte[] innerData - Copy the
innerKeyand themessageto thebyte[] innerData - Now compute the SHA256 hash of
innerDataand store it inbyte[] innerHash - For the final and entire hash, create a buffer for it in
byte[] data - Copy the
outerKeyandinnerHash, the previously computed hash (from #3), to thedata - Compute the final hash of
dataand store it inresultand return it.
- 为内部数据创建缓冲区并将其存储在
byte[] innerData - 将
innerKey和复制message到byte[] innerData - 现在计算 SHA256 哈希值
innerData并将其存储在byte[] innerHash - 对于最终和整个散列,为它创建一个缓冲区
byte[] data - 复制
outerKey和innerHash,先前计算的散列(从#3),对data - 计算 的最终哈希值
data并将其存储result并返回。
To do the byte copying I'm using the Buffer.BlockCopy()function since it apparently faster than some other ways (source).
Those steps then can be written in code like this:
为了进行字节复制,我正在使用该Buffer.BlockCopy()函数,因为它显然比其他一些方式(source)快。然后可以用这样的代码编写这些步骤:
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. Helper functions
2. 辅助功能
Before we get to the hash hex function, you need a few functions to help with converting between things as said in the overview.
在我们使用哈希十六进制函数之前,您需要一些函数来帮助在概述中所述的事物之间进行转换。
string-> byte[]
string-> byte[]
The string encoding assumes the text is plain ASCII and seems to work (for now). Though, if you need to encode with fancy symbols, you are probably going to need to use UTF8 instead. If that is the case, then switch out ASCIIEncodingwith UTF8Encodingor whatever encoding you're using.
字符串编码假定文本是纯 ASCII 并且似乎可以工作(目前)。但是,如果您需要使用花哨的符号进行编码,则可能需要改用 UTF8。如果是这样的话,那么转出ASCIIEncoding用UTF8Encoding或任何你正在使用的编码。
private static byte[] StringEncode(string text)
{
var encoding = new ASCIIEncoding();
return encoding.GetBytes(text);
}
byte[]-> hex string
byte[]-> 十六进制 string
All this does is take an array of bytes and turn it to a lower-case hex string. Pretty simple.
所有这一切都是获取一个字节数组并将其转换为小写的十六进制字符串。很简单。
private static string HashEncode(byte[] hash)
{
return BitConverter.ToString(hash).Replace("-", "").ToLower();
}
hex string-> byte[]
十六进制string->byte[]
Lastly is the conversion of a hex string to a byte array. This came from @bobince's answer so it's not mine. Giving credit where credit is due.
最后是将十六进制字符串转换为字节数组。这来自@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. Hash hex functions
3. 哈希十六进制函数
As said before, these are the helper functions that work with the hash functions with hex data and strings instead. They are pretty self-explanatory:
如前所述,这些是使用十六进制数据和字符串的哈希函数的辅助函数。它们是不言自明的:
Hex hashing for HMAC
HMAC 的十六进制哈希
private static string HashHMACHex(string keyHex, string message)
{
byte[] hash = HashHMAC(HexDecode(keyHex), StringEncode(message));
return HashEncode(hash);
}
Hex hashing for SHA
SHA 的十六进制散列
private static string HashSHAHex(string innerKeyHex, string outerKeyHex, string message)
{
byte[] hash = HashSHA(HexDecode(innerKeyHex), HexDecode(outerKeyHex), StringEncode(message));
return HashEncode(hash);
}
4. Console Test
4. 控制台测试
Well to wrap all the functions together, here is a console program that will call the functions to show that they are actually working properly.
将所有函数包装在一起,这是一个控制台程序,它将调用这些函数以表明它们实际上工作正常。
static void Main(string[] args)
{
string message = "amount=100¤cy=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();
}
If everything went correctly and it ran without errors, you should get the following output showing that all the hashes are correct (refis the expected hash):
如果一切正常并且运行没有错误,您应该得到以下输出,表明所有散列都是正确的(ref是预期的散列):
Ref : b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
HMAC: b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
SHA : b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
Conclusion
结论
Lastly, just to make sure everything worked, the code altogether can be found at:
http://pastebin.com/xAAuZrJX
最后,为了确保一切正常,可以在以下位置找到代码:http:
//pastebin.com/xAAuZrJX
回答by bobince
A SHA hash is calculated on a sequence of bytes. Bytes are a profoundly different datatype to characters. You should not use character Strings to store binary data such as hashes.
SHA 散列是根据字节序列计算的。字节是一种与字符截然不同的数据类型。您不应该使用字符串来存储二进制数据,例如哈希。
sb.Append(Convert.ToString(Convert.ToChar(Int32.Parse(hex.Substring(i, 2)...
sb.Append(Convert.ToString(Convert.ToChar(Int32.Parse(hex.Substring(i, 2)...)
This creates a character string by reading each encoded byte and turning into a character of the same Unicode code point number. This is equivalent to decoding the bytes 0-255 using the ISO-8859-1 (Latin1) encoding, due to that encoding's property of matching the first 256 code points in Unicode.
这通过读取每个编码字节并将其转换为具有相同 Unicode 代码点编号的字符来创建字符串。这等效于使用 ISO-8859-1 (Latin1) 编码解码字节 0-255,因为该编码的属性匹配 Unicode 中的前 256 个代码点。
var enc = Encoding.Default; [...] baSalt = enc.GetBytes(salt);
byte[] sha256Bytes = Encoding.Default.GetBytes(input);
var enc = Encoding.Default; [...] baSalt = enc.GetBytes(salt);
byte[] sha256Bytes = Encoding.Default.GetBytes(input);
These both convert the characters back to bytes using the system default encoding. This encoding varies between installs, but it will never be ISO-8859-1 - even the similar Western European code page 1252 has different characters in the range 0x80-0x9F.
它们都使用系统默认编码将字符转换回字节。这种编码因安装而异,但永远不会是 ISO-8859-1 - 即使是类似的西欧代码页 1252 在 0x80-0x9F 范围内也有不同的字符。
Consequently the byte array you are using doesn't contain the bytes implied by the example hex sequences. A cheap fix would be to use Encoding.GetEncoding("ISO-8859-1")instead of the default encoding, but really you should be using a bytes array to store data in the first place instead of a String, eg:
因此,您使用的字节数组不包含示例十六进制序列所隐含的字节。一个便宜的解决方法是使用Encoding.GetEncoding("ISO-8859-1")而不是默认编码,但实际上您应该首先使用字节数组而不是字符串来存储数据,例如:
byte[] key= new byte[] { 0x57, 0x61, 0x7b, 0x5d, 0x23, 0x49, ... };
and pass that directly into ComputeHash.
并将其直接传递到ComputeHash.
If you must initialise data from a hex string, parse it directly into a byte array, eg:
如果必须从十六进制字符串初始化数据,请将其直接解析为字节数组,例如:
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;
}
回答by Savas Adar
You can use this method for HMACSHA256.
您可以将此方法用于 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);
Here is the ByteToString method:
这是 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);
}
回答by Liknes
I realize the question is answered, but I am posting this in case others need it. Here is a snippet of code created by the payment provider (DIBS):
我意识到问题已得到解答,但我将其发布以防其他人需要它。以下是支付提供商 (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;
}
回答by Ganesh
Thanks you saved my time.
谢谢你节省了我的时间。
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" + " " +
"[email protected],timestamp=" + strtime + ",signature=" + signature);
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
回答by Chris Halcrow
Here's a string extension method for getting a fairly standard HMAC SHA 256 token for a given string:
这是为给定字符串获取相当标准的 HMAC SHA 256 令牌的字符串扩展方法:
usage:
用法:
myMessageString.HmacSha256Digest(mySecret)
string extension method:
字符串扩展方法:
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();
}

