当前位置: 首页 > 知识库问答 >
问题:

PHP中从密码生成openssl_encrypt密钥的正确方法

胡禄
2023-03-14
$password = '12345'; //Entered by the end user

$salt = openssl_random_pseudo_bytes(16);
$key = openssl_pbkdf2($password, $salt, 32, 10000, 'sha256');

我将生成的salt前缀存储到加密文件。密码不存储,只有用户知道。

解决方案奏效了。我的问题是,今天这个解决方案是否足够好和安全?

共有1个答案

谭曦
2023-03-14

正如@topaco所说,在PHP/OpenSSL中禁用了“set salt”选项。幸运的是,在许多现代PHP版本中都有另一个加密库--libsanade(钠)。在这里,我们找到了必要的方法,以一种可复制的方式哈希密码,因为您需要它来加密目的。

这里描述了“sodium_crypto_pwhash”方法:https://www.php.net/manual/de/function.sonal-crypto-pwhash.php。您应该检查是否启用了libsan钠(最简单的方法是phpinfo()):

sodium
sodium support => enabled
libsodium headers version => 1.0.17
libsodium library version => 1.0.17

现在您已经准备好散列密码了。请注意,我使用了一个固定的盐(这是完全不安全的)只是为了演示目的。与“salt”相同,需要存储“opslimit”和“memlimit”变量以获得可复制(解密)密码。

<?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>';
?>
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

关于钠/libsona的简短示例的一个很好的来源是https://www.zend.com/blog/libsona-and-php-encrypt。libsnase的“intro-pages”确实有一些如何使用它的示例--以下代码来自这一边(https://www.php.net/manual/en/intro.sodium.php)。没有检查或错误处理,您只需要一个文件“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');
}
?>
 类似资料:
  • 今天我了解到,“密码”往往意味着任意数量的字符的可记忆字符串,而“密钥”意味着高度随机的位字符串(基于所使用的加密算法的特定长度)。 所以今天我第一次听说了密钥派生函数的概念。 我对如何从任意长度的密码(在PHP中)派生32字节密钥感到困惑。 以下方法有效,但忽略了“[盐]应随机生成”的指示(钠也是如此): 以下方法也有效,但感觉也不太对: 在我的所有搜索中,我很惊讶我没有在PHP中找到一种“官方

  • 我正在尝试创建一个公钥以允许我推送到Git,但我的. ssh文件夹尚未创建。 以下是我运行的命令: $ssh-keygen-t rsa-Cemaill@me.com 生成公共/私有rsa密钥对 输入保存密钥的文件(/h/.ssh/id\u rsa): 这就是H:驱动器配置、数据、配置文件、配置文件中的全部内容。V2 我认为有一个问题,因为当我得到提示输入文件,其中保存密钥(/h//. ssh/id

  • 我用过这个命令 生成密钥库。它工作正常,但从我读到的内容来看,这个命令还应该提示您输入密钥密码(而不是存储密码)?我从来没有收到过这样的提示。我能跑 查看密钥库的内容。钥匙似乎就在那里。。。正确的别名在那里。在哪里获取/设置特定别名的密码? 我有一个key.properties在Android目录 在build.gradle我有: 当我试图生成一个发布版本时,我得到了 我想它可能与keyPassw

  • 我已经使用OpenSSL创建了一个自签名证书链。证书的扩展名为.crt。我试图从那个.crt文件创建一个.keystore文件。 如何成功生成密钥存储库文件?为什么将该文件转换为.p12有效?

  • 我试图为某些设备生成密钥时出错。我能够在运行4.4.2的三星Galaxy Note上重现错误。 我创建了一个小应用程序,只能通过从Android开发者页面“生成新私钥”下的https://developer.android.com/training/articles/keystore.html逐行复制代码来生成密钥 错误似乎发生在kpg.generateKeyPair(),在Android Key

  • 如何使用PHP安全地生成适用于AES 256哈希数据的密钥? 我尝试过谷歌搜索,找到了大量关于如何使用PHP使用AES 256加密数据的信息,但是所有这些示例都使用了现有的预生成密钥,而我需要在PHP中生成密钥。