I am using following js code to encrypt string
var text = 'should be decrypted!';
var key = 'HighlySecretKeyForJsEncryption!!';
var encrypted = CryptoJS.AES.encrypt(text, key);
console.log(encrypted.toString());
output : U2FsdGVkX19vf+s6/+eB8A+3iKFCl1A0e+oe0BSbcMVGxb64FL35Q3CB/LZNu4ng
and this what I did in php to decrypt this
function decrypt($toDecrypt) {
$key = "HighlySecretKeyForJsEncryption!!";
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$toDecrypt = base64_decode($toDecrypt);
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, substr($toDecrypt, $iv_size), MCRYPT_MODE_CBC, substr($toDecrypt, 0, $iv_size)));
}
But this is not working, it gives me garbage string.
From docs:
var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
CryptoJS supports AES-128, AES-192, and AES-256. It will pick the
variant by the size of the key you pass in. If you use a passphrase,
then it will generate a 256-bit key.
You probabily need to pass the constant MCRYPT_RIJNDAEL_256 when decrypting php-side
More about AES encryption / decrytption in php: https://stackoverflow.com/a/3422787/4499267
Related
I am trying to decrypt data coming from ionic in PHP but it is not decrypting using openssl_decrypt().
In my ionic app, I was able to encrypt and also test the decryption function which works well. Below are both the encryption and decryption functions:
Encryption
encrypt(key, iv, value){
const encrypted = CryptoJS.AES.encrypt(value, key, { iv: iv });
const encryptedMessage = encrypted.toString();
return encryptedMessage;
}
Decryption
decrypt(value, keys, ivs){
const decryptedMessage = CryptoJS.AES.decrypt(value, keys, { iv: ivs
}).toString(CryptoJS.enc.Utf8);
return decryptedMessage;
}
Encrypted data with key and iv
$iv = "048eb25d7a45ff00";
$key = "feda4f2f67e7aa96";
$encrypted = "U2FsdGVkX1+kBV6Q5BQrjuOdi4WLiu4+QAnDIkzJYv5ATTkiiPVX8VcDUpMqSeIwCfyTNQOosp/9nYDY4Suu4/Lmhh2quKBU7BHGOtKwu9o=";
**To decrypt in PHP**
<?php
// Use openssl_decrypt() function to decrypt the data
$output = openssl_decrypt($encrypted, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
echo $output;
?>
How can I decrypt this?
In the CryptoJS code, the key material is passed as string, which is why it is interpreted as password and a key derivation is applied. The key derivation function used is the OpenSSL proprietary EVP_BytesToKey(). During encryption, CryptoJS.AES.encrypt() does the following:
Generate an 8 bytes salt implicitly.
Derive a 32 bytes key and 16 bytes IV based on the salt and password using EVP_BytesToKey().
Encrypt the plaintext with key and IV, ignoring any explicitly passed IV (here 048eb25d7a45ff00)!
Convert the data to OpenSSL format (by encrypted.toString()): Base64 encoding of the ASCII encoding of Salted__, followed by the 8 bytes salt and the actual ciphertext.
Therefore, decryption must be carried out as follows:
Separate salt and actual ciphertext.
Derive the 32 bytes key and 16 bytes IV from salt and password using EVP_BytesToKey(). Note that the IV 048eb25d7a45ff00 is not needed.
Decrypt the ciphertext with key and IV.
A possible implementation:
// Separate salt and actual ciphertext
$saltCiphertext = base64_decode("U2FsdGVkX1+kBV6Q5BQrjuOdi4WLiu4+QAnDIkzJYv5ATTkiiPVX8VcDUpMqSeIwCfyTNQOosp/9nYDY4Suu4/Lmhh2quKBU7BHGOtKwu9o=");
$salt = substr($saltCiphertext, 8, 8);
$ciphertext = substr($saltCiphertext, 16);
// Separate key and IV
$keyIv = EVP_BytesToKey($salt, "feda4f2f67e7aa96");
$key = substr($keyIv, 0, 32);
$iv = substr($keyIv, 32, 16);
// Decrypt using key and IV
$decrypted = openssl_decrypt($ciphertext, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
print($decrypted . PHP_EOL); // {"username":"07069605705","password":"father2242"}
// KDF EVP_BytesToKey()
function EVP_BytesToKey($salt, $password) {
$bytes = ''; $last = '';
while(strlen($bytes) < 48) {
$last = hash('md5', $last . $password . $salt, true);
$bytes.= $last;
}
return $bytes;
}
Note: EVP_BytesToKey() is deemed insecure nowadays and should not be used. A more secure alternative is PBKDF2 which is supported by both CryptoJS and PHP.
I am trying to encrypt the payload with the AES encryption like below for SSG-WSG API. But I keep getting
Failed to parse JSON request content
I think something is wrong with my way of encryption. I am doing this in PHP.
<pre>
$cipher = "aes-256-cbc";
$ekey = "encryption key provided to SSG"
//Generate a 256-bit encryption key
$encryption_key = $ekey;
// Generate an initialization vector
$iv_size = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($iv_size);
//Data to encrypt
$data = $f; // payload in f
$encrypted_data = openssl_encrypt($data, $cipher, $encryption_key, 0, $iv);
$x = base64_encode($encrypted_data);
</pre>
Where is the SSGAPIInitVector initialization vector to be used, and how?
Thanks
Based on the openssl_encrypt library documentation, the 5th paramenter $iv is for the initialisation vector value. So, I believe you have to replace what you are doing which randomly generates a random Initialisation Vector to the fixed value provided.
I found this post how to encrypt in php and decrypt in node and it works:
Encrypt in PHP 7 decrypt in Node JS
But I have problem to do the same in oposite direction.
I tried like this:
Node:
const crypto = require('crypto');
const data = "data to encrypt";
const key = "315a5504d921f8327f73a356d2bbcbf1";
const iv = new Buffer(data.substring(0, 32), 'hex');
const cipher = crypto.createCipher('aes-256-cbc', key, iv);
let crypted = cipher.update(data, 'utf8', 'hex')
crypted += cipher.final('hex');
console.log(crypted);
PHP:
<?php
$encryptedMessage = '3aa3fc237aaf34a26482674cfcef1210';
$encryptionMethod = 'aes-256-cbc';
$secretHash = "315a5504d921f8327f73a356d2bbcbf1";
//To Decrypt
$iv_size = openssl_cipher_iv_length($encryptionMethod);
$iv = hex2bin(substr($encryptedMessage, 0, $iv_size * 2));
$decryptedMessage = openssl_decrypt(substr($encryptedMessage, $iv_size * 2), $encryptionMethod, $secretHash, 0, $iv);
echo "Decrypted: $decryptedMessage";
But not working, any idea how to make this work?
The IV should be random and the same IV needs to be used in both the encryption and decryption process.
Your initialization vector is based on the unecrypted string, which is a very bad idea as you'd be leaking part of your unecrypted data if you send the IV with the encrypted data.
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