$ echo 'this is hello world' | openssl aes-256-cbc -a -nosalt -k hello
HEQ/s/mOMof648tJxJvvwtHUTcq2j021RbgvqLA02lY=
-a means encoding the output using base64
-nosalt force openssl do encryption without salt
-k the encryption key
Decrypt the encrypted data by add one more option -d $ echo 'HEQ/s/mOMof648tJxJvvwtHUTcq2j021RbgvqLA02lY=' | openssl aes-256-cbc -a -nosalt -d -k hello this is hello world -d meas decryption
Your can also use openssl encrypt files by passing the -in -out params. without -k option, it will prompt for a password.
openssl aes-256-cbc -a -nosalt -in input.txt -out input.txt.enc
openssl aes-256-cbc -a -nosalt -d -in input.txt.enc -out output.txt
Let's play it one more time, the output is exactly the same as the previous one. This is because we turned off the salt.
$ echo 'this is hello world' | openssl aes-256-cbc -a -nosalt -k hello
HEQ/s/mOMof648tJxJvvwtHUTcq2j021RbgvqLA02lY=
$ echo -n 'this is hello world' | openssl aes-256-cbc -a -salt -k hello
U2FsdGVkX18LU4xDbMT+0er0+CFcEj/wzV6FXH68PxNP8EParh7jcV3vO2eKojVp
Just change the -nosalt to -salt.
Let's play it one more time.
echo -n 'this is hello world' | openssl aes-256-cbc -a -salt -k hello
U2FsdGVkX1/iePtEvUa0dDSZwrzRKtDbpIsbqEg8ozevyE/AUR8eL65Nvdn73D3G
Each time we encrypt with salt will generate different output.
-salt meas openssl will generate 8 byte length random data, combine the password as the final key. So each time the encrypt will generate different output.
$ echo 'U2FsdGVkX1/iePtEvUa0dDSZwrzRKtDbpIsbqEg8ozevyE/AUR8eL65Nvdn73D3G' | openssl aes-256-cbc -a -salt -d -k hello
this is hello world
The same as encryption by add -d option.
Add -p option the checkout what did openssl do while encryption:
$ echo -n 'this is hello world' | openssl aes-256-cbc -a -salt -k hello -p
salt=E2FA0A8D6FFB9FBB
key=5D1913778B7B6E509877D6F320B6ABA185BA53332BDCB5E5C2498FE8B6BC9E01
iv =421C6E9DF6BA9D0C10DDA4AFB2ABCA4D
U2FsdGVkX1/i+gqNb/ufu1sSyqfpUvW6Idw7+QTBOvGjIu5Lz0/wuRLf42y9/xV2
- It first generate an 8-byte long salt;
- By concating the password and salt, it generate the key(32 byte length) and iv(16 byte length)
- Then encrypt the data with key and iv using standard aes-255-cbc algorigthm;
So what's algorithm used for generating the key and iv? From openssl docs: https://www.openssl.org/docs/manmaster/man3/EVP_BytesToKey.html It simply using md5 of the salt and password. md5 generate 16-byte data one time. but the key(32-byte) and iv(16-byte) totally need 48-byte data. So we need to run md5 at least 48/16 = 3 time.
ps: Why key is 32-byte length and iv for 16-byte length?
- aes-256-cbc, 256 meas it use 256 bit key, that's 32-byte.
- so aes-192-cbc use 24-byte key;
- aes-128-cbc use 16-byte key.
- iv is always 16-byte.
base = password + salt
md5_1 = md5(base);
md5_2 = md5(md5_1+base);
md5_3 = md5(md5_2+base);
resut = md5_1 + md5_2 + md5_3
key = result.substr(0, 32); //the first 32 byte as key
iv = result.substr(32, 16); //the next 16 byte as iv.
$ echo 'U2FsdGVkX1/i+gqNb/ufu1sSyqfpUvW6Idw7+QTBOvGjIu5Lz0/wuRLf42y9/xV2' | base64 -D | od -t x1
0000000 53 61 6c 74 65 64 5f 5f e2 fa 0a 8d 6f fb 9f bb
0000020 5b 12 ca a7 e9 52 f5 ba 21 dc 3b f9 04 c1 3a f1
0000040 a3 22 ee 4b cf 4f f0 b9 12 df e3 6c bd ff 15 76
0000060
or..
$ echo 'U2FsdGVkX1/i+gqNb/ufu1sSyqfpUvW6Idw7+QTBOvGjIu5Lz0/wuRLf42y9/xV2' | base64 -D | xxd
00000000: 5361 6c74 6564 5f5f e2fa 0a8d 6ffb 9fbb Salted__....o...
00000010: 5b12 caa7 e952 f5ba 21dc 3bf9 04c1 3af1 [....R..!.;...:.
00000020: a322 ee4b cf4f f0b9 12df e36c bdff 1576 .".K.O.....l...v
The first 8-byte of encrypted data is 'Salted__', which meas the data was encrypted using salt. The next 8-byte is the salt, which is exactly the same as openssl -p output.
salt=E2FA0A8D6FFB9FBB
The left bytes are the cncryped data.
const fs = require('fs');
const crypto = require('crypto');
var password = Buffer.from('hello', 'utf8');
//var input = fs.readFileSync('input.txt.enc', 'ascii');
var input = 'U2FsdGVkX1/i+gqNb/ufu1sSyqfpUvW6Idw7+QTBOvGjIu5Lz0/wuRLf42y9/xV2';
var buf = Buffer.from(input, 'base64'); //decode base64 string to buffer
console.log('buf', buf.toString('hex'));
var salt = buf.slice(8, 16); //8 byte salt
console.log('salt', salt.toString('hex'));
var base = Buffer.concat([password, salt]); //concat the password and salt to generate key and iv
var hash = [];
hash[0] = crypto.createHash('md5').update(base).digest();
hash[1] = crypto.createHash('md5').update(Buffer.concat([hash[0], base])).digest();
hash[2] = crypto.createHash('md5').update(Buffer.concat([hash[1], base])).digest();
var result = Buffer.concat(hash); //concat 3 md5 to get a 48-byte length data.
var key = result.slice(0, 32);
var iv = result.slice(32);
console.log('key', key.toString('hex'));
console.log('iv', iv.toString('hex'));
var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
var content = buf.slice(16);
var output = decipher.update(content, null, 'utf8');
output += decipher.final('utf8');
console.log('output', output);
just to add something here
openssl enc -d -aes256 -k "<hex_key>"
will automatically decrypt aes256 encrypted string containing salt