javascript - window.crypto 返回 352 位 key 而不是 256?

标签 javascript encryption window.crypto

我正在尝试使用 window.crypto 加密一些文本:

await crypto.subtle.encrypt(algorithm, key, dataArrayBuffer).catch(error => console.error(error));
但是我收到此错误 AES key data must be 128 or 256 bits .我正在使用 PBKDF2 从密码创建一个 256 位 key ,我指定的 key 长度为 256 :
window.crypto.subtle.deriveKey(
            {
                "name": "PBKDF2",
                "salt": salt,
                "iterations": iterations,
                "hash": hash
            },
            baseKey,
            {"name": "AES-GCM", "length": 256}, //<------------
            true, 
            ["encrypt", "decrypt"]
            );
但我最终得到了这个 key edi5Fou4yCdSdx3DX3Org+L2XFAsVdomVgpVqUGjJ1g=在我 exportKey 之后并将其从 ArrayBuffer 转换到 string长度为 44字节和 352位...
这可以解释错误,但我怎样才能创建一个实际的 256来自 window.crypto 的位 key 的PBKDF2 ?
JSFiddle:https://jsfiddle.net/6Lyaoudc/1/

最佳答案

显示的长度是指 Base64 编码的 key 。根据显示的值,32 字节 key 的 Base64 编码长度为 44 字节(352 位),因此是正确的,参见例如here .
错误在于第二个 importKey (请参阅从导入中为 AES-GCM 创建 CryptoKey 的注释)传递 Base64 编码 key (即 keyString )而不是二进制 key (即 ArrayBuffer keyBytes )。如果 keyBytes通过,加密工作:

function deriveAKey(password, salt, iterations, hash) {

    // First, create a PBKDF2 "key" containing the password
    window.crypto.subtle.importKey(
        "raw",
        stringToArrayBuffer(password),
        {"name": "PBKDF2"},
        false,
        ["deriveKey"]).
    then(function(baseKey){
        // Derive a key from the password
        return window.crypto.subtle.deriveKey(
            {
                "name": "PBKDF2",
                "salt": salt,
                "iterations": iterations,
                "hash": hash
            },
            baseKey,
            {"name": "AES-GCM", "length": 256}, // Key we want.Can be any AES algorithm ("AES-CTR", "AES-CBC", "AES-CMAC", "AES-GCM", "AES-CFB", "AES-KW", "ECDH", "DH", or "HMAC")
            true,                               // Extractable
            ["encrypt", "decrypt"]              // For new key
            );
    }).then(function(aesKey) {
        // Export it so we can display it
        return window.crypto.subtle.exportKey("raw", aesKey);
    }).then(async function(keyBytes) {
        // Display key in Base64 format
        var keyS = arrayBufferToString(keyBytes);
        var keyB64 = btoa (keyS);
        console.log(keyB64);
        
        console.log('Key byte size: ', byteCount(keyB64));
        console.log('Key bit size: ', byteCount(keyB64) * 8);
        
        var keyString = stringToArrayBuffer(keyB64);
    
        const iv = window.crypto.getRandomValues(new Uint8Array(12));

        const algorithm = {
          name: 'AES-GCM',
          iv: iv,
        };

        //Create CryptoKey for AES-GCM from import
        const key = await crypto.subtle.importKey(
          'raw',//Provided key will be of type ArrayBuffer
          // keyString,
          keyBytes,                                                    // 1. Use keyBytes instead of keyString
          {
            name: "AES-GCM",
          },
          false,//Key not extractable
          ['encrypt', 'decrypt']
        );

        //Convert data to ArrayBuffer
        var data = "The quick brown fox jumps over the lazy dog";      // 2. Define data
        const dataArrayBuffer = new TextEncoder().encode(data);

        const encryptedArrayBuffer = await crypto.subtle.encrypt(algorithm, key, dataArrayBuffer).catch(error => console.error(error));
        
        var ivB64 = btoa(arrayBufferToString(iv));
        console.log(ivB64);                                            // 3. Display (Base64 encoded) IV
        
        var encB64 = btoa(arrayBufferToString(encryptedArrayBuffer));
        console.log(encB64.replace(/(.{56})/g,'$1\n'));                // 4. Display (Base64 encoded) ciphertext (different for each encryption, because of random salt and IV)

    }).catch(function(err) {
        console.error("Key derivation failed: " + err.message);
    });
}

//Utility functions

function stringToArrayBuffer(byteString){
    var byteArray = new Uint8Array(byteString.length);
    for(var i=0; i < byteString.length; i++) {
        byteArray[i] = byteString.codePointAt(i);
    }
    return byteArray;
}

function  arrayBufferToString(buffer){
    var byteArray = new Uint8Array(buffer);
    var byteString = '';
    for(var i=0; i < byteArray.byteLength; i++) {
        byteString += String.fromCodePoint(byteArray[i]);
    }
    return byteString;
}

function byteCount(s) {
    return encodeURI(s).split(/%..|./).length - 1;
}

var salt = window.crypto.getRandomValues(new Uint8Array(16));
var iterations = 5000;
var hash = "SHA-512";
var password = "password";

deriveAKey(password, salt, iterations, hash);

除了 key ,代码还显示 IV 和密文,每个都是 Base64 编码的。可以验证密文,例如here使用 Base64 编码的 key 和 IV。

关于javascript - window.crypto 返回 352 位 key 而不是 256?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63312984/

相关文章:

javascript - 如果有 img 元素,则禁用复选框

java - 错误填充异常 : Given final block not properly padded

javascript - 检查字符串是否已被 HTML 转义 (JS)

javascript - 使用递归 promise 阻止内存泄漏

javascript - 单击图片打开文件上传对话框

java - SSL握手过程

python - AES 解密抛出 ValueError : Input strings must be a multiple of 16 in length

javascript - 如何在 IE11 中使用 TextEncoder?

javascript - 如何使用 Crypto Web API 获取 HMAC