Created
May 7, 2012 16:11
-
-
Save schakko/2628689 to your computer and use it in GitHub Desktop.
Using AES-256-CBC with OpenSSL, node.js and PHP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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); |
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"));
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:
- Openssl CLI memo#Key derivation
- Decrypt openssl AES 256 CBC in browser/CryptoJS#Answer 1
- How to decrypt using openssl EVP?
- Node.js Document
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
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