- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
对于支付提供商,我需要使用 HMAC-SHA256 计算基于哈希的消息验证码。这给我带来了很多麻烦。
支付提供商以伪代码的形式给出了两个正确计算的验证码示例。所有 key 都是十六进制。
key = 57617b5d2349434b34734345635073433835777e2d244c31715535255a366773755a4d70532a5879793238235f707c4f7865753f3f446e633a21575643303f66
message = "amount=100¤cy=EUR"
MAC = HMAC-SHA256( hexDecode(key), message )
result = b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
message = "amount=100¤cy=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¤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;
}
这是我得到的结果:
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-SHA256using System.Security.Cryptography;
...
private static byte[] HashHMAC(byte[] key, byte[] message)
{
var hash = new HMACSHA256(key);
return hash.ComputeHash(message);
}
在 .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);
}
继续阅读。您可能希望使用下面的“方法 2”作为引用点并将其调整为您的服务希望您实现 HMAC 以实现消息防篡改的方式。
这里我们将手动计算 HMAC-SHA256(这回答了原始问题中的“方法 2”)。
假设 outerKey
、 innerKey
和 message
已经是字节数组,我们执行以下操作:
Notation: Assume
A + B
concatenates byte array A and B. You mayalternatively seeA || B
notation used in more academic settings.
HMAC = SHA256( outerKey + SHA256( innerKey + message ) )
. . `------------------´ . .
\ \ `innerData` / /
\ `------------------------´ /
\ `innerHash` /
`----------------------------------´
`data`
因此代码可以分解为这些步骤(以上面的为指导):
byte[] innerData
,其长度为 innerKey.Length + message.Length
(再次假设字节数组)innerKey
和message
复制到byte[] innerData
中innerData
的 SHA256 并将其存储在 byte[] innerHash
中byte[] data
的长度 outerKey.Length + innerHash.Length
outerKey
和 innerHash
(来自第 3 步)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>
withbyte[]
). 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¤cy=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/
我在前几天的测验中遇到了以下问题。 Consider the code fragment (assumed to be in a program in which all variables are
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭 9 年前。 Improve this qu
我刚开始接触 Objective-C,一般来说是 C,所以我想这也是一个 C 问题。它更像是一个为什么的问题,而不是一个如何做的问题问题。 我注意到,在除以两个整数时,小数部分向下舍入为 0,即使结果
我是一名优秀的程序员,十分优秀!