我正在看 dotnet 中的一个示例,如下所示:https://dotnetfiddle.net/t0y8yD。
HttpServerUtility.UrlTokenEncode
方法的输出是:
Pn55YBwEH2S2BEM5qlNrq-LMNE8BDdHYwbWKFEHiPZo1
当我尝试在 NodeJS 中使用
encodeURI
、encodeURIComponent
或任何其他尝试完成相同的操作时,我得到以下信息:
Pn55YBwEH2S2BEM5qlNrq+LMNE8BDdHYwbWKFEHiPZo=
从上面可以看出,“-”应该是“+”,并且最后一个字符部分不同。创建的哈希值相同并输出相同的缓冲区。
var hmac = crypto.createHmac("sha256", buf);
hmac.update("9644873");
var hash = hmac.digest("base64");
如何使两者匹配?另一个重要的注意事项是,这是一个用例,我不确定是否有其他字符执行相同的操作。
我不确定是 dotnet 变体不正确还是 NodeJS 版本不正确。但是,比较将在 dotnet 端完成,因此我需要节点来匹配它。
两个结果的差异是由于 C# 代码中使用 Base64URL 编码与 Node.js 中使用 Base64 编码造成的。
Base64URL
和Base64
几乎相同,但Base64
编码使用字符+
、/
和=
,这些字符在URL中具有特殊含义,因此必须避免使用。在 Base64URL
编码中,+
替换为 -
,/
替换为 _
,而 =
(末尾的填充字符)则替换为 %20
或直接省略。
在您的代码中,您正在计算 HMAC-SHA256 哈希值,因此您会得到 256 位结果,可以用 32 字节进行编码。在
Base64
/Base64URL
中,每个字符代表 6 位,因此您需要 256/6 = 42,66 => 43 个 Base64 字符。对于 43 个字符,末尾会有 2 个“孤独”位,因此添加了一个填充字符 (=
)。
现在的问题是为什么HttpServerUtility.UrlTokenEncode
添加一个1
来代替末尾的填充字符。我在文档中没有找到任何内容。但你应该记住,无论如何这都是微不足道的。
要在node.js中获得相同的效果,您可以使用包base64url,或者仅在base64编码的哈希上使用简单的
replace
语句。
带有base64url包:
const base64url = require('base64url');
var hmacB64 = "Pn55YBwEH2S2BEM5qlNrq+LMNE8BDdHYwbWKFEHiPZo="
var hmacB64url = base64url.fromBase64(hmacb64)
console.log(hmacB64url)
结果是:
Pn55YBwEH2S2BEM5qlNrq-LMNE8BDdHYwbWKFEHiPZo
如您所见,该库只是省略了填充字符。
使用
replace
,同时将填充 =
替换为 1
:
var hmacB64 = "Pn55YBwEH2S2BEM5qlNrq+LMNE8BDdHYwbWKFEHiPZo="
console.log(hmacb64.replace(/\//g,'_').replace(/\+/g,'-').replace(/\=+$/m,'1'))
结果是:
Pn55YBwEH2S2BEM5qlNrq-LMNE8BDdHYwbWKFEHiPZo1
我尝试了使用不同数据的 C# 代码,最后总是得到“1”,因此用
=
替换 1
似乎没问题,尽管它似乎不符合 RFC。
如果您可以选择,另一种选择是更改 C# 代码。使用普通
base64
编码加上字符串替换来获取 base64url
输出,而不是使用 HttpServerUtility.UrlTokenEncode
hmacb64.replace(/\//g,'_').replace(/\+/g,'-').replace(/\=\=$/m,'2').replace(/\=$/m,'1')