Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why can't i decrypt a file with NodeJS that i encrypted with openssl?

i encrypted a file on the command line using

openssl aes-256-cbc -in /tmp/text.txt -out /tmp/text.crypt

i then tried to decrypt it using the following JavaScript code:

crypto        = require( 'crypto' );
cipher_name   = 'aes-256-cbc';
password      = '*';
decoder       = crypto.createDecipher( cipher_name, password );
text_crypt    = njs_fs.readFileSync( '/tmp/text.crypt' );
chunks        = [];
chunks.push decoder.update( text_crypt, 'binary' );
chunks.push decoder.final( 'binary' );
text          = chunks.join( '' ).toString( 'utf-8' );

this fails with

TypeError: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt

what am i doing wrong?

like image 947
flow Avatar asked Nov 12 '13 19:11

flow


1 Answers

Cryptography is fun. Here is the code that decrypts file encrypted with openssl with salt.

var crypto = require('crypto');

function md5(data) {
    var hash = crypto.createHash('md5');
    hash.update(data);
    return new Buffer(hash.digest('hex'), 'hex');
}

var text = require('fs').readFileSync('text.crypt');
var salt = text.slice(8, 16);
var cryptotext = text.slice(16);
var password = new Buffer('*');

var hash0 = new Buffer('');
var hash1 = md5(Buffer.concat([ hash0, password, salt ]));
var hash2 = md5(Buffer.concat([ hash1, password, salt ]));
var hash3 = md5(Buffer.concat([ hash2, password, salt ]));
var key = Buffer.concat([ hash1, hash2 ]);
var iv = hash3;

var decoder = crypto.createDecipheriv('aes-256-cbc', key, iv);

var chunks = [];
chunks.push(decoder.update(cryptotext, "binary", "utf8"));
chunks.push(decoder.final("utf8"));
console.log(chunks.join(''));

Update: more details on what is cbc mode and how openssl works

If you look on how the stream ciphers in cipher-block chaining mode work you will notice that two initial values are required for the cipher to start encrypting the data: initialization vector (iv) and the key. It is important that size of the initialization vector should be equal to the block size and the key size depends on the algorithm, for AES-256 it is 256-bit long.

But users do not want to set up 256-bit random passwords to access their data. This opens a question on how to construct the key and iv from the user's input and openssl solves it by applying EVP_BytesToKey function to the user input, which is practically MD5 applied to the password and salt multiple times.

You can see the derived values by executing

C:\Tools\wget>openssl enc -aes-256-cbc -P
enter aes-256-cbc encryption password:
Verifying - enter aes-256-cbc encryption password:
salt=A94B7976B2534923
key=C8B806C86E60ED664B9C369628D1A78260753580D78D09EAEC04EAC1535077C3
iv =7B6FB26EB62C34F04F254A0C4F4F502A

The parameters "key and "iv" here are the input parameters to the cipher and the salt is required to randomize the cipher-text so it will not be the same for the same data.

The openssl saves the data in the file as follows:

Saltet__;[salt][cipher-text]

So to decrypt it the following steps should be made:

  1. the "Salted" prefix should be skipped
  2. 8 bytes of the input should be read and saved as salt
  3. from the password and salt the key and iv should be constructed
  4. the rest of the file should be decrypted by applying AES-256-CBC decryptor with calculated key and iv

The code above performs these steps and decrypts the file.

like image 68
Alex Netkachov Avatar answered Oct 30 '22 16:10

Alex Netkachov