2

我在使用带有 Webcrypto 的 PBKDF2 时遇到问题。我想将 PBKDF2 与 SHA-1 算法一起使用。目前,我有,

const ENCODING = "utf-8";
const HMACSHA1 = {name: "HMAC", "hash" : "SHA-1"};
const PBKDF2SHA1 = {name: "PBKDF2", "hash": "SHA-1"};

// str2binb takes a string and outputs an ArrayBuffer

async function pbkdf2_generate_key_from_string(string) { //  Working
  return crypto.subtle.importKey(
    "raw",
    str2binb(string),
    PBKDF2SHA1,
    false,
    ["deriveKey", "deriveBits"],
  );
}

async function pbkdf2_derive_salted_key(key, salt, iterations) {  // Not working
  return crypto.subtle.deriveKey(
    {
      "name": "PBKDF2",
      "salt": salt,
      "iterations": iterations,
      "hash": "SHA-1",
      "length": 160
    },
    key,
    {
      "name": "HMAC"
      "hash": "SHA-1",
      "length": 160
    },
    true,
    [ "encrypt", "decrypt"]
  );
}

但是我知道我一定是用错了,因为它是受支持的密钥派生算法,并且根据 Mozilla 的精彩文档,derivedKeyAlgorithm支持 HMAC 以及 HMAC-SHA1。我也让它AES-GCM相当直接地工作。

当我尝试时,我收到的错误消息,

salt = b64binb("QSXCR+Q6sek8bf92"); // ArrayBuffer
key = await pbkdf2_generate_key_from_string("pencil");
x = await pbkdf2_derive_salted_key(key, salt, 4096)

Uncaught DOMException: Cannot create a key using the specified key usages.

注意:我了解不再推荐使用 SHA1,这是为了支持旧版。

注意2:当我将上面的内容替换为时,它可以工作,

async function pbkdf2_derive_salted_key(key, data, salt, iterations) {  // Not working
  return crypto.subtle.deriveKey(
    {
      "name": "PBKDF2",
      salt: salt,
      "iterations": iterations,
      "hash": "SHA-1",
    },
    key,
    {
      "name": "AES-GCM",
      "length": 256
    },
    true,
    [ "encrypt", "decrypt"]
  );
}

谢谢你。

4

1 回答 1

2

deriveKey中,参数keyUsages必须更改为["sign", "verify"]。然后代码工作(因为b64binb,str2binb未发布,以下代码使用适当的替代品):

const b64binb = base64String => Uint8Array.from(atob(base64String), c => c.charCodeAt(0));
const str2binb = str => new TextEncoder().encode(str);
const buf2hex = buffer => Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');

async function pbkdf2_generate_key_from_string(string) { 
    return crypto.subtle.importKey(
        "raw",
        str2binb(string),
        {
            name: "PBKDF2",
        },
        false,
        ["deriveKey", "deriveBits"], 
    );
}

async function pbkdf2_derive_salted_key(key, salt, iterations) {  
    return crypto.subtle.deriveKey(
        {
            name: "PBKDF2",
            salt: salt,
            iterations: iterations,
            hash: {name: "SHA-1"}
        },
        key,
        {
            name: "HMAC",
            hash: "SHA-1",
            length: 160
        },
        true,
        ["sign", "verify"] // <--------------------- Fix!
    );
}

async function test(){
    salt = b64binb("QSXCR+Q6sek8bf92"); // ArrayBuffer
    key = await pbkdf2_generate_key_from_string("pencil");
    x = await pbkdf2_derive_salted_key(key, salt, 4096)

    console.log(buf2hex(await window.crypto.subtle.exportKey("raw", x)));
}

test();

使用此密钥,window.crypto.subtle.sign使用 HMAC-SHA1 创建签名。

更新:

由于在SubtleCrypto中通常指定使用密钥,["encrypt", "decrypt"]因此必须将其keyUsages用作 AES-GCM 和["sign", "verify"]HMAC-SHA 的参数。

为什么["encrypt", "decrypt"]选择 AES-GCM?AES-GCM 用于加密/解密消息:AES描述块密码(允许加密单个块)和GCM操作模式(允许加密多个单个块)。GCM 提供机密性、真实性和完整性。

为什么["sign", "verify"]要使用 HMAC-SHA?HMAC 用于签名/验证消息:某些操作模式(例如CBC )仅提供机密性。为了另外提供真实性和完整性,可以用MAC(例如,HMAC,它是基于密码散列函数的特定类型的MAC,例如SHA家族之一)对消息进行签名。MAC 通常是根据密文而不是明文(Encrypt-then-MAC)计算的。关于 SHA1 漏洞,请参阅此处此处的 HMAC-SHA1 和 HMAC-SHA256 比较。

于 2020-05-09T11:45:26.297 回答