Skip to content

Instantly share code, notes, and snippets.

@schakko
Created May 7, 2012 16:11
Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save schakko/2628689 to your computer and use it in GitHub Desktop.
Save schakko/2628689 to your computer and use it in GitHub Desktop.
Using AES-256-CBC with OpenSSL, node.js and PHP
// Doing AES-256-CBC (salted) decryption with node.js.
// This code is based on http://php.net/manual/de/function.openssl-decrypt.php and works with PHP sqAES.
//
// Create your encrypted data with
// echo -n 'Hello world' | openssl aes-256-cbc -a -e
var crypto = require('crypto');
var password = 'password';
var edata = 'U2FsdGVkX18M7K+pELP06c4d5gz7kLM1CcqJBbubW/Q=';
var data = new Buffer(edata, "base64");
console.log("Data (Base64): " + data );
var salt = data.toString("binary", 8, 16);
console.log("Salt (Base64): " + new Buffer(salt, "binary").toString("base64"));
var ct = data.toString("binary", 16);
console.log("Content (Base64): " + new Buffer(ct, "binary").toString("base64"));
var rounds = 3;
var data00 = password + salt;
console.log("Data00 (Base64): " + new Buffer(data00, "binary").toString("base64"));
md5_hash = new Array();
md5_hash[0] = crypto.createHash("md5").update(data00).digest("binary");
var result = md5_hash[0];
console.log("MD5-Hash[0] (Base64): " + new Buffer(result, "binary").toString("base64"));
for (i = 1; i < rounds; i++) {
md5_hash[i] = crypto.createHash("md5").update(md5_hash[i - 1] + data00).digest("binary");
result += md5_hash[i];
console.log("Result (Base64): " + new Buffer(result, "binary").toString("base64"));
}
key = result.substring(0, 32);
console.log("Key (Base64): " + new Buffer(key, "binary").toString("base64"));
var iv = result.substring(32, (32 + 16));
console.log("IV (Base64): " + new Buffer(iv, "binary").toString("base64"));
var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
var content = decipher.update(ct, "binary", "utf8");
content += decipher.final("utf8");
console.log("Decrypted: " + content);
<?php
// Doing AES-256-CBC (Salted) decryption with PHP
// This code is based on http://php.net/manual/de/function.openssl-decrypt.php and adds only some comments
//
// Create your encrypted data with
// echo -n 'Hello world' | openssl aes-256-cbc -a -e
$password = 'password';
$edata = 'U2FsdGVkX18M7K+pELP06c4d5gz7kLM1CcqJBbubW/Q=';
$data = base64_decode($edata);
print "Data: " . $data . "\n";
$salt = substr($data, 8, 8);
print "Salt (Base64): " . base64_encode($salt) . "\n";
$ct = substr($data, 16);
print "Content (Base64): " . base64_encode($ct) . "\n";
$rounds = 3;
$data00 = $password.$salt;
print "Data00 (Base64): " . base64_encode($data00) . "\n";
$md5_hash = array();
$md5_hash[0] = md5($data00, true);
$result = $md5_hash[0];
print "MD5-Hash[0] (Base64): " . base64_encode($result) . "\n";
for ($i = 1; $i < $rounds; $i++) {
$md5_hash[$i] = md5($md5_hash[$i - 1].$data00, true);
$result .= $md5_hash[$i];
print "Result (Base64): " . base64_encode($result) . "\n";
}
$key = substr($result, 0, 32);
print "Key (Base64): " . base64_encode($key) . "\n";
$iv = substr($result, 32, 16);
print "IV (Base64): " . base64_encode($iv) . "\n";
print "Decrypted: " . openssl_decrypt($ct, 'aes-256-cbc', $key, true, $iv);
@txt8888
Copy link

txt8888 commented Sep 4, 2016

could you also provide the node.js encrypt function that the openssl can decrypt as well? Thanks!

i.e.
encrypt-from-nodejs.js | openssl aes-256-cbc -base64 -a -d -k password

