NodeJS 转换为字节数组代码与 python 相比返回不同的结果

Lin*_*yen 6 javascript python arrays hash node.js

我得到了以下 Javascript 代码,我需要将其转换为 Python(我不是哈希方面的专家,所以很抱歉我在这个主题上的知识)

function generateAuthHeader(dataToSign) {
    let apiSecretHash = new Buffer("Rbju7azu87qCTvZRWbtGqg==", 'base64');
    let apiSecret = apiSecretHash.toString('ascii');
    var hash = CryptoJS.HmacSHA256(dataToSign, apiSecret);
    return hash.toString(CryptoJS.enc.Base64);
}
Run Code Online (Sandbox Code Playgroud)

当我跑的时候generateAuthHeader("abc")它返回了+jgBeooUuFbhMirhh1KmQLQ8bV4EXjRorK3bR/oW37Q=

所以我尝试编写以下Python代码:

def generate_auth_header(data_to_sign):
    api_secret_hash = bytearray(base64.b64decode("Rbju7azu87qCTvZRWbtGqg=="))
    hash = hmac.new(api_secret_hash, data_to_sign.encode(), digestmod=hashlib.sha256).digest()
    return base64.b64encode(hash).decode()
Run Code Online (Sandbox Code Playgroud)

但是当我运行时generate_auth_header("abc")它返回了不同的结果aOGo1XCa5LgT1CIR8C1a10UARvw2sqyzWWemCJBJ1ww=

有人能告诉我我的 Python 代码有什么问题以及我需要更改什么吗?

base64 是我为这篇文章自己生成的字符串

更新:这是我正在使用的文档

//Converting the Rbju7azu87qCTvZRWbtGqg== (key) into byte array 
//Converting the data_to_sign into byte array 
//Generate the hmac signature
Run Code Online (Sandbox Code Playgroud)

它看起来像apiSecretHash并且api_secret_hash是不同的,但我不太明白,因为new Buffer()NodeJS 中的等价物bytearray()在 python 中

Lin*_*yen 5

我花了2天的时间查了一下,并询问了python discord中的人,我终于得到了答案。我来总结一下问题:

  • 两者的 API 秘密哈希值返回字节数组 javascript 的不同哈希值

JavaScript

apiSecret = "E8nm,ns:\u0002NvQY;F*"
Run Code Online (Sandbox Code Playgroud)

Python

api_secret_hash = b'E\xb8\xee\xed\xac\xee\xf3\xba\x82N\xf6QY\xbbF\xaa'
Run Code Online (Sandbox Code Playgroud)

一旦我们用 python 代码替换了哈希值,它就会返回相同的结果

def generate_auth_header(data_to_sign):
    api_secret_hash = "E8nm,ns:\u0002NvQY;F*".encode()

    hash = hmac.new(api_secret_hash, data_to_sign.encode(), digestmod=hashlib.sha256).digest()
    return base64.b64encode(hash).decode()
Run Code Online (Sandbox Code Playgroud)

Node.js 中的 ASCII 编码可以在这里找到https://github.com/nodejs/node/blob/a2a32d8beef4d6db3a8c520572e8a23e0e51a2f8/src/string_bytes.cc#L636-L647

case ASCII:
  if (contains_non_ascii(buf, buflen)) {
    char* out = node::UncheckedMalloc(buflen);
    if (out == nullptr) {
      *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
      return MaybeLocal<Value>();
    }
    force_ascii(buf, out, buflen);
    return ExternOneByteString::New(isolate, out, buflen, error);
  } else {
    return ExternOneByteString::NewFromCopy(isolate, buf, buflen, error);
  }
Run Code Online (Sandbox Code Playgroud)

当数据包含非ASCII字符时,会调用这个force_ascii()函数,该函数在此处实现https://github.com/nodejs/node/blob/a2a32d8beef4d6db3a8c520572e8a23e0e51a2f8/src/string_bytes.cc#L531-L573

所以我们需要检查与 NodeJS 相同的哈希值,这样我们就得到了 Python 代码的最终版本:

def generate_auth_header(data_to_sign):
    # convert to bytearray so the for loop below can modify the values
    api_secret_hash = bytearray(base64.b64decode("Rbju7azu87qCTvZRWbtGqg=="))
    
    # "force" characters to be in ASCII range
    for i in range(len(api_secret_hash)):
        api_secret_hash[i] &= 0x7f;

    hash = hmac.new(api_secret_hash, data_to_sign.encode(), digestmod=hashlib.sha256).digest()
    return base64.b64encode(hash).decode()
Run Code Online (Sandbox Code Playgroud)

现在它返回与 NodeJS 相同的结果

感谢来自 python discord 的 Mark 帮助我理解并解决这个问题!

希望将来尝试将字节数组从 javascript 转换为 python 的任何人都知道 NodeJS Buffer() 函数的不同之处