I'm trying to work out how I can use openSSL to encrypt and decrypt a string in bash and PHP and get the same results regardless of where I do this.
I need to save the encrypted string to MySQL and retrieve it from MySQL to be read back and decrypted. I'm happy I can read and write from MySQL
For example. In PHP I can encrypt and decrypt using the following:
$textToEncrypt = "Test Message";
$key = "Testkey";
$iv = "+ByrvYwA-4hB^?jF";
$keyHex = bin2hex($key);
$ivHex = bin2hex($iv);
//To encrypt
$encryptedMessage = openssl_encrypt($textToEncrypt, "AES-256-CBC", $key, 0, $iv);
//To Decrypt
$decryptedMessage = openssl_decrypt($encryptedMessage, "AES-256-CBC", $key, 0, $iv);
This can be decrypted in bash using:
echo -n "3GfrAdvtkHSpalmb4qzEVw==" | openssl aes-256-cbc -d -a -A -K "546573746b6579" -iv "2b427972765977412d3468425e3f6a46"
What I'm struggling with is encrypting the message in BASH so it can be decrypted back in PHP.
Can anyone advise on this ?
Thanks
Just swap the openssl parameter. -d is for decryption, -e for encryption
echo -n "Test Message" | openssl aes-256-cbc -e -a -A -K "546573746b6579" -iv "2b427972765977412d3468425e3f6a46"
I'm trying to decrypt some data that has been encrypted with a passphrase and aes-256-cbc method in a PHP script.
Here is how I encrypt the original data
printf "Hello" | openssl enc -e -base64 -A -aes-256-cbc -k "MYPASSWORD"
// output
U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=
When I try to decrypt it in command-line it works fine
printf "U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=" | openssl enc -d -base64 -A -aes-256-cbc -k "MYPASSWORD"
// output
Hello
BUT when I use openssl_decrypt() in my PHP script it doesn't work!!
$result = openssl_decrypt("U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=", 'AES-256-CBC', "MYPASSWORD");
var_dump($result);
//output
bool(false)
I append the following lines to get the error
while ($msg = openssl_error_string())
echo $msg . "<br />\n";
And it returns:
error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad
decrypt
I know that I should use a key/iv pair but I am not able to extract it from my passphrase with any salt. How can I get it to make the following command work?
printf "U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=" | openssl enc -d -base64 -A -aes-256-cbc -K ??????????????? -iv ????????????????
// expected output !!!
Hello
EDIT:
I tried to get key/iv with -p argument but it doesn't work
printf "U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=" | openssl enc -d -base64 -A -aes-256-cbc -k "MYPASSWORD" -p
salt=9D5AE06E8A2B627C
key=8ACC4E30E9128FBB0763DDDA8998A7141DFDC77B9DADF0A5FC65E67E2A8313FA
iv =4150125DCCD36F73A9F08F3020151A04
Hello
printf "U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=" | openssl enc -d -base64 -A -aes-256-cbc -K 8ACC4E30E9128FBB0763DDDA8998A7141DFDC77B9DADF05E67E2A8313FA -iv 4150125DCCD36F73A9F08F3020151A04
bad decrypt
140735954895816:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:529:
??G?"r!C???&C&??
There is a difference between the password (or passphrase) used as a parameter to openssl enc via the -k option (in your case "MYPASSWORD") and the key parameter that the PHP function openssl_decrypt() expects. The -k option to openssl enc is a passphrase of any length from which an actual 256 bits encryption key will be derived. That is also the key that the PHP openssl_decrypt() function needs. This encryption key is 256 bits because you have chosen aes-256.
You can get to know what that derived encryption key is by adding the -p option when invoking openssl enc. This also prints the iv, another parameter that you will need to use with the PHP openssl_decrypt() function. For example:
printf "Hello" | openssl enc -e -base64 -A -aes-256-cbc -k "MYPASSWORD" -nosalt -p
key=E0FAC2DD2C00FFE30F27A6D14568CB4F12EB84676A3A2BFB172A444C3BBB831F
iv =5A79774BB4B326EED949E6871FC27697
sp0z18QezUO8tSy7tgjOEw==
These printed key and iv values are the ones that you will need to feed into your PHP openssl_decrypt() function invocation, like this:
$ciphertext = 'sp0z18QezUO8tSy7tgjOEw==';
$key = hex2bin('E0FAC2DD2C00FFE30F27A6D14568CB4F12EB84676A3A2BFB172A444C3BBB831F');
$iv = hex2bin('5A79774BB4B326EED949E6871FC27697');
$result = openssl_decrypt($ciphertext, 'AES-256-CBC', $key, 0, $iv);
var_dump($result);
Running the PHP script now results in success:
$ php decrypt.php
string(5) "Hello"
You may have noticed the extra -nosalt option when running openssl enc. Salt is used to add some randomness/uniqueness to the key derivation process and -nosalt omits that step. As a result, the key, iv and ciphertext will be the same in every run (if the same passphrase and plaintext are used) and you should be able to exactly reproduce the output. If you do not use -nosalt, your experiment will still work but the key, iv and ciphertext values will be different for each run and you will also have to get rid of the salt that openssl adds as a header -- see further down this answer for the details.
Another option would be to let the PHP code derive the key and iv from the passphrase before invoking openssl_decrypt(). To do that, you will have to inspect the code of the enc tool for the openssl version that you are using. There you can see which key derivation function is used -- it depends on the version of openssl you are using as well as the options you are giving it -- and whether that is available in the PHP bindings of openssl.
Update, responding to your comment where you add the information that you only have the ciphertext and the passphrase available and that ciphertext was created with crypto-js.
Looking at the source code of crypto-js, it mentions in a comment in the source file evpkdf.js that "the key derivation function is meant to conform with EVP_BytesToKey", which is the same function that most openssl versions use. So you should be able to use the openssl enc tool to extract the key and the iv by using the -p option, like this:
$printf "U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=" | openssl enc -d -base64 -A -aes-256-cbc -k "MYPASSWORD" -p
salt=9D5AE06E8A2B627C
key=8ACC4E30E9128FBB0763DDDA8998A7141DFDC77B9DADF0A5FC65E67E2A8313FA
iv =4150125DCCD36F73A9F08F3020151A04
(which you have confirmed in another comment by now as well) and then use those when invoking the PHP function, as described above. Note that you will have to do this for every ciphertext separately, because the salt (and thus the key and iv) were chosen differently, randomly by crypto-js for each encryption action. To do this in PHP directly, see my previous remark: the required functionality does not seem to be available in its decrypt module.
You can verify that this works by feeding the key and iv into openssl enc when decrypting. However, there is a snag. When using a salt, the openssl way is to include that salt in the output, as you can see here:
$ printf "U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=" | openssl base64 -d -A | hexdump -C
00000000 53 61 6c 74 65 64 5f 5f 9d 5a e0 6e 8a 2b 62 7c |Salted__.Z.n.+b||
00000010 7e 33 bb 56 2f fe 5e fe 1d c7 c8 a9 1f f0 c5 27 |~3.V/.^........'|
00000020
The first 16 bytes of the output are the "magic" bytes Salted__ with the salt after that. This salt is normally read by the tool if you use a passphrase, but it is in the way if you decrypt with key and iv directly. So you will have to remove that header before feeding the bytes into openssl enc as ciphertext when decrypting, for example using tail like this:
printf "U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=" | openssl base64 -d -A | tail -c +17 | openssl enc -d -aes-256-cbc -K 8ACC4E30E9128FBB0763DDDA8998A7141DFDC77B9DADF0A5FC65E67E2A8313FA -iv 4150125DCCD36F73A9F08F3020151A04
Hello
This one-liner first does the base64 decoding, then removes the 16 first bytes and then feeds the result into openssl enc, no longer needing the -base64 options because that has already been taken care of.
In PHP:
$ciphertext = 'U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=';
$ciphertext_decoded = base64_decode($ciphertext);
$ciphertext_nosalt = base64_encode(substr($ciphertext_decoded, 16));
$key = hex2bin('8ACC4E30E9128FBB0763DDDA8998A7141DFDC77B9DADF0A5FC65E67E2A8313FA');
$iv = hex2bin('4150125DCCD36F73A9F08F3020151A04');
$result = openssl_decrypt($ciphertext_nosalt, 'AES-256-CBC', $key, 0, $iv);
var_dump($result);
All that said, you would probably be better off moving away from the key derivation that openssl enc and crypto-js use and that relies on the proprietary mechanism implemented by the OpenSSL EVP_ByesToKey function. Even openssl enc now warns about this being deprecated .
Instead, start using a standard algorithm like PBKDF2. This is supported by more recent versions of openssl enc and I have spotted it in the source code of the crypto-js and PHP crypto modules as well (but have never used those myself). If you have a database of encrypted data that you need to keep, you can re-encrypt its contents one time, using the old approach to decrypt and the PKDBF2 approach to encrypt. Make sure to store the salts separately and not as one blob together with the ciphertext.
The issue here is that you are not using EVP_BytesToKey. This is the OpenSSL KDF used to derive a key and IV from your password.
Note that it is insecure. You should prefer passing a hex key and IV directly to openssl enc.
I encrypted a file using file_input_contents function:
file_input_contents('myfile',openssl_encrypt("This string was AES-128 / ECB encrypted.", "AES-128-ECB", "password"));
But I can't decrypt it using openssl command. Always getting "bad magic number" error.
I tried with this command:
openssl enc -aes-128-ecb -d -in myfile -out outputfile
I'm having some weird behaviour with the openssl_decrypt method in PHP. It's failing, giving me an error: Unknown cipher algorithm, but only sometimes (about 6:10 times) i.e. If I run the command enough times, it will eventually work... My code is:
$result = openssl_decrypt(base64_decode($hash), 'AES-128-CBC', $timestamp);
running openssl list-cipher-commands lists AES-128-CBC as one of the available cipher methods. The specs don't really list anything on the subject - only specifying that unknown cipher algorithm is a possible exception from running the command.
edit:
Using the command line: i.e. running echo "soemthing" | openssl enc -aes-128-cbc on a random machine and then decrypting on the machine that fails with the above echo "..." | openssl enc -aes-128-cbc -d works consistently.
I have to encrypt files that will be decrypted on demand with PHP :
$fh = fopen('encrypted_file', 'rb');
$content = fread($fh, $size);
print mcrypt_decrypt(MCRYPT_TRIPLEDES, 'myPassword', $content, MCRYPT_MODE_ECB);
fclose($fh);
I cannot change the PHP code as it is used many times in the site.
Otherwise, I have to seriously prove that the change is mandatory.
Now, my problem is to find the Linux OpenSSL command to encrypt files that will be decrypted with the given code.
I tried things like :
openssl enc -e -des3 -k myPassword -nosalt -in text_file -out encrypted_file
But I cannot find the decrypted file through PHP.
May you help me to correct the openssl command?
There are so many options (I tried many, I sware) and I don't find how to make them corresponding to the PHP one.
Regards,
Olivier