Edit:您可能正在寻找一种快速、简单的方法来执行 HMAC-SHA256,而不需要深入了解更详细的细节。最初的问题询问了那些更详细的细节,这些细节将在下面进一步解释。
我想在一个上执行 HMAC-SHA256byte[]
信息输入
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 之前的版本,请滚动到“帮助函数”,其中有替代解决方案。
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”)。
Assume outerKey
, innerKey
, and message
已经是字节数组,我们执行以下操作:
符号: Assume A + B
连接字节数组 A 和 B。您可以
或者参见A || B
更多学术环境中使用的符号。
HMAC = SHA256( outerKey + SHA256( innerKey + message ) )
. . `------------------´ . .
\ \ `innerData` / /
\ `------------------------´ /
\ `innerHash` /
`----------------------------------´
`data`
因此,代码可以分为以下步骤(使用上述内容作为指导):
- 创建一个空缓冲区
byte[] innerData
的长度innerKey.Length + message.Length
(再次假设字节数组)
- 复制
innerKey
和message
进入byte[] innerData
- 计算 SHA256
innerData
并将其存储在byte[] innerHash
- 创建一个空缓冲区
byte[] data
的长度outerKey.Length + innerHash.Length
- 复制
outerKey
and innerHash
(来自步骤#3)
- 计算最终的哈希值
data
并将其存储在byte[] result
并归还它。
为了进行字节复制,我正在使用Buffer.BlockCopy() http://msdn.microsoft.com/en-us/library/system.buffer.blockcopy%28v=vs.110%29.aspx功能,因为它显然比其他一些方式更快(source https://stackoverflow.com/questions/895120/append-2-byte-arrays-in-c-sharp).
注:很可能(阅读:肯定是)使用新的更好的方法来做到这一点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[]
.
Use ASCIIEncoding http://msdn.microsoft.com/en-us/library/system.text.asciiencoding%28v=vs.110%29.aspx or UTF8Encoding http://msdn.microsoft.com/en-us/library/system.text.utf8encoding%28v=vs.110%29.aspx或您使用的任何外来编码。
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();
}
hex 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;
}
注:如果您需要 .NET Framework 4.x 上的性能调整版本,您也可以向后移植 .NET 5+ 版本(通过替换ReadOnlySpan<byte>
with byte[]
)。它使用正确的查找表并了解热代码路径。您可以参考 .NET 5 (麻省理工学院授权 https://github.com/dotnet/runtime/blob/main/LICENSE.TXT) System.Convert
code 在 Github 上 https://github.com/dotnet/runtime/blob/7afb66262c287b7832b872473d1e668038aa2496/src/libraries/Common/src/System/HexConverter.cs#L235.
为了完整起见,以下是使用“方法 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¤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 http://pastebin.com/xAAuZrJX