如何向btoa传入正确二进制字符串,实现原生JS替代CryptoJS编码?
解决原生JS中HMAC-SHA1十六进制转Base64的错误问题
问题的核心是你误解了btoa()的输入要求——它需要的是代表原始字节的字符字符串,而不是由0和1组成的二进制位字符串。你的代码把十六进制转成了一串01字符,这相当于把每个字节拆成了8个单独的字符,完全不符合btoa的预期,所以得到了错误的结果。
修正后的完整代码
首先,我们需要把十六进制字符串转换成每个字符对应一个字节的字符串,再传给btoa():
const encrypted_vendor = VENDOR.hmacSHA1("message", "secret"); // 输出十六进制字符串:"0caf649feee4953d87bf903ac1176c45e028df16" // 拆分十六进制字符串为双字符的字节片段 function splitHex(hexString) { const chunks = []; for (let i = 0; i < hexString.length; i += 2) { chunks.push(hexString.substring(i, i + 2)); } return chunks; // 注意:原函数遗漏了返回值,这也是问题之一 } // 将单个十六进制字节转换为对应的字符(每个字符代表一个原始字节) function hexByteToChar(hexByte) { return String.fromCharCode(parseInt(hexByte, 16)); } // 生成符合btoa要求的字节字符串,再编码为Base64 const hexChunks = splitHex(encrypted_vendor); const byteString = hexChunks.map(hexByteToChar).join(''); const correctB64 = btoa(byteString); // 结果:"DK9kn+7klT2Hv5A6wRdsReAo3xY="
简化版实现
如果不想单独写拆分函数,也可以直接循环处理:
const encrypted_vendor = VENDOR.hmacSHA1("message", "secret"); let byteString = ''; for (let i = 0; i < encrypted_vendor.length; i += 2) { // 每次取两位十六进制,转成字节对应的字符 byteString += String.fromCharCode(parseInt(encrypted_vendor.substring(i, i + 2), 16)); } const correctB64 = btoa(byteString);
为什么这样能解决问题?
parseInt(hexByte, 16)把两位十六进制字符串转换成0-255之间的字节数值,这就是HMAC-SHA1计算后的原始字节值。String.fromCharCode()将字节数值转换成对应的Unicode字符(对于0-255的数值,这个字符和ASCII字符完全一致)。- 最终生成的
byteString完全对应CryptoJS内部的字节序列,所以btoa()能正确编码出和CryptoJS.enc.Base64.stringify()一致的结果。
内容的提问来源于stack exchange,提问作者Phil Sheard
相关产品推荐
相关产品推荐

