如何使用Crypto Web API获取HMAC

Ste*_*rev 9 javascript cryptography window.crypto

如何使用Crypto Web API(window.crypto)在浏览器中获取HMAC-SHA512(密钥,数据)?

目前我使用的是CryptoJS库,它非常简单:

CryptoJS.HmacSHA512("myawesomedata", "mysecretkey").toString();

结果是91c14b8d3bcd48be0488bfb8d96d52db6e5f07e5fc677ced2c12916dc87580961f422f9543c786eebfb5797bc3febf796b929efac5c83b4ec69228927f21a03a.

我想摆脱额外的依赖,并开始使用Crypto Web API.我怎样才能得到相同的结果?

Ste*_*lum 30

异步/等待加密微妙 HMAC SHA-256/512 与 Base64 摘要

\n

以下是 \xe2\x9c\x85 答案的副本。这次我们使用async/await干净的语法。此方法还提供 Base64 编码的摘要。

\n
    \n
  • secret是将用于签署 的密钥body
  • \n
  • body是要签名的字符串。
  • \n
  • enc是一个文本编码器,可将 UTF-8 转换为 JavaScript 字节数组。
  • \n
  • algorithm是一个JS对象,用于标识签名方法。
  • \n
  • key是一个CryptoKey
  • \n
  • signature是字节数组哈希。
  • \n
  • digest是base64编码的签名。
  • \n
\n

JavaScript 代码如下:

\n

\r\n
\r\n
(async ()=>{\n\'use strict\';\n\nlet secret = "sec-demo"; // the secret key\nlet enc = new TextEncoder("utf-8");\nlet body = "GET\\npub-demo\\n/v2/auth/grant/sub-key/sub-demo\\nauth=myAuthKey&g=1&target-uuid=user-1&timestamp=1595619509&ttl=300";\nlet algorithm = { name: "HMAC", hash: "SHA-256" };\n\nlet key = await crypto.subtle.importKey("raw", enc.encode(secret), algorithm, false, ["sign", "verify"]);\nlet signature = await crypto.subtle.sign(algorithm.name, key, enc.encode(body));\nlet digest = btoa(String.fromCharCode(...new Uint8Array(signature)));\n\nconsole.log(digest);\n\n})();
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

此页面上的原始答案对今天早些时候的调试工作很有帮助。我们使用它来帮助识别文档​​中创建签名以授予访问令牌以使用具有读/写权限的 API 的错误。

\n


Ste*_*rev 14

回答我自己的问题.下面的代码返回相同的结果CryptoJS.HmacSHA512("myawesomedata", "mysecretkey").toString();

由于WebCrypto是异步的,因此随处可见:

// encoder to convert string to Uint8Array
var enc = new TextEncoder("utf-8");

window.crypto.subtle.importKey(
    "raw", // raw format of the key - should be Uint8Array
    enc.encode("mysecretkey"),
    { // algorithm details
        name: "HMAC",
        hash: {name: "SHA-512"}
    },
    false, // export = false
    ["sign", "verify"] // what this key can do
).then( key => {
    window.crypto.subtle.sign(
        "HMAC",
        key,
        enc.encode("myawesomedata")
    ).then(signature => {
        var b = new Uint8Array(signature);
        var str = Array.prototype.map.call(b, x => ('00'+x.toString(16)).slice(-2)).join("")
        console.log(str);
    });
});
Run Code Online (Sandbox Code Playgroud)


ikh*_*vjs 7

不知怎的,@StephenBlum 的答案对我不起作用。

我将@StepanSnigirev 的答案重写为下面的异步。

"use strict";
(async () => {
    const secret = "mysecretkey";
    const enc = new TextEncoder();
    const body = "myawesomedata";
    const algorithm = { name: "HMAC", hash: "SHA-512" };

    const key = await crypto.subtle.importKey(
        "raw",
        enc.encode(secret),
        algorithm,
        false,
        ["sign", "verify"]
    );

    const signature = await crypto.subtle.sign(
        algorithm.name,
        key,
        enc.encode(body)
    );

    // convert buffer to byte array
    const hashArray = Array.from(new Uint8Array(signature));

    // convert bytes to hex string
    const digest = hashArray
        .map((b) => b.toString(16).padStart(2, "0"))
        .join("");

    console.log(digest);
})();
Run Code Online (Sandbox Code Playgroud)

注意:我们不能使用new Uint8Array(arrayBuffer).map(...). 虽然 Uint8Array 实现了 ArrayLike 接口,但它的 map 方法将返回另一个不能包含字符串(在我们的例子中为十六进制八位字节)的 Uint8Array,因此Array.fromhack

参考: https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array#instance_properties