给定的 C# 哈希码的 OpenSSL 等效项是什么?

给定的 C# 哈希码的 OpenSSL 等效项是什么?

我有使用以下逻辑验证哈希的 C# 服务器代码。我无法控制服务器代码,也无法更改它。

string key = "xZBn34L4myg[...]cebvr7A==";

var hmacSha256 = new System.Security.Cryptography.HMACSHA256 { Key = Convert.FromBase64String(key) };
byte[] hashPayLoad = hmacSha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(payLoad));  
string signature = Convert.ToBase64String(hashPayLoad);  

对于消息“信息“上面的代码计算JbE9u3X+bKwYizXNcrWTImjooqg+a4Lh9hj4yQHMoHs=为签名。

在客户端的 bash 脚本中,我想使用 OpenSSL 创建一条服务器应该验证的消息:

signature=$(echo -n "message" | openssl dgst -sha256 -hmac xZBn34L4myg[...]cebvr7A== -binary | base64)

该操作的结果是 的签名bFHvntwvCI6eJqTdoyryxgtPSwyUw/+a79rSvvKs5vE=

这与服务器版本不同。原因是服务器使用Convert.FromBase64String()来获取密钥的字节,但 OpenSSL 对“​​-hmac”参数的解释不同。如果我可以控制服务器,我可以将其更改为Key = UTF8.GetBytes(key),最终会得到与客户端相同的哈希值。但遗憾的是,这是不可能的。

所以我的问题是:如何更改 bash 脚本以使其产生服务器的散列​​结果?

答案1

我简直不敢相信……我找到了答案。经过几个小时的尝试!

OpenSSL 将输入的密钥字符串视为二进制数据。然而,服务器将其解释为 base64 编码数据,对其进行解码,并使用生成的字节作为密钥。

这意味着,客户端还必须将密钥视为 base64 并对其进行解码。可以使用以下方法完成此操作:

echo -n "xZBn34L4myg[...]cebvr7A==" | base64 --decode > key.dat

该命令会将解码后的数据写入文件。

有趣的是,现在可以将文件内容传递给 openssl,并且显然它不会抱怨潜在的不可打印字符:

echo -n "message" | openssl dgst -sha256 -hmac "$(<./key.dat)" -binary | base64

或者,也可以不使用文件绕行来实现:

hashedSignature=$(echo -n "message" | openssl dgst -sha256 -hmac "$(echo -n "xZBn34L4myg[...]cebvr7A==" | base64 --decode)" -binary | base64)

根据@user1686的评论,上述解决方案可能会有问题:

请记住,命令行参数是 C 空终止字符串,因此最好确保 -hmac 键没有任何 0x00 字节,否则它会在该位置被截断。使用 -macopt hexkey: 可避免此问题

为了解决这个问题,这里有一个使用六角扳手的版本:

hashedSignature=$(echo -n "message" | openssl dgst -sha256 -mac hmac -macopt hexkey:$(echo -n "xZBn34L4myg[...]cebvr7A==" | base64 --decode | hexdump -v -e '/1 "%02x"') -binary | base64)

我必须说,这并没有变得更具可读性:-) 我推测,0x00 字节的问题也适用于基于文件的解决方案。

全部将生成预期的哈希值JbE9u3X+bKwYizXNcrWTImjooqg+a4Lh9hj4yQHMoHs=

相关内容