Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js encrypts large file using AES

I try to use following code to encrypt a file of 1 GB. But Node.js abort with "FATAL ERROR: JS Allocation failed - process out of memory". How can I deal with it?

var fs = require('fs');
var crypto = require('crypto');
var key = "14189dc35ae35e75ff31d7502e245cd9bc7803838fbfd5c773cdcd79b8a28bbd";
var cipher = crypto.createCipher('aes-256-cbc', key);
var file_cipher = "";
var f = fs.ReadStream("test.txt");
f.on('data', function(d) {
    file_cipher = file_cipher + cipher.update(d, 'utf8', 'hex');
});
f.on('end', function() {  
    file_cipher = file_cipher + cipher.final('hex');
});   
like image 838
Jan Leo Avatar asked Dec 07 '14 17:12

Jan Leo


2 Answers

You could write the encrypted file back to disk instead of buffering the entire thing in memory:

var fs = require('fs');
var crypto = require('crypto');

var key = '14189dc35ae35e75ff31d7502e245cd9bc7803838fbfd5c773cdcd79b8a28bbd';
var cipher = crypto.createCipher('aes-256-cbc', key);
var input = fs.createReadStream('test.txt');
var output = fs.createWriteStream('test.txt.enc');

input.pipe(cipher).pipe(output);

output.on('finish', function() {
  console.log('Encrypted file written to disk!');
});
like image 136
mscdex Avatar answered Oct 03 '22 07:10

mscdex


crypto.createCipher() without initialization vector is deprecated since NodeJS v10.0.0 use crypto.createCipheriv() instead.

You can also pipe streams using stream.pipeline() instead of pipe method and then promisify it (so the code will easily fit into promise-like and async/await flow).

const {createReadStream, createWriteStream} = require('fs');
const {pipeline} = require('stream');
const {randomBytes, createCipheriv} = require('crypto');
const {promisify} = require('util');

const key = randomBytes(32); // ... replace with your key
const iv = randomBytes(16); // ... replace with your initialization vector

promisify(pipeline)(
        createReadStream('./text.txt'),
        createCipheriv('aes-256-cbc', key, iv),
        createWriteStream('./text.txt.enc')
)
.then(() => {/* ... */})
.catch(err => {/* ... */});

With NodeJS 15+ you could simplify it (skip promisify part)

const {createReadStream, createWriteStream} = require('fs');
const {pipeline} = require('stream/promises');
const {randomBytes, createCipheriv} = require('crypto');

const key = randomBytes(32); // ... replace with your key
const iv = randomBytes(16); // ... replace with your initialization vector

pipeline(
  createReadStream('./text.txt'),
  createCipheriv('aes-256-cbc', key, iv),
  createWriteStream('./text.txt.enc')
)
.then(() => {/* ... */})
.catch(err => {/* ... */});
like image 34
Ihor Sakailiuk Avatar answered Oct 03 '22 07:10

Ihor Sakailiuk