PHP:警告mcrypt_generic_init():Iv大小不正确;提供长度:12,需要:8

时间:2016-06-15 02:45:16

标签: php encryption utf-8 mcrypt

基本事实:

$algorithm  = MCRYPT_BLOWFISH;
$mode       = MCRYPT_MODE_CBC;
$randSource = MCRYPT_DEV_URANDOM;

注意 这不是一个严格的编码问题。

上下文

CentOS 7,Apache 2.4.12,& PHP 5.6.20。

我正在制作一封带有&#34的HTML电子邮件;验证您的电子邮件地址"允许注册过程完成的链接。我的虚拟专用服务器上的所有内容都是UTF-8,所有表单和查询字符串输入都使用多字节(mb)函数进行处理。

背景

作为一个实验(我知道mcrypt库的年龄和状态),我试图解密Blowfish加密的查询字符串参数。假设在上升的过程中,加密序列工作正常,我收到带有链接的电子邮件。

在向下的过程中,hmac_hash()签名(SHA-512,仅适用于此实验)正在运行,我能够将每个独立消息(32个字符)与其哈希校验和(128个字符)分开。分离的消息部分的Base64解码正在工作。对于每个参数,我留下了复合密文,其中复合密文等于 IV +基础密文。假设我使用 substr()版本来独立获取IV和基本密码文本(这是课程的标准)。

问题

PHP: Warning  mcrypt_generic_init(): Iv size is incorrect; supplied length: 12, needed: 8

假设我已经梳理了PHP手册和Stackoverflow。假设我已经看过类似的其他问题,但不完全像这个问题。假设我在互联网上搜索无济于事。假设我有足够的经验来正确设置mb_string。假设当我遇到当前问题时我会处理mcrypt填充。

多字节问题会干扰解密吗?

base64编码IV + base cipher text是否会破坏IV?

base64填充可能是个问题吗?

我应该指定更具体的MCRYPT_BLOWFISH_*吗?

为什么河豚IV大小报告8个字节,但很少产生8字节IV?

我应该使用哪个substr(),substr()mb_substr(),用于设置所有UTF-8并将所有其他输入作为多字节UTF-8处理。我知道这是一个奇怪的问题,但所有的PHP手册mycrypt解密序列示例都使用 substr(),没有使用mb_substr()。我的网站上的所有内容都尽可能使用mb_functions,我不介意使用substr()如果它解决了我的问题,但它没有解决它。当我使用mb_substr()时,我收到以下警告。

PHP: Warning  mcrypt_generic_init(): Iv size is incorrect; supplied length: 11, needed: 8

有没有人对这个问题有任何经验?建设性的答案将得到回报!

最新

Goal: To recreate a hash, like this, from a Blowfish encrypted query string.

上面是一个示例Blowfish哈希,我试图从一个数组重建,通过SHA512 HMACed,对称Blowfish加密(CBC),url安全Base64编码,urlencoded,查询字符串(p!)接收。

下面是查询字符串的字符串(上面已经删除了blowfish哈希),在加密,签名和base64编码之后,但在进行urlencoded之前。每一个都是128个字符长(每个字符串随着你做更多的东西而变长)。

enter image description here

Decrypted Array

上面是从查询字符串派生的Base64解码和Blowfish解密数组(显然,这个结果之间有安全步骤,但我只是想显示事物的最新状态。)有些事情是不对的。加密似乎没有任何错误。解密也不会产生任何错误。纯文本是错的。如果我加入/破坏这些元素,它们将不会像上面的Blowfish哈希。

1 个答案:

答案 0 :(得分:1)

我猜这个问题会隐藏在UTF-8编码的某处,因为你在不正确的上下文中使用它。也可能是您的框架对所有用例都有一些魔力。这可能太多了,通常最终会出现在安全漏洞或者像这样的漏洞中,因为当你真的需要完成它时,你不会做真正需要做的事情。

PHP中的字符串只是字节的集合。您可以按照您选择的编码存储文本,也可以在那里存储二进制数据,如图像。 PHP既不知道什么类型的数据在什么字符串中,也不知道在那里使用什么编码。这取决于开发人员跟踪此信息。

使用加密时,在生成随机字符串或加密某些有效负载时会获得二进制数据。它保存在字符串中,但它没有UTF-8编码,因为它只是字节。我甚至不会说它的编码是ISO-8859-1,因为这意味着字节77(0x4D)代表字母“M”。但实际上,它只是数字 - 77根本不代表任何字母。

