3

我在使用 PHP 解密使用 iOS 5.x 的 CommonCrypto 库加密的字符串时遇到问题。以下是参数:

Algorithm: AES-128
Mode: CTR
Mode options: CTR Little-Endian
Padding: None

这是我最好的尝试示例:

<?php
$encrypted = base64_decode('MlNFlnXE1sqIsmKZRtjChBvUMgiJlXgdjHVxQJ6JK24Id4uaN9NK/nBtY+cgrMJR/PRJRCmIUx0boQO5XqJYZ8VJ0w==');
$key = base64_decode('HB+dD1Irj2rXQ/nO+IuqSiK9xVE3PD9cZGIGzrMtwtA=');
$iv = base64_decode('2gxxKYU/G4lj7174e5wj+g==');

$cryptor = mcrypt_module_open('rijndael-128', '', 'ctr', '');
mcrypt_generic_init($cryptor, $key, $iv);
echo mdecrypt_generic($cryptor, $encrypted);

mcrypt_generic_deinit($cryptor);
mcrypt_module_close($cryptor);

输出如下所示:

Lorem ipsum dolo?N??]ѕȢ?+?
                                             ????x??k????}??'???Ŧ??;t

但它应该是“ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do... ”(包括尾随省略号。)

块大小为 16,并且前 16 个字符是正确的。这似乎表明 Mcrypt 和 CommonCrypto 之间的 AES CTR 反递增过程不匹配。到目前为止,我听到的每个人都建议这是 Big Endian 与 Little Endian 的问题。

我花了几天时间试图自己弄清楚所有这些字节顺序和反递增的东西,但这对我来说仍然是巫术。:-( 我只需要一些 PHP 代码来正确解密我的字符串。我不在乎算法的运行速度有多快。我愿意放弃 Mcrypt 以支持 PHP-native 解决方案或其他一些 PHP 扩展,只要这是一个常见的。但是,在 iOS 端进行更改不是一种选择。

请帮忙!

4

2 回答 2

3

分组密码模式非常简单,如果两个实现不兼容,您可以自己实现它们。

这是针对您的特定情况的 CTR 实现:

function ctr_crypt($str, $key, $iv) {
    $numOfBlocks = ceil(strlen($str) / 16);

    $ctrStr = '';
    for ($i = 0; $i < $numOfBlocks; ++$i) {
        $ctrStr .= $iv;

        // increment IV
        for ($j = 0; $j < 16; ++$j) {
            $n = ord($iv[$j]);
            if (++$n == 0x100) {
                // overflow, set this one to 0, increment next
                $iv[$j] = "\0";
            } else {
                // no overflow, just write incremented number back and abort
                $iv[$j] = chr($n);
                break;
            }
        }
    }

    return $str ^ mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $ctrStr, MCRYPT_MODE_ECB);
}

该算法非常简单:您始终追加并递增 IV,直到您有一个更长(或等长)的字符串作为输入字符串。然后,您使用 ECB 模式加密此字符串,并将其与输入字符串进行异或。

增量是这里复杂的部分,因为我们正在处理二进制数。Little Endian 意味着我们从左到右递增 (j = 0, j < 16, j++)。Big Endian 意味着我们从右向左递增 (j = 15, j >= 0, j--)。

试试看:

$encrypted = base64_decode('MlNFlnXE1sqIsmKZRtjChBvUMgiJlXgdjHVxQJ6JK24Id4uaN9NK/nBtY+cgrMJR/PRJRCmIUx0boQO5XqJYZ8VJ0w==');
$key = base64_decode('HB+dD1Irj2rXQ/nO+IuqSiK9xVE3PD9cZGIGzrMtwtA=');
$iv = base64_decode('2gxxKYU/G4lj7174e5wj+g==');

var_dump(ctr_crypt($encrypted, $key, $iv));
// string(67) "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do..."

注意:ctr_crypt既可用作加密功能,也可用作解密功能。

于 2013-05-18T10:40:43.353 回答
0

看起来 PHP 的 mcrypt 使用其他 CTR 模式(大端而不是小端)。密钥计划和 IV 都可以(正如您在解密第一个块时看到的那样),但 IV 递增函数出现问题。

请参阅以下 PHP 代码,该代码通过手动递增 IV 并在 ECB 模式下加密 gamma 来解决您的问题:

<?php
$encrypted = base64_decode('MlNFlnXE1sqIsmKZRtjChBvUMgiJlXgdjHVxQJ6JK24Id4uaN9NK/nBtY+cgrMJR/PRJRCmIUx0boQO5XqJYZ8VJ0w==');
$key = base64_decode('HB+dD1Irj2rXQ/nO+IuqSiK9xVE3PD9cZGIGzrMtwtA=');
$iv = base64_decode('2gxxKYU/G4lj7174e5wj+g==');

$ivarr = array_values(unpack('C*', $iv));
$ivdata = array_merge($ivarr);
$blocklen = count($ivarr);

for ($i = 1; $i < strlen($encrypted)/strlen($iv); $i++)
{
    // incrementing IV
    $ivarr[0] += 1;
    if ($ivarr[0] == 256)
    $ivarr[0] = 0;

    $j = 0;
    while ($ivarr[$j] == 0)
    {   
    $j++;
    if ($j == $blocklen)
        break;
    $ivarr[$j]++;
    }
    // appending to array
    var_dump($ivarr);
    $ivdata = array_merge($ivdata, $ivarr);
}

// now ivdata contains the full CTR gamma. Encrypting it.

$cryptor = mcrypt_module_open('rijndael-128', '', 'ecb', '');
$res = mcrypt_generic_init($cryptor, $key, "");

$ivdatastr = implode(array_map("chr", $ivdata));
$ivdecr = mcrypt_generic($cryptor, $ivdatastr);

$ivdecr = array_values(unpack('C*', $ivdecr));
$decrypted = array_values(unpack('C*', $encrypted));
$i = 0;

for ($i = 0; $i < count($decrypted); $i++)
{
    $decrypted[$i] = $decrypted[$i] ^ $ivdecr[$i % count($ivdecr)];
}    

$string = implode(array_map("chr", $decrypted));

var_dump($string);

mcrypt_generic_deinit($cryptor);
mcrypt_module_close($cryptor);
?>
于 2013-05-10T19:41:31.667 回答