PHP 带恢复码的加密/解密
PHP encryption / deryption with recovery code
我尝试重建加密流程解释 here
加密
$password = 'pass123456'; //user password
$message = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.';
$Ku = openssl_random_pseudo_bytes(256 / 8); //user-specific symmetric key
$Ru = openssl_random_pseudo_bytes(256 / 8); //recovery code
$Su = openssl_random_pseudo_bytes(256 / 8); //user salt ###STORED ON SERVER###
$hash = hash_pbkdf2('sha256', $password, $Su, 5000, 256 / 8, true);
$split = str_split($hash, 16);
$Vu = $split[0]; //password verification token ###STORED ON SERVER###
$Ek = $split[1]; //key to encrypt Ku
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$Eu = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $Ek, $Ku, MCRYPT_MODE_CBC, $iv); ###STORED ON SERVER###
$Fu = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $Ru, $Ku, MCRYPT_MODE_CBC, $iv); ###STORED ON SERVER###
$encryptedMessage = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $Ku, $message, MCRYPT_MODE_CBC, $iv);
解密
$hash = hash_pbkdf2('sha256', ###ENTERED PASSWORD###, ###STORED Su###, 5000, 256 / 8, true);
$split = str_split($hash, 16);
$Vu = $split[0];
$Ek = $split[1];
if($Vu != ###STORED Vu###) {
=> wrong password
} else {
=> correct password
}
$Ku = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $EK, ###STORED Eu###, MCRYPT_MODE_CBC, ###STORED iv###);
$message = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $Ku, $encryptedMessage, MCRYPT_MODE_CBC, ###STORED iv###);
如果我丢失了密码,我可以找回 "Ku":
$Ku = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, ###REVOVERY KEY###, ###STORED Fu###, MCRYPT_MODE_CBC, ###STORED iv###);
上面的代码安全吗? openssl_random_pseudo_bytes
足够随机还是有更好的可能性,即使用鼠标移动来生成初始化数字?
如何处理 mcrypt 的 "initialization vector"? this 正确吗?
分部分析
看起来不错,但您可能想要
- 增加 PBKDF2 函数的迭代次数。现在5000已经很低了。尝试一百万。
- 具有 256 位块大小的 Rijndael 不如具有 128 位块大小(AES)的 Rijndael 分析得好。你应该使用
MCRYPT_RIJNDAEL_128
。这也将提高与其他实现的兼容性。
- 您没有使用可以检测(恶意)对您的密文的操纵的密文身份验证。有关执行此操作的简单 OpenSSL 方法,请参阅 this example。
- 您正在使用 MCrypt 的默认零填充,如果您的明文以 0x00 字节结尾,这就不好了。从解密的消息中删除这些内容时,您需要小心。
Is openssl_random_pseudo_bytes
random enough or is there a better possibility i.e. using mouse movement to generate a initialization number?
初始化向量只需要是不可预测的,而不是秘密的。如果 IV 生成是可预测的,则存在可以利用系统的攻击,但 openssl_random_pseudo_bytes
应该是好的。有很小的机会它没有正确初始化并且你将使用非 "strong" 随机字节(检查第二个参数),但即使那样取决于你的系统架构,IV 的可预测性可能不是可利用。
我也想知道你想如何在 PHP 中获得鼠标移动,因为我假设它在服务器上运行。
整体分析
该代码似乎实现了a good protocol described by Thomas Pronin,但协议略有不完整。因为没有什么可以阻止攻击者提交随机 Ru
并用它覆盖所有用户的值。
在创建帐户(和更改密码)期间,您还需要为恢复密钥创建一个验证值。我认为创建
就足够了
$VRu = hash_pbkdf2('sha256', $Ru, $Su, 1000, 256 / 8, true);
并将其存储在服务器上以备恢复时使用。
由于Ru
是随机生成的,迭代次数可以少一些,但仍然是一个代价高昂的操作。您应该限制恢复操作的使用以减少拒绝服务攻击。
我尝试重建加密流程解释 here
加密
$password = 'pass123456'; //user password
$message = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.';
$Ku = openssl_random_pseudo_bytes(256 / 8); //user-specific symmetric key
$Ru = openssl_random_pseudo_bytes(256 / 8); //recovery code
$Su = openssl_random_pseudo_bytes(256 / 8); //user salt ###STORED ON SERVER###
$hash = hash_pbkdf2('sha256', $password, $Su, 5000, 256 / 8, true);
$split = str_split($hash, 16);
$Vu = $split[0]; //password verification token ###STORED ON SERVER###
$Ek = $split[1]; //key to encrypt Ku
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$Eu = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $Ek, $Ku, MCRYPT_MODE_CBC, $iv); ###STORED ON SERVER###
$Fu = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $Ru, $Ku, MCRYPT_MODE_CBC, $iv); ###STORED ON SERVER###
$encryptedMessage = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $Ku, $message, MCRYPT_MODE_CBC, $iv);
解密
$hash = hash_pbkdf2('sha256', ###ENTERED PASSWORD###, ###STORED Su###, 5000, 256 / 8, true);
$split = str_split($hash, 16);
$Vu = $split[0];
$Ek = $split[1];
if($Vu != ###STORED Vu###) {
=> wrong password
} else {
=> correct password
}
$Ku = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $EK, ###STORED Eu###, MCRYPT_MODE_CBC, ###STORED iv###);
$message = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $Ku, $encryptedMessage, MCRYPT_MODE_CBC, ###STORED iv###);
如果我丢失了密码,我可以找回 "Ku":
$Ku = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, ###REVOVERY KEY###, ###STORED Fu###, MCRYPT_MODE_CBC, ###STORED iv###);
上面的代码安全吗? openssl_random_pseudo_bytes
足够随机还是有更好的可能性,即使用鼠标移动来生成初始化数字?
如何处理 mcrypt 的 "initialization vector"? this 正确吗?
分部分析
看起来不错,但您可能想要
- 增加 PBKDF2 函数的迭代次数。现在5000已经很低了。尝试一百万。
- 具有 256 位块大小的 Rijndael 不如具有 128 位块大小(AES)的 Rijndael 分析得好。你应该使用
MCRYPT_RIJNDAEL_128
。这也将提高与其他实现的兼容性。 - 您没有使用可以检测(恶意)对您的密文的操纵的密文身份验证。有关执行此操作的简单 OpenSSL 方法,请参阅 this example。
- 您正在使用 MCrypt 的默认零填充,如果您的明文以 0x00 字节结尾,这就不好了。从解密的消息中删除这些内容时,您需要小心。
Is
openssl_random_pseudo_bytes
random enough or is there a better possibility i.e. using mouse movement to generate a initialization number?
初始化向量只需要是不可预测的,而不是秘密的。如果 IV 生成是可预测的,则存在可以利用系统的攻击,但 openssl_random_pseudo_bytes
应该是好的。有很小的机会它没有正确初始化并且你将使用非 "strong" 随机字节(检查第二个参数),但即使那样取决于你的系统架构,IV 的可预测性可能不是可利用。
我也想知道你想如何在 PHP 中获得鼠标移动,因为我假设它在服务器上运行。
整体分析
该代码似乎实现了a good protocol described by Thomas Pronin,但协议略有不完整。因为没有什么可以阻止攻击者提交随机 Ru
并用它覆盖所有用户的值。
在创建帐户(和更改密码)期间,您还需要为恢复密钥创建一个验证值。我认为创建
就足够了$VRu = hash_pbkdf2('sha256', $Ru, $Su, 1000, 256 / 8, true);
并将其存储在服务器上以备恢复时使用。
由于Ru
是随机生成的,迭代次数可以少一些,但仍然是一个代价高昂的操作。您应该限制恢复操作的使用以减少拒绝服务攻击。