在PHP中确定性地从密码派生32字节的密钥 [英] Derive a 32-byte key from a password deterministically in PHP

查看:94
本文介绍了在PHP中确定性地从密码派生32字节的密钥的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

今天我了解到,密码" 倾向于表示一个可记住的任意数量字符的字符串,而"key" 则表示一个高度随机的位字符串(具体长度取决于所使用的加密算法.

Today I learned that "password" tends to mean a memorizable string of an arbitrary number of characters, while "key" means a highly random string of bits (of a specific length based on the encryption algorithm used).

所以今天我首先听说了密钥派生函数的概念.

And so today I first heard of the concept of a Key derivation function.

对于如何从任意长度的密码(在PHP中)中派生32字节的密钥,我感到困惑.

I'm confused about how to derive a 32-byte key from a password of arbitrary length (in PHP).

以下方法有效,但忽略了[[The盐]应该随机生成"(():

The following approach works but ignores the instruction of "[The salt] should be generated randomly" (so does Sodium):

$salt = 'this salt remains constant';
$iterations = 10;
$length = 32;
$aesKey = hash_pbkdf2('sha256', $somePasswordOfArbitraryLength, $salt, $iterations, $length, false);

以下方法也可以使用,但感觉也不正确:

The following approach also works but doesn't quite feel right either:

$hash = password_hash($somePasswordOfArbitraryLength, PASSWORD_BCRYPT, ['cost' => $iterations]);
$aesKey = substr($hash, -$length);//this smells weird

在我所有的搜索过程中,我感到惊讶的是,我还没有在PHP中找到确定性地从密码中派生32字节密钥的正式"方式.

我应该怎么做?

P.S.我在PHP中使用Laravel,并想使用AES-256-CBC加密,如下所示:

P.S. I'm using Laravel in PHP and want to use AES-256-CBC encryption like this:

$encrypter = new \Illuminate\Encryption\Encrypter($aesKey, 'AES-256-CBC');
$encryptedText = $encrypter->encrypt($text);

Laravel的加密助手(例如Crypt::encryptString('Hello world.'))似乎不符合我的要求,因为我希望每个用户的数据将根据每个人的密码分别加密.

Laravel's encryption helper (e.g. Crypt::encryptString('Hello world.')) seems unfit for my requirements since I want each user's data to be encrypted separately based on each individual's password.

我使用的任何密钥派生函数每次都需要生成相同的密钥,因为我将使用对称加密来解密该用户使用该密钥加密的字符串.

Whatever key derivation function I use needs produce the same key every time since I'll be using symmetric encryption to then decrypt strings that that user had encrypted with that key.

P.P.S.感谢问题 1 2 3 向我介绍某些概念.

P.P.S. Thanks to questions 1, 2, and 3 for introducing certain concepts to me.

推荐答案

对于 hash-pbkdf2 您说:

以下方法有效,但忽略了"[盐]应该随机生成"的指示.

"The following approach works but ignores the instruction of "[The salt] should be generated randomly"

好吧,解决方法是随机生成盐,并将其与密文一起存储.有关如何在PHP中生成安全的随机字节的方法,请参见此问题.然后可以将输出用作加密的密钥.当然,密钥将始终使用存储的盐和存储的密码来重新生成,并且不需要存储.请注意,密钥由原始字节组成;最好是从hash-pbkdf2(最后一个参数)中检索原始密钥.

Well, the fix to that is to do generate the salt randomly, and store it with the ciphertext. See this question for methods on how to generate secure random bytes within PHP. The output can then be used as key to encrypt; of course the key will always be regenerated using the stored salt and memorized password, and doesn't need to be stored. Note that keys consist of raw bytes; it's probably best to retrieve a raw key from hash-pbkdf2 (the last parameter).

请注意,迭代次数应尽可能多.如今,通常将100,000左右视为最佳选择,但越高越安全.攻击者需要花费大量时间来计算每个密码的结果密钥,并且由于密码仅包含30到60位(对于好的密码而言),确实有助于抵御字典攻击.

Note that the iteration count should be as high as possible. Normally 100,000 or so is considered optimal nowadays, but the higher the more secure. It takes about as much time for an attacker to calculate the resulting key for each password, and as passwords only contain 30 to 60 bits (for good passwords) it really helps against dictionary attacks.

这篇关于在PHP中确定性地从密码派生32字节的密钥的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