Matching ouput for HttpServerUtility.UrlTokenEncode in NodeJS Javascript

泪湿孤枕 提交于 2020-04-16 02:32:45

问题


I am looking at an example in dotnet which looks like the following: https://dotnetfiddle.net/t0y8yD.

The output for the HttpServerUtility.UrlTokenEncode method is:

Pn55YBwEH2S2BEM5qlNrq-LMNE8BDdHYwbWKFEHiPZo1

When I try to complete the same in NodeJS with encodeURI, encodeURIComponent or any other attempt I get the following:

Pn55YBwEH2S2BEM5qlNrq+LMNE8BDdHYwbWKFEHiPZo=

As you can see from the above the '-' should be a '+' and the last character part is different. The hash is created the same and outputs the same buffer.

  var hmac = crypto.createHmac("sha256", buf);
  hmac.update("9644873");
  var hash = hmac.digest("base64");

How can I get the two to match? One other important note is that this is one use case and I am unsure if there are other chars that do the same.

I am unsure if the dotnet variant is incorrect or the NodeJS version is. However, the comparison will be done on the dotnet side, so I need node to match that.


回答1:


The difference of the two results is caused by the use of Base64URL encoding in the C# code vs. Base64 encoding in node.js.

Base64URL and Base64 are almost identical, but Base64 encoding uses the characters +, / and =, which have a special meaning in URLs and thus have to be avoided. In Base64URL encoding + is replaced with -, / with _ and = (the padding character on the end) is either replaced with %20 or simply omitted.

In your code you're calculating a HMAC-SHA256 hash, so you get a 256 bit result, which can be encoded in 32 bytes. In Base64/Base64URL every character represents 6 bits, therefore you would need 256/6 = 42,66 => 43 Base64 characters. With 43 characters you would have 2 'lonesome' bits on the end, therefore a padding char (=) is added. The question now is why HttpServerUtility.UrlTokenEncode adds a 1 as a replacement for the padding char on the end. I didn't find anything in the documentation. But you you should keep in mind that it's insignificant anyway.

To to get the same in node.js, you can use the package base64url, or just use simple replace statements on the base64 encoded hash.

With base64url package:

const base64url = require('base64url');
var hmacB64 = "Pn55YBwEH2S2BEM5qlNrq+LMNE8BDdHYwbWKFEHiPZo="
var hmacB64url = base64url.fromBase64(hmacb64)

console.log(hmacB64url)

The result is:

Pn55YBwEH2S2BEM5qlNrq-LMNE8BDdHYwbWKFEHiPZo

as you can see, this library just omits the padding char.

With replace, also replacing the padding = with 1:

var hmacB64 = "Pn55YBwEH2S2BEM5qlNrq+LMNE8BDdHYwbWKFEHiPZo="
console.log(hmacb64.replace(/\//g,'_').replace(/\+/g,'-').replace(/\=+$/m,'1'))

The result is:

Pn55YBwEH2S2BEM5qlNrq-LMNE8BDdHYwbWKFEHiPZo1

I tried the C# code with different data and always got '1' on the end, so to replace = with 1 seems to be ok, though it doesn't seem to be conform to the RFC.

The other alternative, if this is an option for you, is to change the C# code. Use normal base64 encoding plus string replace to get base64url output instead of using HttpServerUtility.UrlTokenEncode

A possible solution for that is described here




回答2:


I'm new here so I can't comment (need 50 reputation), but I would like to add to @jqs answer that if the string ends with two "=", the replace needs to be done with "2". So my replace looks like:

hmacb64.replace(///g,'_').replace(/+/g,'-').replace(/\=\=$/m,'2').replace(/\=$/m,'1')



来源:https://stackoverflow.com/questions/60764586/matching-ouput-for-httpserverutility-urltokenencode-in-nodejs-javascript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!