As @Topaco stated the "set salt" option was disabled in PHP/openssl. Fortunately there is another crypto library available in a lot of modern PHP-versions - libsodium (sodium). Here we find the necessary method to hash passwords in a reproducible way as you need it for encryption purposes.
The "sodium_crypto_pwhash" method is described here: https://www.php.net/manual/de/function.sodium-crypto-pwhash.php.
You should check that libsodium is enabled (the easiest way is phpinfo()):
sodium
sodium support => enabled
libsodium headers version => 1.0.17
libsodium library version => 1.0.17
Now you are ready to hash your passwords. Please note that I used a fixed salt (that is totally insecure) just for demonstration purposes. Same to the "salt" the "opslimit" and "memlimit" variables need to get stored to get a reproducible (decryption) password.
<?php
echo 'php version: ' . PHP_VERSION . ' openssl version: '
. OPENSSL_VERSION_TEXT . ' sodium: '
. SODIUM_LIBRARY_VERSION . '<br>';
echo 'key derivation with PHP-libsodium' . '<br>';
echo 'https://stackoverflow.com/questions/62535675/proper-way-to-generate-key-from-password-to-openssl-encrypt-in-php' . '<br>';
$password = '12345'; //Entered by the end user
//$salt = openssl_random_pseudo_bytes(16); // openssl-randombytes
//$salt = random_bytes(SODIUM_CRYPTO_PWHASH_SALTBYTES); // libsodium randombytes
// ### just for demonstration purpose I'm using a static salt
$salt = "1234567890123456";
$key = sodium_crypto_pwhash(
32,
$password,
$salt,
SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
// or SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE or SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE
SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE,
// or SODIUM_CRYPTO_PWHASH_MEMLIMIT_MODERATE or SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE
SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13
);
echo PHP_EOL . '<br>' . 'key: ' . bin2hex($key) . PHP_EOL . '<br>';
?>
the reproducible result:
php version: 7.4.6 openssl version: OpenSSL 1.1.1g 21 Apr 2020 sodium: 1.0.17
key derivation with PHP-libsodium
https://stackoverflow.com/questions/62535675/proper-way-to-generate-key-from-password-to-openssl-encrypt-in-php
key: 8a2a973c147a18fe2a60f7a2c4513e75141a12be0bdc793243fa12b067a3acbe
Edit: complete file encryption for small files with PHP/libsodium, Argon2ID as KDF and XCHACHA20POLY1305 authenticated encryption
A good source for short examples on sodium/libsodium is https://www.zend.com/blog/libsodium-and-php-encrypt. The "intro-pages" of libsodium do have some examples on how to use it - the following codes are taken from this side
(https://www.php.net/manual/en/intro.sodium.php). There are no checks or error handling, you just need a file "plaintext.txt".
encryption.php:
<?php
// https://www.php.net/manual/de/intro.sodium.php
$password = 'password';
$input_file = 'plaintext.txt';
$encrypted_file = 'encryption1.enc';
$chunk_size = 4096;
$alg = SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13;
$opslimit = SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE;
$memlimit = SODIUM_CRYPTO_PWHASH_MEMLIMIT_MODERATE;
$salt = random_bytes(SODIUM_CRYPTO_PWHASH_SALTBYTES);
$secret_key = sodium_crypto_pwhash(SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES,
$password, $salt, $opslimit, $memlimit, $alg);
$fd_in = fopen($input_file, 'rb');
$fd_out = fopen($encrypted_file, 'wb');
fwrite($fd_out, pack('C', $alg));
fwrite($fd_out, pack('P', $opslimit));
fwrite($fd_out, pack('P', $memlimit));
fwrite($fd_out, $salt);
list($stream, $header) = sodium_crypto_secretstream_xchacha20poly1305_init_push($secret_key);
fwrite($fd_out, $header);
$tag = SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_MESSAGE;
do {
$chunk = fread($fd_in, $chunk_size);
if (stream_get_meta_data($fd_in)['unread_bytes'] <= 0) {
$tag = SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL;
}
$encrypted_chunk = sodium_crypto_secretstream_xchacha20poly1305_push($stream, $chunk, '', $tag);
fwrite($fd_out, $encrypted_chunk);
} while ($tag !== SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL);
fclose($fd_out);
fclose($fd_in);
?>
decryption.php
<?php
// https://www.php.net/manual/de/intro.sodium.php
$encrypted_file = 'encryption1.enc';
$decrypted_file = 'decrypt1.txt';
$password = 'password';
$fd_in = fopen($encrypted_file, 'rb');
$fd_out = fopen($decrypted_file, 'wb');
$chunk_size = 4096;
$alg = unpack('C', fread($fd_in, 1))[1];
$opslimit = unpack('P', fread($fd_in, 8))[1];
$memlimit = unpack('P', fread($fd_in, 8))[1];
$salt = fread($fd_in, SODIUM_CRYPTO_PWHASH_SALTBYTES);
$header = fread($fd_in, SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES);
$secret_key = sodium_crypto_pwhash(SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES,
$password, $salt, $opslimit, $memlimit, $alg);
$stream = sodium_crypto_secretstream_xchacha20poly1305_init_pull($header, $secret_key);
$tag = SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_MESSAGE;
while (stream_get_meta_data($fd_in)['unread_bytes'] > 0 &&
$tag !== SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL) {
$chunk = fread($fd_in, $chunk_size + SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES);
$res = sodium_crypto_secretstream_xchacha20poly1305_pull($stream, $chunk);
if ($res === FALSE) {
break;
}
list($decrypted_chunk, $tag) = $res;
fwrite($fd_out, $decrypted_chunk);
}
$ok = stream_get_meta_data($fd_in)['unread_bytes'] <= 0;
fclose($fd_out);
fclose($fd_in);
if (!$ok) {
die('Invalid/corrupted input');
}
?>