@dtiemann83
Copy link

You're a lifesaver, thanks dude.

@chengen
Copy link

chengen commented Jun 20, 2018

The code need to update:
hash.update(data[, inputEncoding])

Version Changes
v6.0.0 The default inputEncoding changed from binary to utf8.
v0.1.92 Added in: v0.1.92

After nodejs v6.0.0, the default encoding of Hash#update changed from 'binary' to 'utf8'.
When calculate the hash, encoding must be explicit as 'binary'.

md5_hash[0] = crypto.createHash("md5").update(data00).digest("binary");
==>
md5_hash[0] = crypto.createHash("md5").update(data00, 'binary').digest("binary");

@chengen
Copy link

chengen commented Jun 20, 2018

I also suggest the debug as hex not base64, so it's easier to debug with the openssl command line utils.

$ cat input.txt | openssl aes-256-cbc -a -salt -k hello -p -out input.txt.enc
salt=C97734D83EDAFD8D
key=67B99E14801776F828D1614328653BDD02A706EC74B772F362BB5517D2BE1B37
iv =07EAEDD9CC7E4577957FE314C589E361

@juliusza
Copy link

Original implementation no longer works. I've rewrote everything using modern JS:

"use strict";

// This will decrypt output of: echo -n 'Hello world' | openssl aes-256-cbc -a -e -k julius -p -md md5

const crypto = require("crypto");

function aesDecrypt(message, secret) {
    const TRANSFORM_ROUNDS = 3;
    const cypher = Buffer.from(message, "base64");
    const salt = cypher.slice(8, 16);
    const password = Buffer.concat([Buffer.from(secret, "binary"), salt]);
    const md5Hashes = [];

    let digest = password;

    for (let i = 0; i < TRANSFORM_ROUNDS; i++) {
        md5Hashes[i] = crypto.createHash("md5")
            .update(digest)
            .digest();

        digest = Buffer.concat([md5Hashes[i], password]);
    }

    const key = Buffer.concat([md5Hashes[0], md5Hashes[1]]);
    const iv = md5Hashes[2];
    const contents = cypher.slice(16);
    const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);

    return decipher.update(contents) + decipher.final();
}

console.log(aesDecrypt("U2FsdGVkX196b1K93xH6y0aSOTJmR5w9ofQDeDyIq3w=", "julius"));

@th-yoo
Copy link

th-yoo commented Dec 1, 2021

Thank you for showing me clues!
When I try echo -n 'Hello world' | openssl aes-256-cbc -a -e -k julius -p -md md5,
OpenSSL warned me:

*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.

The old manner has been deprecated since OpenSSL 1.1.1.
I looked into about pbkdf2.
And then RTFM (where is the fine manual?)
My references are as follow:

The result is:

'use strict';

const crypto = require('crypto');

// echo -n 'message to encrypt' | openssl aes-256-cbc -a -e -k <PASSPHRASE> -pbkdf2 -p
function dec(message, secret)
{
	const cypher = Buffer.from(message, "base64");
	// eat up 'Salted__'
	const salt = cypher.slice(8, 16);

	// iteration: "10000", hash algorithm: "sha256" by default
	// you can change the values with -iter and -md options
	// rv: key(aes256 key length 32-Byte) + iv(aes256 iv length 16-Byte)
	const key_iv = crypto.pbkdf2Sync(secret, salt, 10000, 32+16, 'sha256');

	const key = key_iv.slice(0,32);
	const iv = key_iv.slice(32);

	const contents = cypher.slice(16);
	const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);

	return decipher.update(contents) + decipher.final();
}

// echo -n '<CYPHERTEXT_IN_BASE64>' | node thiscode.js
if (require.main === module) {
	const buf = [];
	process.stdin.on('data', data => {
		buf.push(data);
	});
	process.stdin.on('end', () => {
		const cyphertext = buf.join('');
		const plaintext = dec(cyphertext, '<PASSPHRASE>');
		console.log(plaintext);
	});
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment