javascript - 如何通过 Subtle Crypto 正确解密通过 CryptoJS 加密的文本

标签 javascript encryption cryptography aes cryptojs

我有使用 CryptoJS.AES 加密用户数据的代码,在不同的地方存储 key 、iv 和加密内容。 它还根据用户需求使用存储的 key 和 iv 解密加密内容。

我想使用 Subtle Crypto 浏览器 API 进行加密,已完成。

但我也希望能够使用 Subtle Crypto 解密旧数据(使用 CryptoJS.AES 加密)。

旧数据是使用以下代码生成的

  var CryptoJS = require("crypto-js/core");
  CryptoJS.AES = require("crypto-js/aes");

  let encKey = generateRandomString();
  let aesEncrypted = CryptoJS.AES.encrypt(content, encKey);
  let encrypted = {
    key: aesEncrypted.key.toString(),
    iv: aesEncrypted.iv.toString(),
    content: aesEncrypted.toString()
  };

我尝试按如下方式解密它

  let keyArrayBuffer = hexArrayToArrayBuffer(sliceArray(encrypted.key, 2));
  let decKey = await importKey(keyArrayBuffer);
  let decIv = hexArrayToArrayBuffer(sliceArray(encrypted.iv, 2));
  let encContent = stringToArrayBuffer(encrypted.content);
  let decryptedByteArray = await crypto.subtle.decrypt(
    { name: "AES-CBC", iv: decIv },
    decKey,
    encContent
  );
  let decrypted = new TextDecoder().decode(decrypted);

我收到DOMException await crypto.subtle.decrypt 上没有回溯的错误

完整的复制品可以在 https://codesandbox.io/s/crypto-js-to-subtle-crypto-u0pgs?file=/src/index.js 找到

最佳答案

在 CryptoJS 代码中, key 作为字符串传递。因此,它被解释为密码,结合随机生成的 8 字节盐,派生出 32 字节 key 和 16 字节 IV,请参阅 here 。专有的(且相对不安全的)OpenSSL key 派生函数 EVP_BytesToKey用于此。

CryptoJS.AES.encrypt() 返回一个 CipherParams 对象,该对象封装了各种参数,例如生成的 key 和 IV 作为 WordArray,请参阅here 。应用于键或 IV WordArraytoString(),返回十六进制编码的数据。 toString() 应用于 CipherParams 对象,返回 OpenSSL 格式的密文,即第一个 block (= 前 16 个字节)由 的 ASCII 编码组成Salted__,后面是 8 字节盐和实际密文,全部经过 Base64 编码,请参阅 here 。这意味着实际的密文(在 Base64 解码后)从第二个 block 开始。

以下代码说明了如何使用 WebCrypto API 解密使用 CryptoJS 生成的密文

//
// CryptoJS
//
const content = "The quick brown fox jumps over the lazy dog";
const encKey = "This is my passphrase";

const aesEncrypted = CryptoJS.AES.encrypt(content, encKey);
const encrypted = {
    key: aesEncrypted.key.toString(),
    iv: aesEncrypted.iv.toString(),
    content: aesEncrypted.toString()
};

//
// WebCrypto API
// 
// https://stackoverflow.com/a/50868276
const fromHex = hexString => new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
// https://stackoverflow.com/a/41106346
const fromBase64 = base64String => Uint8Array.from(atob(base64String), c => c.charCodeAt(0));
 
async function decryptDemo(){
    
    const rawKey = fromHex(encrypted.key);
    const iv = fromHex(encrypted.iv);
    const ciphertext = fromBase64(encrypted.content).slice(16);
        
    const key = await window.crypto.subtle.importKey(           
        "raw",
        rawKey,                                                 
        "AES-CBC",
        true,
        ["encrypt", "decrypt"]
    );

    const decrypted = await window.crypto.subtle.decrypt(
        {
            name: "AES-CBC",
            iv: iv
        },
        key,
        ciphertext
    );

    const decoder = new TextDecoder();
    const plaintext = decoder.decode(decrypted);
    console.log(plaintext);     
}
    
decryptDemo();
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

关于javascript - 如何通过 Subtle Crypto 正确解密通过 CryptoJS 加密的文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64067812/

相关文章:

java - 讨论: Encryption for Password Keeper Android application

Android - Tomcat - MySQL 困惑,这样的应用设计是否可行?

c# - 生成具有指定位长度的盐

javascript - 通过 Javascript 上传 HTML 文件预览

javascript - 使用 DES (Node.js) 将数据传递到 Cipher.update 时丢失数据

javascript - 单击浏览器后退按钮运行动画

security - 我在哪里可以了解经过验证的共享加密 key 的方法?

javax.crypto.BadPaddingException : Decryption error when using Java RSA encryption

php - 带有 javascript 控件的 Flash MP3 播放器

javascript - jquery倒计时年月日