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.