I'm currently implementing encryption on my API. I have to implement this the same way it is encrypted and decrypted in a specific script written in PHP.
This works all fine when I output the result in, let's say, base64. The PHP decrypt method runs against it and works just fine. The problem is that I need to output not in base64 but in binary due to certain requirements, but when I encrypt from the Nodejs side into binary the result is different than when I encrypt in the PHP into binary when it should be the same.
PHP Encryption:
function vd_encrypt($plaintext, $password) {
$method = "AES-256-CBC";
$key = hash('sha256', $password, true);
$iv = openssl_random_pseudo_bytes(16);
$ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv);
$hash = hash_hmac('sha256', $ciphertext, $key, true);
return $iv . $hash . $ciphertext;
}
Javascript Encryption:
import crypto from 'crypto';
export function encrypt (plain_text: string): string {
const encryptionMethod = 'AES-256-CBC';
const iv = crypto.randomBytes(IV_LENGTH);
const key = crypto.createHash("sha256").update(secret).digest();
var encryptor = crypto.createCipheriv(encryptionMethod, key, iv);
const result = iv + encryptor.update(plain_text, 'utf8', 'binary') + encryptor.final('binary');
return result;
}
I've updated your code slightly to accept an iv parameter. You can generate this in the same way as before (e.g. openssl_random_pseudo_bytes).
For the purposes of demonstration I'll use a fixed IV so we can show the same result.
PHP
function vd_encrypt($plaintext, $password, $iv) {
$method = "AES-256-CBC";
$key = hash('sha256', $password, true);
$ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv);
$hash = hash_hmac('sha256', $ciphertext, $key, true);
return $iv . $hash . $ciphertext;
}
// Replace with below code in production
// $iv = openssl_random_pseudo_bytes(16);
$iv = base64_decode("eQMrc61Gt8qRejRjhJOkVw==");
$result = vd_encrypt("He was a man take him for all in all, I shall not look upon his like again", "password", $iv);
echo "Result (base64): " . base64_encode($result) . "\n";
Node.js
import crypto from 'crypto';
export function encrypt (plaintext: string, password: string, iv: string): string {
const encryptionMethod = 'AES-256-CBC';
const key = crypto.createHash("sha256").update(password).digest();
const encryptor = crypto.createCipheriv(encryptionMethod, key, iv);
const encryptedData = Buffer.concat([encryptor.update(plaintext, 'utf8'), encryptor.final()]);
const hash = crypto.createHmac("sha256", key).update(encryptedData).digest();
return Buffer.concat([iv, hash, encryptedData]);
}
// Replace with below code in production
//const iv = crypto.randomBytes(16);
const iv = Buffer.from("eQMrc61Gt8qRejRjhJOkVw==", "base64");
const result = encrypt("He was a man take him for all in all, I shall not look upon his like again", "password", iv);
console.log("Result (base64):", result.toString("base64"));
In this case the results will be like so:
PHP:
Result (base64): eQMrc61Gt8qRejRjhJOkVxsqZTqUjSUnaL46yZDLGGK5+o7WKLyIiG4UKj0ST93Wi7UlaAyTFIjpIs0C893SFsnHeuVshG+6EJF99GrLSUCMFJG3J1pJnmxF4Pu8ZCbN7Ounp0BjhJKIpu9yQn6uEYylJLXWpzNw+aCwsnIV1h0=
Node.js:
Result (base64): eQMrc61Gt8qRejRjhJOkVxsqZTqUjSUnaL46yZDLGGK5+o7WKLyIiG4UKj0ST93Wi7UlaAyTFIjpIs0C893SFsnHeuVshG+6EJF99GrLSUCMFJG3J1pJnmxF4Pu8ZCbN7Ounp0BjhJKIpu9yQn6uEYylJLXWpzNw+aCwsnIV1h0=
Related
I want to encrypt data in livecode using mergAESEncryptWithKey pData,pKey,pIV,[pMode],[pKeySize],[pPadding]. The encrypted data is then posted to php. PhP decrypts the data using the same function, does something with the data and then encrypts the results and posts them to livecode. Livecode then decrypts the data from php
My PHP Code looks like this (This works perfect)
function encrypt($plaintext, $salt) {
$method = "AES-256-CBC";
$key = hash('sha256', $salt, true);
$iv = openssl_random_pseudo_bytes(32);
$ciphertext = openssl_encrypt($plaintext, $method, $key, $iv);
$hash = hash_hmac('sha256', $ciphertext . $iv, $key, true);
return $iv . $hash . $ciphertext;
}
function decrypt($ivHashCiphertext, $salt) {
$method = "AES-256-CBC";
$iv = substr($ivHashCiphertext, 0, 32);
$hash = substr($ivHashCiphertext, 32, 48);
$ciphertext = substr($ivHashCiphertext, 64);
$key = hash('sha256', $salt, true);
//if (!hash_equals(hash_hmac('sha256', $ciphertext . $iv, $key, true), $hash)) return null;
return openssl_decrypt($ciphertext, $method, $key, $iv);
}
echo $encrypted."</br>";
echo "----------------------------The message is:<br/>";
echo decrypt($encrypted, 'hashsalt');
For most of you who just don't know but still want to post good for nothing comments, I figured it out. #Sammitch and #Mark asking a question don't mean someone is stupid.
/*
* Encrypt or decrypt data using
* AES-256-CBC
*/
function encrypt_decrypt($mode,$data,$key,$salt){
//define the cipher method
$ciphering = "AES-256-CBC";
// Use OpenSSl Encryption method. This works on PHP 7.2 and above
$iv_length = openssl_cipher_iv_length($ciphering);
$options = 0;
if($mode=="encrypt"){
// Use openssl_encrypt() function to encrypt the data
$Data = openssl_encrypt($data, $ciphering, $key, $options, $salt);
}else{
// Use openssl_decrypt() function to decrypt the data
$Data = openssl_decrypt($data, $ciphering, $key,$options, $salt);
}
return $Data;
}
Oooh and this is for livecode
//encrypt
encrypt tString using "aes-256-cbc" with key theKey and IV saltHash at 256 bit
//decrypt
decrypt base64Decode(varEnc) using "aes-256-cbc" with key theKey and IV saltHash at 256 bit
I need in my front (ionic) encrypt a UUID to send it to the backend.
I use CryptoJS, this is my code to encrypt the UUID.
const UUID = 'ABCD1234';
const privateKey = 'f38d09938ead31a57eca34d2a0df1c44';
const salt = CryptoJS.lib.WordArray.random(16);
const iv = CryptoJS.lib.WordArray.random(16);
const key = CryptoJS.PBKDF2(privateKey, salt, {
hasher: CryptoJS.algo.SHA512,
keySize: 4,
iterations: 1
});
const encripted = CryptoJS.AES.encrypt(UUID, key, {
iv
});
const cp = {};
cp.mid = CryptoJS.enc.Base64.stringify(encripted.ciphertext);
cp.iv = CryptoJS.enc.Hex.stringify(iv);
cp.s = CryptoJS.enc.Hex.stringify(salt);
So I send the CP in headers to the server (Slim Framework 3).
I receive like this:
$mid = $request->getHeader('HEADER-MID')[0];
$iv = $request->getHeader('HEADER-IV')[0];
$salt = $request->getHeader('HEADER-S')[0];
//Example Data Received
//mid: losv78Amn1zpgRe5/4hYFA==
//iv: 2d198339d178c053c37e36b7d03e8a3b
//salt: fb07dd1f61d72148bc1423af8cd1f295
$ct = base64_decode($mid);
$salt = hex2bin($salt);
$iv = hex2bin($iv);
$key = hash_pbkdf2("sha512", $privateKey, $salt, 1, 32);
$decrypted = openssl_decrypt($ct, 'AES-256-CBC', hex2bin($key), OPENSSL_RAW_DATA, $iv);
The key is generated correctly in back and front.
But the openssl_decrypt function return false and with openssl_error_string() get this:
error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
I do not know what I'm doing wrong.
Thanks in advance.
In PHP 5.6 there were plenty of solutions that worked perfectly that were based on http://php.net/manual/en/function.mcrypt-decrypt.php
For example
public function encrypt($data)
{
//don't use default php padding which is '\0'
$pad = $this->blocksize - (strlen($data) % $this->blocksize);
$data = $data . str_repeat(chr($pad), $pad);
return bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128,
$this->encryptKey,
$data, MCRYPT_MODE_CBC, $this->iv));
}
But PHP7 has a WARNING that discourages using this function.
"This function has been DEPRECATED as of PHP 7.1.0. Relying on this function is highly discouraged."
Any ideas for safe encryption using keywords on both ends; PHP + Node.js?
LibMcrypt was abandoned in 2007. More information https://wiki.php.net/rfc/mcrypt-viking-funeral
You have to use openssl encrypt http://php.net/manual/en/function.openssl-encrypt.php
PHP
<?php
$textToEncrypt = "Secret Text to Encrypt";
$encryptionMethod = 'aes-256-cbc';
$secretHash = "315a5504d921f8327f73a356d2bbcbf1"; // <---- you have to use some persistent key.
$iv_size = openssl_cipher_iv_length($encryptionMethod);
$iv = openssl_random_pseudo_bytes($iv_size);
//To encrypt
$encryptedMessage = openssl_encrypt($textToEncrypt, $encryptionMethod, $secretHash, 0, $iv);
//Concatenate iv with data
$encryptedMessageWithIv = bin2hex($iv) . $encryptedMessage;
//To Decrypt
$iv_size = openssl_cipher_iv_length($encryptionMethod);
$iv = hex2bin(substr($encryptedMessageWithIv, 0, $iv_size * 2));
$decryptedMessage = openssl_decrypt(substr($encryptedMessageWithIv, $iv_size * 2), $encryptionMethod, $secretHash, 0, $iv);
echo "Encrypted: $encryptedMessageWithIv <br>Decrypted: $decryptedMessage";
Try it here https://3v4l.org/r9pYv
Node.JS(i really not a node.js programmer, there can be more efficient way)
var data = "ad699a2537ec2a7f699acbf97ca0080eh3z5EgvnTAvlc76YeR6HdWPmkDDt+pHiG//qo7xnqyQ=";
var key = "315a5504d921f8327f73a356d2bbcbf1";
var iv = new Buffer(data.substring(0,32), 'hex');
var dec = crypto.createDecipheriv('aes-256-cbc',key,iv);
var decrypted = Buffer.concat([dec.update(new Buffer(data.substring(32),'base64')), dec.final()]);
console.log('DECRYPTED TEXT: '+decrypted.toString());
Try here: https://repl.it/FQyo/2
I'm trying to decrypt a string, previously encrypted by a third party software using PHP RIJNDAEL_128 in CBC mode, using node.js.
Here is an interactive link of the following PHP code, in a sandbox, so you can compile and see for yourself. http://sandbox.onlinephpfunctions.com/code/504a7d052c5b123fac8103a073c05c2ff5f80571
PHP source code:
<?php
class CryptClass{
private $key;
public function __construct($key){
$this->key = $key;
}
public function cryptage($message){
$key = base64_decode($this->key);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $message, MCRYPT_MODE_CBC, $iv);
$ciphertext = $iv . $ciphertext;
return base64_encode($ciphertext);
}
public function decryptage($message){
$key = base64_decode($this->key);
$iv_size2 = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$ciphertext_dec = base64_decode($message);
$iv_dec = substr($ciphertext_dec, 0, $iv_size2);
$ciphertext_dec = substr($ciphertext_dec, $iv_size2);
$message_decrypt = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key,$ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);
return str_replace("\0", "", $message_decrypt);
}
}
// Secret key and data
define('KEY', 'azertyuiolskzif');
define('DATA', 'user#email.com|1477576941|origin.com');
// Crypt
$Crypt = new CryptClass(base64_encode(KEY));
$encodedData = base64_encode($Crypt->cryptage(DATA));
// Decrypt
$decodedData = $Crypt->decryptage(base64_decode($encodedData));
echo 'base64_encode: '.base64_encode(KEY);
echo "\nDATA: ".DATA;
echo "\nDATA length: ".strlen(DATA);
echo "\n\nencodedData: ".$encodedData;
echo "\n\ndata: ".$decodedData;
echo "\n\ndata length: ".strlen($decodedData);
echo "\n\ncrypt/decrypt match?: ".(DATA == $decodedData ? 'yes':'no');
Here is my implementation in node.js: NOT WORKING, see below for working solution
var crypto = require('crypto');
var textToEncrypt = 'user#email.com|1477576941|origin.com';
var encryptionMethod = 'AES-128-CBC';
var secret = "azertyuiolskzif";
var iv = 'aaaabbbbccccdddd';
var encrypt = function (plain_text, encryptionMethod, secret, iv) {
var encryptor = crypto.createCipheriv(encryptionMethod, secret, iv);
return encryptor.update(plain_text, 'utf8', 'base64') + encryptor.final('base64');
};
var decrypt = function (encryptedMessage, encryptionMethod, secret, iv) {
var decryptor = crypto.createDecipheriv(encryptionMethod, secret, iv);
return decryptor.update(encryptedMessage, 'base64', 'utf8') + decryptor.final('utf8');
};
var encryptedMessage = encrypt(textToEncrypt, encryptionMethod, secret, iv);
var decryptedMessage = decrypt(encryptedMessage, encryptionMethod, secret, iv);
console.log(decrypt());
console.log(encryptedMessage);
console.log(decryptedMessage);
I have tried many things and I'm getting lost here between Invalid key length and other error messages. One thing I don't quite understand is that the KEY apparently used to encrypt the data is azertyuiolskzif which is 15 chars long while most script use a required 32 chars string... Maybe PHP doesn't need a 32 char string but Node does?
Or maybe it's related to the difference between 128 and 256. Or is it due to the difference of padding between PHp and Node implementation?
I tried to follow the advices given at Encrypt string in PHP and decrypt in Node.js but even tho, I didn't succeed at encrypting my data in node yet.
Edit:
After some more digging around (and thanks for the explanations in the answers), I finally made crypt/decrypt work in Node.js. But I haven't succeeded to decrypt something crypted by PHP yet.
var crypto = require('crypto');
var AES = {};
AES.encrypt = function(dataToEncrypt, encryptionMethod, secret, iv, padding) {
var encipher = crypto.createCipheriv(encryptionMethod, secret, iv);
encipher.setAutoPadding(padding || 0); // "true" or "128" would work with aes-128-cbc
var encryptedData = encipher.update(dataToEncrypt, 'utf8', 'base64');
encryptedData += encipher.final('base64');
return encryptedData;
};
AES.decrypt = function(encryptedData, encryptionMethod, secret, iv, padding) {
var decipher = crypto.createDecipheriv(encryptionMethod, secret, iv);
decipher.setAutoPadding(padding || 0); // "true" or "128" would work with aes-128-cbc
var decoded = decipher.update(encryptedData, 'base64', 'utf8');
decoded += decipher.final('utf8');
return decoded;
};
// ----
var textToEncrypt = 'user#email.com|1477576941|origin.com';
var secret = "aaaabbbbccccdddd"; // Must be 16 chars
var iv = crypto.randomBytes(16); // Must be 16 chars
var encryptionMethod = 'AES-128-CBC';
// Testing crypt/decrypt using Node.js algorithm.
var encryptedMessage = AES.encrypt(textToEncrypt, encryptionMethod, secret, iv, 128);
var decryptedMessage = AES.decrypt(encryptedMessage, encryptionMethod, secret, iv, 128);
console.log('encryptedMessage', encryptedMessage); // Displays "GWpMWORNKkqlrHJDPuNgSmTKr1vJhaAApHP+ssK3SH5EALTkdWneUZRp9PXNpVQ2"
console.log('decryptedMessage', decryptedMessage); // Displays "user#email.com|1477576941|origin.com"
// Testing decrypt from a string generated by PHP algorithm.
// XXX Doesn't work "Error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length"
// XXX Probably due to wrong padding between PHP/Node implementation?
var stringToDecode = 'RlM3Wkl3N3JRM0dnaEh4SkdoZWFDRy9mZGRoTnkxNlZUL2IvcHl4TkdzUUlRSXQwSWNwWUZ5OFpaRENZQys3S2t0bFZIUWoweUVsZGxUU21sYU9tS0E9PQ==';
console.log('stringToDecode', stringToDecode);
console.log(AES.decrypt(
stringToDecode,
encryptionMethod, secret, iv, 128
));
AES keys must be exactly one of 128, 192 or 256-bits. Some implementations will pad keys in some way but this should not be relied on. Make the key a correct size.
I need to communicate with a asp platform that uses the aspEncrypt from persits.
Can anyone provide an example how to decode a string with PHP and mcrypt that was created via the aspEncrypt routines.
An example page of aspEncrypt is available at this link:
http://support.persits.com/encrypt/demo_text.asp
So if I use the text "Test" and the key "test" it provides an base64 encoded string. I need a php example that convert this encoded string back to the text "Test" with usage of key "test".
This is how i finally solved it:
Expectation:
Key is known
IV is known (in my case, first 32 characters of encoded data)
Encrypted Text is known
In my special case all received data hex encoded.
This means IV and encrypted text.
function decrypt($sString, $sIv, $sKey, $iCipherAlg) {
$sDecrypted = mcrypt_decrypt($iCipherAlg, $sKey, $sString, MCRYPT_MODE_CBC, $sIv);
return trim($sDecrypted);
}
function hex2bin($sData) {
$iLen = strlen($sData);
$sNewData = '';
for($iCount=0;$iCount<$iLen;$iCount+=2) {
$sNewData .= pack("C",hexdec(substr($sData,$iCount,2)));
}
return $sNewData;
}
$sKey = 'this is my key';
// first 32 chars are IV
$sIv = hex2bin(substr($sEncodedData, 0, 32));
$sEncodedData = substr($sEncodedData, 32);
$sEncodedRaw = hex2bin($sEncodedData);
$sDecrypted = decrypt($sEncodedRaw, $sIv, $sKey, MCRYPT_RIJNDAEL_128);
A corresponding encryption works like that:
$sIv = mcrypt_create_iv(mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
$sKey = 'this is my key';
$sContent = 'a lot of content';
$sEncrypted = bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $sKey, $sContent, MCRYPT_MODE_CBC, $sIv));
$sFullEncodedText = bin2hex($sIv) . $sEncrypted;
I encountered an old VBScript project which was encrypting strings with AspEncrypt like this:
Function EncryptString(data, base64Iv)
Set CM = Server.CreateObject("Persits.CryptoManager")
Set Context = CM.OpenContextEx("Microsoft Enhanced RSA and AES Cryptographic Provider", "", True)
Set Key = Context.GenerateKeyFromPassword("secret encryption password", calgSHA512, calgAES256)
Set IVblob = CM.CreateBlob
IVblob.Base64 = base64Iv
Key.SetIV IVblob
Set Blob = Key.EncryptText(data)
EncryptString = Blob.Base64 & ":" & base64Iv
End Function
Based on the arguments to GenerateKeyFromPassword, a binary key is created by hashing the password with SHA-512, and data is encrypted with the aes-256-cbc algorithm. The random Base64-encoded initialization vector is appended to the encrypted value after a colon.
This can be replicated in PHP using the OpenSSL extension:
class Aes256Cbc
{
private string $algo = 'aes-256-cbc';
private string $key;
private int $ivLen;
public function __construct(string $password)
{
$this->key = hash('sha512', $password, true);
$this->ivLen = openssl_cipher_iv_length($this->algo);
}
public function encrypt(string $data): string
{
$iv = random_bytes($this->ivLen);
$ciphertext = openssl_encrypt($data, $this->algo, $this->key, OPENSSL_RAW_DATA, $iv);
return base64_encode($ciphertext) . ':' . base64_encode($iv);
}
public function decrypt(string $encrypted): string
{
[$ctPart, $ivPart] = explode(':', $encrypted);
$iv = base64_decode($ivPart);
$ciphertext = base64_decode($ctPart);
return openssl_decrypt($ciphertext, $this->algo, $this->key, OPENSSL_RAW_DATA, $iv);
}
}
Example usage:
$aes = new Aes256Cbc("secret encryption password");
$decrypted = $aes->decrypt($someValue);
Note: if AspEncrypt was used without setting an initialization vector, the IV will be sequence of null bytes. This fixed IV could be generated in the above PHP class as follows:
$iv = str_repeat("\0", $this->ivLen);
It depends on which cipher it uses, take a look at mcrypt as long as you know the cipher and key it should be easy to decrypt.
If you know the cipher and mode used by the encryption, the function mcrypt_decrypt can decrypt it.
http://uk3.php.net/manual/en/function.mcrypt-decrypt.php