执行 trim($decrypted) 将删除解密后可能出现的空填充。
问题是,如果您正在加密诸如 MSWord 文档之类的内容,这些文档通常以空字符结尾。结果 $decrypted 将小于原始明文 - 然后将无法在 MSOffice 中打开。
要解决此问题,请确保存储原始明文的长度,并在解密时执行
$decrypted = substr(mdecrypt_generic($td, $encrypted), 0, $originalLength);
(PHP 4 >= 4.0.2, PHP 5, PHP 7 < 7.2.0, PECL mcrypt >= 1.0.0)
mcrypt_module_open — 打开要使用的算法和模式的模块
此函数自 PHP 7.1.0 起已弃用,自 PHP 7.2.0 起移除。强烈建议不要依赖此函数。
此函数打开要使用的算法和模式的模块。算法的名称在 algorithm 中指定,例如 "twofish"
或为 MCRYPT_ciphername
常量之一。通过调用 mcrypt_module_close() 关闭模块。
algorithm
MCRYPT_ciphername
常量之一,或算法的名称作为字符串。
algorithm_directory
algorithm_directory
参数用于查找加密模块。提供目录名称时,将使用该目录。将其设置为空字符串 (""
) 时,将使用 mcrypt.algorithms_dir
php.ini 指令设置的值。如果未设置,则使用编译到 libmcrypt 中的默认目录(通常为 /usr/local/lib/libmcrypt)。
mode
MCRYPT_MODE_modename
常量之一,或以下字符串之一:"ecb"、"cbc"、"cfb"、"ofb"、"nofb" 或 "stream"。
mode_directory
mode_directory
参数用于查找加密模块。提供目录名称时,将使用该目录。将其设置为空字符串 (""
) 时,将使用 mcrypt.modes_dir
php.ini 指令设置的值。如果未设置,则使用编译到 libmcrypt 中的默认目录(通常为 /usr/local/lib/libmcrypt)。
通常返回加密描述符,或在出错时返回 false
。
示例 #1 mcrypt_module_open() 示例
<?php
$td = mcrypt_module_open(MCRYPT_DES, '',
MCRYPT_MODE_ECB, '/usr/lib/mcrypt-modes');
$td = mcrypt_module_open('rijndael-256', '', 'ofb', '');
?>
上面示例中的第一行将尝试从默认目录打开 DES
密码,并从 /usr/lib/mcrypt-modes 目录打开 ECB
模式。第二个示例使用字符串作为密码和模式的名称,这仅在扩展与 libmcrypt 2.4.x 或 2.5.x 链接时才有效。
示例 #2 在加密中使用 mcrypt_module_open()
<?php
/* 打开密码 */
$td = mcrypt_module_open('rijndael-256', '', 'ofb', '');
/* 创建 IV 并确定密钥长度,在 Windows 上使用 MCRYPT_RAND
* 代替 */
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_DEV_RANDOM);
$ks = mcrypt_enc_get_key_size($td);
/* 创建密钥(仅示例:MD5 不是此哈希算法的良好选择) */
$key = substr(hash('md5', 'very secret key'), 0, $ks);
/* 初始化加密 */
mcrypt_generic_init($td, $key, $iv);
/* 加密数据 */
$encrypted = mcrypt_generic($td, 'This is very important data');
/* 终止加密处理程序 */
mcrypt_generic_deinit($td);
/* 初始化加密模块以进行解密 */
mcrypt_generic_init($td, $key, $iv);
/* 解密加密字符串 */
$decrypted = mdecrypt_generic($td, $encrypted);
/* 终止解密句柄并关闭模块 */
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
/* 显示字符串 */
echo trim($decrypted) . "\n";
?>
执行 trim($decrypted) 将删除解密后可能出现的空填充。
问题是,如果您正在加密诸如 MSWord 文档之类的内容,这些文档通常以空字符结尾。结果 $decrypted 将小于原始明文 - 然后将无法在 MSOffice 中打开。
要解决此问题,请确保存储原始明文的长度,并在解密时执行
$decrypted = substr(mdecrypt_generic($td, $encrypted), 0, $originalLength);
对于类似以下的错误
' mcrypt_module_open(): Could not open encryption module in '
确保您使用的是正确的名称。提供密码列表的页面**不是**说明每个密码的正确方式(此处显示https://php.net/manual/en/mcrypt.ciphers.php)。
为了查看哪些密码受支持,请尝试使用phpinfo(); 并查看mcrypt下的内容,例如:
mcrypt support => enabled
mcrypt_filter support => enabled
Version => 2.5.8
Api No => 20021217
Supported ciphers => cast-128 gost rijndael-128 twofish arcfour cast-256 loki97 rijndael-192 saferplus wake blowfish-compat des rijndael-256
serpent xtea blowfish enigma rc2 tripledes
Supported modes => cbc cfb ctr ecb ncfb nofb ofb stream
还需要指出的是,在为mcrypt形成密钥时,不应使用md5()和/或sha1()。这是因为十六进制编码仅使用一组16个字符[0-9a-f],相当于4位,因此将加密强度降低了一半:4 x 32 = 128位。
我重写了显示的示例,因此以下是我获得真正256位加密的建议
<?php
$key1 = "这是一个秘密密钥";
$key2 = "这是第二个秘密密钥";
$input = "让我们在9点钟在秘密地点见面。";
$length = strlen($input);
/* 打开密码 */
$td = mcrypt_module_open('rijndael-256', '', 'cbc', '');
/* 创建IV并确定密钥长度,在Windows上使用MCRYPT_RAND
* 代替 */
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
$ks = mcrypt_enc_get_key_size($td);
/* 创建密钥 */
$key1 = md5($key1);
$key2 = md5($key2);
$key = substr($key1, 0, $ks/2) . substr(strtoupper($key2), (round(strlen($key2) / 2)), $ks/2);
$key = substr($key.$key1.$key2.strtoupper($key1),0,$ks);
/* 初始化加密 */
mcrypt_generic_init($td, $key, $iv);
/* 加密数据 */
$encrypted = mcrypt_generic($td, $input);
/* 终止加密处理程序 */
mcrypt_generic_deinit($td);
/* 初始化解密模块 */
mcrypt_generic_init($td, $key, $iv);
/* 解密加密字符串 */
$decrypted = mdecrypt_generic($td, $encrypted);
/* 终止解密句柄并关闭模块 */
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
/* 显示字符串 */
echo "文本: ".substr($decrypted,0,$length) . "<br>";
echo "编码: ".$encrypted ."<br>";
echo "<br>key1: $key1 <br>key2: $key2<br>生成的密钥: $key";
?>
关于之前关于十六进制格式和大小写作为提高密钥的方法的评论
这似乎很明显,但选择仅限于十六进制字符([0-9a-z]);您可以从md5()或sha1()获取原始的RAW输出,而不是默认的可读十六进制格式。
原始输出的结果将是16或20(取决于使用的哈希函数)个范围为0-255的字符序列。比[0-9a-z]甚至[0-9a-zA-Z]都要好得多。
16或20通常小于最大密钥长度(示例中的$ks),但您可以将两个或多个密钥连接在一起
<?php
$human_key1 = '一些非常秘密的东西';
$human_key2 = '一些其他非常秘密的东西';
// 使用两个“人类可读”密钥和sha1生成40字节二进制密钥。
$bigger_binary_key = sha1($human_key1, true) . sha1($human_key2, true);
// 然后像平时一样使用它(从示例中提取)
$key = substr($bigger_binary_key, 0, $ks);
?>
...或者您可以自动将一个大型“人类密钥”拆分为两个或多个部分,使用sha1(原始输出!)对这些部分进行哈希,并将其重新组合(按原始顺序或重新排列、加盐、根据需要转换它们)以获得40、60、80或更多字符的二进制密钥,具体取决于秘密密钥已拆分的部件数量=)
针对Windows的mcrypt_module_open()错误的匿名用户的后续操作
<?php
$M = mcrypt_list_modes();
$A = mcrypt_list_algorithms();
foreach ($M as $m)
foreach ($A as $a) {
$t = @mcrypt_module_open($a,'',$m,'');
print "$m, $a = ";
print ($t)?"ok":"nope";
print "<br>";
}
?>
这将显示并非所有模式都适用于所有算法。Cygwin也没有'libmcrypt.dll',它也只适用于某些组合。
(第一次尝试碰巧是其中一个不起作用的!)
请记住,mcrypt函数没有实现填充,例如pkcs#5。这会导致末尾出现零字节的问题,并且字符串无法在其他环境中正确解码。
有关如何添加pkcs 5填充的示例,请参阅ref.mcrypt.php
对dinamic的创建密钥函数的轻微改进
我认为弱点在于大写字母始终用于字符串的同一部分。以下代码将字符串的随机字符大写,使密钥更难以预测
<?php
$key = substr($key1, 0, $ks/2) . substr($key2, (round(strlen($key2) / 2)), $ks/2);
$key = substr($key.$key1.$key2.$key1,0,$ks);
$buffer = str_split($key);
$limit = count($buffer)-1;
srand((float)microtime() * 1000000);
$end = rand(0, $limit);
$a = 0;
// 将随机字符替换为大写字母
while ($a < $end) {
list($usec, $sec) = explode(' ', microtime());
$seed = ((float)$sec) + ((float) $usec * 100000);
mt_srand($seed);
$index = mt_rand(0,$limit);
$buffer[$index] = strtoupper($buffer[$index]);
$a++;
}
$key = join('', $buffer);
?>