要添加的一件事 - 对于ASCII符号(拉丁字母,数字等 - 0-127字节值),它需要一个字节来表示UTF-8编码的符号(与ISO-8859相同)。因此,只要您传递base64_encode d数据,就不必过于担心。 mb_substr的工作方式与substr的工作方式相同。 但是!对于二进制数据,您不能使用mb_*函数,因为它适用于字符。例如,如果加密数据是两个字节0xC5 0xA1,则它只是UTF-8中的单个符号。加密适用于字节(直到最终结果,可能是任何东西 - 甚至是二进制文件),而不是字符。

由于您没有提供任何代码,我已经为您准备了一些代码 - 我希望它能帮助解决您的问题(如果它仍然相关的话)。

要在网址中显示传递参数,有两个文件:encrypt.phpdecrypt.php。保存到目录,在其中运行php -S localhost:8000并转到http://localhost:8000/encrypt.php

encrypt.php

<?php
// mcrypt_enc_get_key_size($td) gives 56, so it's longest that this key can be
$key = 'LedsoilgarvEwAbDavVenpirabUfjaiktavKekjeajUmshamEsyenvoa';
$data = 'This is very important data, with some š UTF-8 ĘĖ symbols';

$td = mcrypt_module_open(MCRYPT_BLOWFISH, '', MCRYPT_MODE_CBC, '');

// create random IV - it's just random 8 bytes. You should use random_bytes() instead if available
$ivSize = mcrypt_enc_get_iv_size($td);
$iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);

mcrypt_generic_init($td, $key, $iv);

$encrypted = mcrypt_generic($td, $data);

mcrypt_generic_deinit($td);
mcrypt_module_close($td);

// payload that you want to send - binary. It's neither UTF-8 nor ISO-8859-1 - it's just bytes
$payload = $iv . $encrypted;

// base64 to pass safely
$base64EncodedPayload = base64_encode($payload);
// URL encode for URL. No need to do both URL-safe base64 *and* base64 + urlencode
$link = 'http://localhost:8000/decrypt.php?encryptedBase64=' . urlencode($base64EncodedPayload);

// in fact, just for the reference, you don't even need base64_encode - urlencode also works at byte level
// base64_encode takes about 1.33 more space, but urlencode takes 3 times more than original for non-safe symbols, so base_64 will probably be shorter
$link2 = 'http://localhost:8000/decrypt.php?encrypted=' . urlencode($payload);

?>
<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <pre><?php
            var_dump('Data:', $data);
            var_dump('Data size in bytes:', strlen($data));
            var_dump('Data size in characters - smaller, as 3 of the characters take 2 bytes:', mb_strlen($data, 'UTF-8'));
            var_dump('Encrypted data size in bytes - same as original:', strlen($encrypted));
            var_dump('Encrypted data size in characters - will be pseudo-random each time:', mb_strlen($encrypted, 'UTF-8'));

            var_dump('IV base64 encoded:', base64_encode($iv));
            var_dump('Encrypted string base64 encoded:', base64_encode($encrypted));
        ?></pre>
        <!-- Link will not contain any special characters, so htmlentities should not make any difference -->
        <!-- In any case, I would still recommend to use right encoding at the right context to avoid any issues if something changes -->
        <a href="<?php echo htmlentities($link, ENT_QUOTES, 'UTF-8');?>">Link to decrypt</a><br/>
        <a href="<?php echo htmlentities($link2, ENT_QUOTES, 'UTF-8');?>">Link to decrypt2</a>
    </body>
</html>

decrypt.php

<?php
$key = 'LedsoilgarvEwAbDavVenpirabUfjaiktavKekjeajUmshamEsyenvoa';

if (isset($_GET['encryptedBase64'])) {
    // just get base64_encoded symbols (will be ASCII - same in UTF-8 or other encodings)
    $base64EncodedPayload = $_GET['encryptedBase64'];
    $payload = base64_decode($base64EncodedPayload);
} else {
    // just get binary string from URL
    $payload = $_GET['encrypted'];
}

$td = mcrypt_module_open(MCRYPT_BLOWFISH, '', MCRYPT_MODE_CBC, '');

$ivSize = mcrypt_enc_get_iv_size($td);

$iv = substr($payload, 0, $ivSize);
$encrypted = substr($payload, $ivSize);

mcrypt_generic_init($td, $key, $iv);

/* Decrypt encrypted string */
$decrypted = mdecrypt_generic($td, $encrypted);

/* Terminate decryption handle and close module */
mcrypt_generic_deinit($td);
mcrypt_module_close($td);

?>
<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <pre><?php
            var_dump('IV base64 encoded:', base64_encode($iv));
            var_dump('Encrypted string base64 encoded:', base64_encode($encrypted));
            var_dump('Result:', $decrypted);
        ?></pre>
    </body>
</html>