Node.js 加密与 Google Apps 脚本 (GAS) 中的 HMAC

Posted

技术标签:

【中文标题】Node.js 加密与 Google Apps 脚本 (GAS) 中的 HMAC【英文标题】:HMAC in Node.js crypto vs. Google Apps Script (GAS) 【发布时间】:2015-05-21 09:26:18 【问题描述】:

谁能解释一下使用 Node.JS 的 Crypto 模块和 Google Apps Script 创建 HmacSha512 签名之间的区别?

代码 1 - Node.JS

var secret = "my secret";
var message = "message";
var crypto = require("crypto");
var hmac = new crypto.createHmac("sha512", secret);
var signature = hmac.update(message).digest("base64");
console.log(signature);

代码 1 - Google Apps 脚本

var secret = "my secret";
var message = "message";
var signature = Utilities.base64Encode(Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_512, message, secret));
Logger.log(signature);

两个代码生成相同的签名:

g4fZkM2XGNjhti9Wah3TU2/rvmxbL3nk4F3ZLljpED23oQ7Y7dtVmVKprQKuzyt0B4Spo214isWCvnoXXVTS8g==

但是当我们拥有 base64 编码密钥形式的秘密时,问题就来了。所以,我们要做的第一步是准备秘密。让我们修改代码:

代码 2 - Node.JS

var key = "JOLDQW5wVIdwvHbhSDCktxhfwpgtxlAH+DG5EPoeDT8aPGSDYYh5U6QjbASUhvztjGPgA/Ue2x8QKwUklX7+Xw==";
var secret = new Buffer(key, "base64");
var message = "message";
var crypto = require("crypto");
var hmac = new crypto.createHmac("sha512", secret);
var signature = hmac.update(message).digest("base64");
console.log(signature);

结果:

GELSKf33zit7nIfjj8XH3wZIga/CSYuCU5oTGysqOg6C/wFggunw59wzc7Mr95XW/gZ8putB67AADqnP0gLdiw==

代码 2 - Google Apps 脚本

var key = "JOLDQW5wVIdwvHbhSDCktxhfwpgtxlAH+DG5EPoeDT8aPGSDYYh5U6QjbASUhvztjGPgA/Ue2x8QKwUklX7+Xw==";
var message = "message";
var secret = Utilities.base64Decode(key);
var signature = Utilities.base64Encode(Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_512, message, secret));
Logger.log(signature);

结果:

l11xAQ5C5ARx/r/pbNYpMKCqWOwIaxFTkfS9OXCwfUxv33y3gU/sL2vHueOxpkCKmF+lxIcFMYblwrvfWaTZkg==

差异可能在于处理/解码密钥(缓冲区与 Utilities.base64Decode。Node.js 版本是正确的(已针对服务器端进行验证)。

如何使用 Google Apps 脚本正确解码和使用密钥?

【问题讨论】:

你解决过这个问题吗? 相信这家伙有答案:***.com/a/49267366/3346628 【参考方案1】:

Utilities.base64Decode() 返回一个字节数组而不是字符串。您可以从字节数组创建一个 blob,然后将其作为恢复原始编码字符串的字符串获取。

var secret = Utilities.base64Decode(key);
secret = Utilities.newBlob(secret).getDataAsString();

试试这个例子。我用新的 base64 编码字符串替换了您的密钥,并将 b64 解码的输出转换为字符串: 在节点中:

var key = "VEhJUyBJUyBBIFNUUklORw==";
//var key = "JOLDQW5wVIdwvHbhSDCktxhfwpgtxlAH+DG5EPoeDT8aPGSDYYh5U6QjbASUhvztjGPgA/Ue2x8QKwUklX7+Xw==";
var secret = new Buffer(key, "base64").toString();
var message = "message";
var crypto = require("crypto");
var hmac = new crypto.createHmac("sha512", secret);
var signature = hmac.update(message).digest("base64");
console.log(signature);

在气体中:

//var key = "JOLDQW5wVIdwvHbhSDCktxhfwpgtxlAH+DG5EPoeDT8aPGSDYYh5U6QjbASUhvztjGPgA/Ue2x8QKwUklX7+Xw==";
var key = "VEhJUyBJUyBBIFNUUklORw=="
var message = "message";
var secret = Utilities.base64Decode(key);
secret = Utilities.newBlob(secret).getDataAsString();
var hmac = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_512, message, secret);
var signature = Utilities.base64Encode(hmac);
Logger.log(signature);

都返回签名:

a9Jk2YQsKC164zEUoVChIpyfnEUZLj+Sj1mCAqs+jhDFvOliTupIfV+D6CNtaQGhQvAO40FZLhvYGubt1R5jQA==

但是如果我把你的钥匙重新放回签名中,就停止匹配。

【讨论】:

感谢@Spencer 的提议。我试图修改代码accrdingly:var key = "JOLDQW5wVIdwvHbhSDCktxhfwpgtxlAH+DG5EPoeDT8aPGSDYYh5U6QjbASUhvztjGPgA/Ue2x8QKwUklX7+Xw=="; var message = "message"; var secret = Utilities.newBlob(Utilities.base64Decode(key)).getDataAsString(); var signature = Utilities.base64Encode(Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_512, message, secret)); Logger.log(signature); 结果是:rbW61ipkR57jJOvFIhfeiAhBH/5cH9W2l/RtRGLCvFUeyl2iEN+yNnrubWjK4EcGyQkFbKpjhtCLgot/fV5LaQ== 因此,越来越多,除了Node.JS 生成...【参考方案2】:

其他发现:

当使用另一个键时,两个代码的结果是相同的 - 正如Spencer 所写。但是还有另一件奇怪的事情。让我们使用 Spencer 的密钥来确保这两个代码都能正常工作。当我们想用非 ASCII 字符对消息进行签名时,例如“Tomáš”,代码也会产生不同的结果!

【讨论】:

以上是关于Node.js 加密与 Google Apps 脚本 (GAS) 中的 HMAC的主要内容,如果未能解决你的问题,请参考以下文章

在 Node.js 中使用公钥加密数据

使用 HtmlService 使用 Google Apps 脚本上传文件

为啥通过 Google Apps 脚本向 Bigquery 加载的数据仅限于 513 行数据?

CryptoLocker - 使用 Google Apps 脚本恢复云端硬盘文件版本

Node.js - nodemon vs node - 开发与生产

将 Node.js 的 Express 与 vhost 一起使用时出现意外错误