基本事实:
$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
有没有人对这个问题有任何经验?建设性的答案将得到回报!
最新
上面是一个示例Blowfish哈希,我试图从一个数组重建,通过SHA512 HMACed,对称Blowfish加密(CBC),url安全Base64编码,urlencoded,查询字符串(p!)接收。
下面是查询字符串的字符串(上面已经删除了blowfish哈希),在加密,签名和base64编码之后,但在进行urlencoded之前。每一个都是128个字符长(每个字符串随着你做更多的东西而变长)。
上面是从查询字符串派生的Base64解码和Blowfish解密数组(显然,这个结果之间有安全步骤,但我只是想显示事物的最新状态。)有些事情是不对的。加密似乎没有任何错误。解密也不会产生任何错误。纯文本是错的。如果我加入/破坏这些元素,它们将不会像上面的Blowfish哈希。
答案 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.php
和decrypt.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>