问题描述
我有使用CryptoJS.AES加密用户数据的代码,将密钥,iv和加密内容存储在不同的位置。 它还使用存储的密钥和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代码中,密钥作为字符串传递。因此,它被解释为密码,与随机生成的8字节盐一起从中得出32字节密钥和16字节IV,请参见here。专有(相对不安全)的OpenSSL密钥派生函数EVP_BytesToKey
用于此目的。
CryptoJS.AES.encrypt()
返回一个CipherParams
对象,该对象封装了各种参数,例如生成的密钥和IV为WordArray
,请参见here。 toString()
应用于键或IV WordArray
,返回十六进制编码的数据。 toString()
应用于CipherParams
对象,以OpenSSL格式返回密文,即,第一个块(=前16个字节)由Salted__
的ASCII编码组成,后跟8个字节盐和实际密文(均由Base64编码),请参见here。这意味着实际的密文从第二个块开始(在Base64解码之后)。
以下代码说明了如何使用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>