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

2024-04-30

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

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

Method 1

key = 57617b5d2349434b34734345635073433835777e2d244c31715535255a366773755a4d70532a5879793238235f707c4f7865753f3f446e633a21575643303f66
message = "amount=100&currency=EUR"
MAC = HMAC-SHA256( hexDecode(key), message )
result = b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905

Method 2

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/ http://tech.dibs.dk/dibs_api/other_features/hmac_tool/

PHP 示例代码:http://tech.dibs payment.com/dibs_api/other_features/mac_calculation/ http://tech.dibspayment.com/dibs_api/other_features/mac_calculation/


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`

因此,代码可以分为以下步骤(使用上述内容作为指导):

  1. 创建一个空缓冲区byte[] innerData的长度innerKey.Length + message.Length(再次假设字节数组)
  2. 复制innerKeymessage进入byte[] innerData
  3. 计算 SHA256innerData并将其存储在byte[] innerHash
  4. 创建一个空缓冲区byte[] data的长度outerKey.Length + innerHash.Length
  5. 复制outerKey and innerHash(来自步骤#3)
  6. 计算最终的哈希值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&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 http://pastebin.com/xAAuZrJX

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 C# 计算 HMACSHA256 以匹配支付提供商示例 的相关文章

  • 我应该把 try/catch 和“using”语句放在哪里? [复制]

    这个问题在这里已经有答案了 可能的重复 try catch using 正确的语法 https stackoverflow com questions 4590490 try catch using right syntax 我想try c
  • WebClient.DownloadDataAsync 冻结了我的 UI

    我在 Form 构造函数中的 InitializeComponent 之后有以下代码 using WebClient client new WebClient client DownloadDataCompleted new Downloa
  • 锁定 ASP.NET 应用程序变量

    我在 ASP NET 应用程序中使用第三方 Web 服务 对第 3 方 Web 服务的调用必须同步 但 ASP NET 显然是多线程的 并且可能会发出多个页面请求 从而导致对第 3 方 Web 服务的同时调用 对 Web 服务的调用封装在自
  • XPATH 查询、HtmlAgilityPack 和提取文本

    我一直在尝试从名为 tim new 的类中提取链接 我也得到了解决方案 给出了解决方案 片段和必要的信息here https stackoverflow com questions 2982862 extracting a table ro
  • 并行化斐波那契序列生成器

    我正在学习并行化 在一项练习中 我得到了一些我应该提高性能的算法 其中之一是斐波那契数列生成器 array 0 0 array 1 1 for q 2 q lt MAX q array q array q 1 array q 2 我怀疑 这
  • 全局使用和 .NET Standard 2.0

    我最近意识到我可以使用 C 10 功能文件范围的命名空间在 NET Standard 2 0 项目中也可以通过设置
  • C++ 将联合强制转换为其成员类型之一

    以下对我来说似乎完全符合逻辑 但不是有效的 C 联合不能隐式转换为其成员类型之一 有人知道为什么不这样做的充分理由吗 union u int i char c function f int i int main u v v i 6 f v
  • while循环中的变量初始化

    我有一个可以分块读取文件的函数 public static DataObject ReadNextFile 数据对象看起来像这样 public DataObject public string Category get set And ot
  • C# 编译器数字文字

    有谁知道 C 编译器数字文字修饰符的完整列表 默认情况下 声明 0 使其成为 Int32 声明 0 0 使其成为 Double 我可以在末尾使用文字修饰符 f 来确保某些内容被视为 Single 例如像这样 var x 0 x is Int
  • 有什么方法可以重载 C# 中的扩展方法吗?

    我有以下模型模式 public abstract class PARENTCLASS public class CHILD A CLASS PARENTCLASS public static class EXTENSION public s
  • 如何在win32中使用GetSaveFileName保存文件?

    我编写此代码是为了获取 fileName 来保存我的文件 include stdafx h include
  • 你好,我最近正在开发我的新游戏,我遇到了*无限跳跃*的问题

    所以基本上当我按跳跃 空格键时我会跳跃但是如果我连续按空格键它 只是跳啊跳啊跳等等 我不想要我只想它跳一次 code if Input GetKeyDown space isGrounded velocity y Mathf Sqrt ju
  • 用于连接 DataTable 上的动态列的动态 LINQ

    我目前遇到的情况不确定如何继续 我有两个从数据库填充的数据表 我还有一个可用的列名称列表 可用于将这两个数据表连接在一起 我希望编写一组 LINQ 查询 这些查询将 显示两个数据表中的行 内部联接 用于从一个数据表更新另一个数据表 显示一个
  • Resharper:IEnumerable 的可能多重枚举

    我正在使用新的 Resharper 版本 6 在我的代码中的几个地方 它给一些文本加了下划线 并警告我可能存在IEnumerable 可能的多重枚举 我理解这意味着什么 并在适当的情况下采纳了建议 但在某些情况下 我不确定这实际上是一个大问
  • 使用 OleDbCommandBuilder 时访问 SQL 语法错误

    我要在 C 中使用 OleDbDataAdapter 在 Access 数据库中插入数据 但收到错误消息INSERT INTO 命令中的语法错误 BackgroundWorker worker new BackgroundWorker Ol
  • doxygen c++:记录由“using”声明公开的私有继承成员

    作为一个例子 我有以下课程 class A public void methodOne class B private A public Brief description using A methodOne 我还没有找到强制 doxyge
  • EnumDisplayDevices 与 WMI Win32_DesktopMonitor,如何检测活动监视器?

    对于我当前的 C 项目 我需要为在大量计算机上连接并处于活动状态的每个监视器检测一个唯一的字符串 研究指出了两种选择 使用 WMI 并查询 Win32 DesktopMonitor 以获取所有活动监视器 使用 PNPDeviceID 来唯一
  • 浮点字节序?

    我正在为实时海上模拟器编写客户端和服务器 并且由于我必须通过套接字发送大量数据 因此我使用二进制数据来最大化可以发送的数据量 我已经了解整数字节顺序以及如何使用htonl and ntohl为了规避字节顺序问题 但我的应用程序与几乎所有模拟
  • Autoconf 问题:“错误:C 编译器无法创建可执行文件”

    我正在尝试使用 GNU 自动工具构建一个用 C 编写的程序 但显然我设置错误 因为当configure运行 它吐出 configure error C compiler cannot create executables 如果我看进去con
  • 如何提高环复杂度?

    对于具有大量决策语句 包括 if while for 语句 的方法 循环复杂度会很高 那么我们该如何改进呢 我正在处理一个大项目 我应该减少 CC gt 10 的方法的 CC 并且有很多方法都存在这个问题 下面我将列出一些例如我遇到的问题的

随机推荐

  • jQuery Mobile/MVC:使用 RedirectToAction 更改浏览器 URL

    我的第一篇文章 当我使用 RedirectToAction 时 浏览器中的 url 不会改变 我怎样才能实现这个目标 在使用 Web 表单 10 多年后 我将切换到 ASP NET MVC 3 0 也使用 jQuery Mobile 我已经
  • 我收到“无法解析模块`react

    i am getting the below error 开发服务器返回响应错误码 500 URL http 10 0 2 2 8081 index android bundle platform android dev true hot
  • Android NSD 未发现所有服务

    我正在尝试使用 Android 本机服务发现来运行应用程序 但有时当我运行该应用程序时 它不会发现我的网络中的所有服务 我正在运行代码https github com joeluchoa nsd https github com joelu
  • ExitCodeGenerator 和 System.exit(0) 之间的区别

    我最近了解到关闭 Spring Boot 应用程序的正确方法是 public class Application Bean public ExitCodeGenerator exitCodeGenerator return new Exit
  • 什么时候适合使用Lua这样的嵌入式脚本语言

    我玩 魔兽世界 大约有两年了 我对用来编写插件的 Lua 很好奇 由于到目前为止我读到的有关 Lua 的内容都是 快 轻 和 这太棒了 所以我想知道如何以及何时使用它 您需要在系统中嵌入像 Lua 这样的脚本语言的典型情况是什么 当您需要最
  • 将控制器工厂添加到 ASP MVC

    我对工作中的一个大型项目有一个设计想法 我想我已经弄清楚了 但我真的很想得到一些关于a 总体想法和b 我提议的实现的反馈 基本想法很简单 我想创建一个 ASP MVC 应用程序 将来可以使用其他控制器和视图进行扩展 而无需重新编译代码 这个
  • 同时冻结第 1 行和 A 列

    我想在 Excel 2010 中同时 冻结 第 1 行和 A 列 这可能吗 选择单元格 B2 并单击 冻结窗格 这将冻结第 1 行和 A 列 为了便于将来参考 在 Excel 中选择 冻结窗格 将冻结所选单元格上方的行以及所选单元格左侧的列
  • 如何使用分页库在回收器视图中添加日期分隔符?

    经过大量搜索 我知道使用常规适配器是可能的 但我不知道如何使用分页库来做到这一点 我不需要代码 只是一个线索 Example 要添加分隔符 您基本上有两个选择 基于视图 您显式地将分隔符作为 项目 包含在列表中 并为这些分隔符定义新的视图类
  • 在Eclipse中添加注释掉代码的快捷按钮

    只是想知道是否有一种方法可以在 Eclipse 编辑器中添加一个按钮 就像在 Visual Studio 中一样 在 Java 视图 中快速注释或取消注释选定的代码块 Using the keyboard shortcut isn t ea
  • React 中大括号的使用

    我正在尝试学习 React 我在使用花括号时遇到问题 JSX 和 JS 之间使用大括号的区别 在下面的代码中 大括号 1 表示 现在是 JS 为什么有花括号 2 它已经在花括号区域内了吗 var React require react va
  • 如何在 suave webpart 中设置 Json 响应

    我从 Suave 和 F 开始 我正在尝试在我的 web 部件中传递一个 json 序列化对象以在我的响应中获取它 在 php 中我有这个 player1Key hdegftzj25 gameKey aegfhzkfszl
  • Swift:如何在 UITableViewController(包含 UICollectionView)中使用“didSelectItemAtIndexPath”?

    我有一个UITableViewController 在 的里面TableViewCell 它是UICollectionView 我想传递来自CollectionViewCell to a DetailViewController当用户点击单
  • 为什么 localhost 不会在 chrome (OSX) 中路由到 127.0.0.1?

    当我使用 node debug 启动脚本时 它尝试导航到 URL localhost debug port 5858 但找不到那里提供的页面 如果我将 localhost 更改为 127 0 0 1 一切正常 我可以 ping localh
  • ELF 文件头

    关于 elf 文件头的一个简单问题 我似乎找不到任何关于如何在 elf 头中添加 更改字段的有用信息 我希望能够更改幻数并向标题添加构建日期 以及可能的其他一些内容 据我了解 链接器创建标头信息 但我在 LD 脚本中没有看到任何引用它的内容
  • onKeyEvent 和辅助服务

    我的用户将使用启用 TalkBack 的服务或其他一些无障碍服务 我想捕获我们应用程序中的 onKeyEvent 事件 但该事件被分派到启用的辅助功能服务 我创建了以下基本辅助服务 public class Accessibility Se
  • PHP - 外部类/库可以从 apache 访问,但不能从 phpunit 访问

    我在我的 Web 应用程序中使用 ZeroMQ 套接字库 我已经配置了 php ini 以便 Apache 可以使用 ZMQ 但我不知道 phpunit 如何使用它 phpunit 不使用与 apache 使用相同的 php ini 吗 在
  • 如何将 ZIP 文件从 API 管理上传到 Blob 存储

    我正在对我的 Azure Api 管理 API 之一发送 POST 请求 在此 post 请求中 有一个 json 正文 其中包含 base64 编码数据 zip 文件 如下例所示 foo 酒吧 数据 你的base64字符串 在 API 策
  • 具有多个绑定的ServiceHostFactory

    如何使用具有多个绑定的 ServiceHostFactory 这是我尝试过的方法 但我不断遇到问题 一个又一个错误 using System using System Collections Generic using System Lin
  • 获取表情符号字符的描述

    每个表情符号都有一个描述 您可以在 Mac 操作系统中看到 Space特殊字符选择器 有这里有他们的名单 http www grumdrig com emoji list 有没有办法让我在代码中查询此描述 无需将它们全部输入到结构中 我想做
  • 使用 C# 计算 HMACSHA256 以匹配支付提供商示例

    对于支付提供商 我需要使用 HMAC SHA256 计算基于哈希的消息身份验证代码 这给我带来了很大的麻烦 支付提供商以伪代码形式给出了两个正确计算验证码的示例 所有密钥均为十六进制 Method 1 key 57617b5d2349434