最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - how to properly decrypt text via Subtle Crypto which was encrypted via CryptoJS - Stack Overflow

programmeradmin3浏览0评论

I have code that encrypts user's data using CryptoJS.AES, stores key, iv and encrypted content in different places. Also it decrypts encrypted content using stored key and iv by user demand.

I want to use Subtle Crypto browser API for encryption,which is done.

But I also want to have possibility to decrypt old data (that was ecnrypted using CryptoJS.AES) using Subtle Crypto.

old data was generated with following code

  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()
  };

and I've tried to decrypt it as following

  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);

I receive DOMException error without backtrace on await crypto.subtle.decrypt

plete reproduction can be found at =/src/index.js

I have code that encrypts user's data using CryptoJS.AES, stores key, iv and encrypted content in different places. Also it decrypts encrypted content using stored key and iv by user demand.

I want to use Subtle Crypto browser API for encryption,which is done.

But I also want to have possibility to decrypt old data (that was ecnrypted using CryptoJS.AES) using Subtle Crypto.

old data was generated with following code

  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()
  };

and I've tried to decrypt it as following

  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);

I receive DOMException error without backtrace on await crypto.subtle.decrypt

plete reproduction can be found at https://codesandbox.io/s/crypto-js-to-subtle-crypto-u0pgs?file=/src/index.js

Share edited Sep 25, 2020 at 16:34 Senid asked Sep 25, 2020 at 16:16 SenidSenid 5826 silver badges14 bronze badges 1
  • CryptoJS said that it uses AES-CBC with 256-bit key and PKCS7 padding, and I've used same values for crypto.subtle but decryption still fail – Senid Commented Sep 25, 2020 at 16:18
Add a ment  | 

2 Answers 2

Reset to default 4

In the CryptoJS code the key is passed as string. Therefore it is interpreted as a password, from which in bination with a randomly generated 8 bytes salt, a 32 bytes key and a 16 bytes IV are derived, see here. The proprietary (and relatively insecure) OpenSSL key derivation function EVP_BytesToKey is used for this.

CryptoJS.AES.encrypt() returns a CipherParams object that encapsulates various parameters, such as the generated key and IV as WordArray, see here. toString() applied to the key or IV WordArray, returns the data hex encoded. toString() applied to the CipherParams object, returns the ciphertext in OpenSSL format, i.e. the first block (= the first 16 bytes) consists of the ASCII encoding of Salted__, followed by the 8 bytes salt and the actual ciphertext, all together Base64 encoded, see here. This means that the actual ciphertext starts (after Base64 decoding) with the second block.

The following code illustrates how the ciphertext generated with CryptoJS can be decrypted with the WebCrypto API

//
// 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./a/50868276
const fromHex = hexString => new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
// https://stackoverflow./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./ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

It is also possible to remove CryptoJS and have everything done by crypto.subtle. (This code, password, and salt are from another answer, which explains why the first byte is ignored.)

(async function subtle() {
    const encryptedDataB64 = "EAAAACMtkB64He4p/MSI+yF2A2rJWhxpssG6b48Z01JrfbErvQ1r6Gi0esgCmdrBaFxOPFF1+AUsyrUUl5FQ4Nk0dSU=";
    const passString = 'D2s1d_5$_t0t3||y_4c3$0m3!1!1!!';
    const saltString = 'o6805542kcM7c5';
    const encoder = new TextEncoder();
    const passHex = encoder.encode(passString);
    const saltHex = encoder.encode(saltString);
    const passKey = await crypto.subtle.importKey('raw',passHex,'PBKDF2',false,['deriveKey']);
    const pbkdfParams = {
        'name': 'PBKDF2',
        'hash': 'SHA-1',
        'salt': saltHex,
        'iterations': 1000
    };
    const passCrypto = await crypto.subtle.deriveKey(pbkdfParams,passKey,{'name':'AES-CBC','length':256},false,['decrypt']);
    const encryptedData = Uint8Array.from(atob(encryptedDataB64), c => c.charCodeAt(0));
    const iv = encryptedData.slice(4,20);
    const ciphertext = encryptedData.slice(20);
    const decrypted = await crypto.subtle.decrypt({'name':'AES-CBC','iv':iv},passCrypto,ciphertext);
    const decryptedText = new TextDecoder().decode(decrypted);
    console.log(decryptedText);
})();

发布评论

评论列表(0)

  1. 暂无评论