Node.js encrypts large file using AES - javascript

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

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!');
});

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 => {/* ... */});

I would simply use the Fileger to do it. It's a promise-based package and a clean alternative to the NodeJS filesystem.
const fileger = require("fileger")
const file = new fileger.File("./your-file-path.txt");
file.encrypt("your-password") // this will encrypt the file
.then(() => {
file.decrypt("your-password") // this will decrypt the file
})

Related

I need to replicate this from Java to JavaScript (NodeJs)

I try to get the same functionality of java class to replicate in NodeJs, but I can't do it. I don't understand all the JavaCode, specialy when the instance sing() (what's return). for that can't replicate into NodeJs
Java Code
public String generateSignature(String key, String stringToSign) throws Exception {
Signature signatureInstance = Signature.getInstance("SHA256WithRSA");
ServiceKeyRep keyRep = new ServiceKeyRep(KeyRep.Type.PRIVATE, "RSA", "PKCS#8", Base64.getDecoder().decode(key));
PrivateKey resolvedPrivateKey = (PrivateKey) keyRep.readResolve();
signatureInstance.initSign(resolvedPrivateKey);
byte[] bytesToSign = stringToSign.getBytes("UTF-8");
signatureInstance.update(bytesToSign);
String signatureString = Base64.getEncoder().encodeToString(signatureBytes);
return signatureString;
}
And I try this on Node JS
const privateKey = fs.readFileSync('myKey.pem', 'utf-8');
const stringToSign = new Int8Array([ myData ]);
const data = Buffer.from(stringToSign);
const sign = crypto.sign("RSA-SHA256", data , privateKey);
const signature = sign.toString('base64');
//Printing the signature
console.log(`Signature:\n\n ${signature}`);
but when I print, the out is not the same
Using crypto library we can do something similar in JS. Adapt as appropriate, this is just a reference.
const fs = require('fs');
const crypto = require('crypto');
function generateSignature(key, stringToSign) {
const privateKey = fs.readFileSync(key, 'utf-8');
const sign = crypto.createSign('RSA-SHA256');
sign.update(stringToSign);
const signature = sign.sign(privateKey, 'base64');
return signature;
}
const key = 'myKey.pem';
const stringToSign = 'myData';
const signature = generateSignature(key, stringToSign);
console.log(`Signature:\n\n${signature}`);

How to use Node JS to decrypt a file that was encrypted using C#

I have files encrypted in C# (using DES, PKCS7). I need to decode these files in Node JS.
The code I use to decrypt in C# looks like this:
public string SSFile_Reader( string fileToDecrypt )
{
DESCryptoServiceProvider provider = new DESCryptoServiceProvider
{
Key = Encoding.UTF8.GetBytes( "13^07v90" ),
IV = Encoding.UTF8.GetBytes( "13^07v90" ),
Padding = PaddingMode.PKCS7
};
using( FileStream streamToDecrypt = new FileStream( fileToDecrypt, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ) )
{
ICryptoTransform cryptoTransform = provider.CreateDecryptor();
string outputString = "";
using( CryptoStream stream2 = new CryptoStream( streamToDecrypt, cryptoTransform, CryptoStreamMode.Read ) )
{
try
{
using( StreamReader reader = new StreamReader( stream2 ) )
{
try
{
outputString = reader.ReadToEnd();
}
catch
{
//handle error here
}
stream2.Close();
streamToDecrypt.Close();
return outputString;
}
}
catch( Exception exception )
{
//handle error here
}
}
}
return '';
}
I literally need to convert the above to Node JS. I have tried the Node JS code below, but the output is just some random stuff rather than the original encrypted string:
const { Readable } = require("stream");
const { scrypt, scryptSync, randomFill, createCipheriv, createDecipheriv } = require('crypto');
const fs = require('fs');
const [, , pathToEncryptedFile] = process.argv;
if (!pathToEncryptedFile) {
console.log('No file to decrypt')
exit()
}
const keyAndIv = '31335e3037763930'; //hex equivalence of 13^07v90
const key = Buffer.from(keyAndIv, 'hex');
const iv = Buffer.from(keyAndIv, 'hex');
const decryptedData = '';
const decipher = createDecipheriv('des', key, iv);
const readableStream = Readable.from(fs.createReadStream(pathToEncryptedFile)
.pipe(decipher));
readableStream.on("data", (chunk) => {
decryptedData += chunk.toString()
})
readableStream.on('end', function () {
console.log({decryptedData})
});
readableStream.on('error', function (err) {
console.log({err})
});
I also tried using crypto-js to no avail (https://github.com/brix/crypto-js/issues/396).
This is an example of one of the files I need to decrypt: https://files.fm/u/6pewftkk2
I can also give the C# code that does the encryption if the C# code given above for the decryption does not suffice
One possible variant is to load the ciphertext completely from the file system and decrypt it:
var crypto = require('crypto')
var fs = require('fs')
const algorithm = 'des'; // defaults to 'des-cbc';
const password = 'Password used to generate key';
const key = '13^07v90';
const iv = '13^07v90';
const ciphertext = fs.readFileSync('<path to high_classes.ssdata>');
const decipher = crypto.createDecipheriv(algorithm, key, iv);
const plaintext = decipher.update(ciphertext, '', 'utf8') + decipher.final();
console.log(plaintext);
which outputs the following plaintext (for the linked file):
SSS1
SSS2
SSS3
Alternatively, especially for large data, the plaintext can also be streamed to a file. To do this, replace the last block with:
const decipher = crypto.createDecipheriv(algorithm, key, iv);
const readStream = fs.createReadStream('<path to high_classes.ssdata>');
const writeStream = fs.createWriteStream('<path to file where decrypted data should be saved>');
readStream.pipe(decipher).pipe(writeStream);
which creates a file containing the decrypted data.
Note that DES is outdated and insecure these days. Using the key as an IV is also insecure. Typically, a random IV is generated during encryption and passed to the other side along with the ciphertext (usually concatenated).

How to convert this signature method from crypto (node) to crypto-js (browser)?

I have a signature method that is meant to be used in Node.js but I'd like to implement it client-side with crypto-js. It should work in latest Chrome versions.
I have tried to follow some answers like this one: Decode a Base64 String using CryptoJS
But I either get errors such as "Error: Malformed UTF-8 data", or a different result than the expected hmacDigest.
I am not sure how I could find an alternative to the "binary" digest although I found this question:
How to get digest representation of CryptoJS.HmacSHA256 in JS
The method is supposed to answer the following:
"Message signature using HMAC-SHA512 of (URI path + SHA256(nonce + POST data)) and base64 decoded secret API key"
This is the Nodejs version (with crypto):
const crypto = require('crypto')
function sign(path, params, secret) {
const message = querystring.stringify(params)
const secretBase64 = Buffer.from(secret, 'base64')
const hash = crypto.createHash('sha256')
const hmac = crypto.createHmac('sha512', secretBase64)
const hashDigest = hash.update(params.nonce + message).digest('binary')
const hmacDigest = hmac.update(path + hashDigest, 'binary').digest('base64')
return hmacDigest
}
note: querystring is just an helper module that can also run in browsers: https://nodejs.org/api/querystring.html
This is my attempt (wip) at implementing with crypto-js:
import cryptojs from 'crypto-js')
function sign (path, params, secret) {
const message = querystring.stringify(params)
const secretParsed = cryptojs.enc.Base64.parse(secret)
const secretDecoded = cryptojs.enc.Utf8.stringify(secretParsed) // -> this throws an error as "Error: Malformed UTF-8 data"
const hash = cryptojs.SHA256(params.nonce + message).toString(cryptojs.enc.hex)
const hmac = cryptojs.HmacSHA512(path + hash, secretDecoded).toString(cryptojs.enc.hex)
return hmac
}
Try this ! I think this is what you looking for !
const crypto = require("crypto")
const sign = (path, params, secret) => {
const message = querystring.stringify(params)
const secret = Buffer.from(secret, 'base64')
let sign = params.nonce + message
let hash = crypto.createHmac('sha256', secret)
.update(sign)
.digest("base64").toString()
let encoded = encodeURIComponent(hash)
return encoded
}

Remove or change original data from stream Nodejs

I have a code to write a hash to the file from text of another file, but the problem is that in resulting file is written not only hash, but also the original text.
For example: if content of source file qwerty a got in result file qwertyd8578edf8458ce06fbc5bb76a58c5ca4, but i need just d8578edf8458ce06fbc5bb76a58c5ca4.
const fs = require('fs');
const crypto = require('crypto');
const hash = crypto.createHash('MD5');
const readData = fs.createReadStream('./task1/input.txt');
const writeData = fs.createWriteStream('./task1/output.txt');
readData.on('data', (chunk) => {
hash.update(chunk);
});
readData.on('end', () => {
const resultHash = hash.digest('hex');
writeData.end(resultHash);
console.log(resultHash);
});
readData.pipe(writeData);
How i can fix this? Thanks.
If you want to hash a stream, thats super easy as hash is itself a stream ( a Transform stream). Just pipe your input into it, and pipe the resulting hash into your output:
const fs = require('fs');
const crypto = require('crypto');
const hash = crypto.createHash('MD5');
const readData = fs.createReadStream('./task1/input.txt');
const writeData = fs.createWriteStream('./task1/output.txt');
readData.pipe(hash).pipe(writeData);
Reference

node.js fs.writeFileSync() How to set encoding to big5?

the fs.writeFileSync encode default is UTF 8
I can't set the encode to big5.
The documentation does not mention those encoding support.
If this function does not support BIG5, what can I do?
var fs = require('fs');
var FilePath='./text.txt';
var Str='this is a test!';
var encode='utf8';
fs.writeFileSync(FilePath, Str, encode);
When I set encoding(var encode='big5';) BIG5, the server generates an error.
To use an encoding that isn't standard with Node Core. You can use iconv-lite.
It adds support for additional encodings including big5, here is the full list of encodings.
const iconv = require('iconv-lite');
const fs = require('fs');
const stream = require('stream');
var Str = iconv.encode('This is a test', 'big5');
var readStream = new stream.PassThrough();
var writeStream = fs.createWriteStream('./text.txt');
readStream.once('error', (err) => { console.log(err); });
readStream.once('end', () => { console.log('File Written'); });
readStream.end(Str); // write data to stream
readStream.pipe(writeStream); // pipe data to file

Categories

Resources