我在使用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原生解决方案或其他一些PHP扩展,只要这是一个常见的。但是,改变iOS方面的东西是不是一个选项。
请帮忙!
答案 0 :(得分:3)
分组密码模式非常简单,如果两个实现不兼容,您可以自己实现它们。
以下是针对您的特定情况的点击率实施:
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
既可用作加密,也可用作解密功能。
答案 1 :(得分:0)
看起来PHP的mcrypt使用其他CTR模式(big endian而不是little endian)。关键时间表和IV都可以(正如您在解密第一个块时所看到的那样),但IV递增功能出现了问题。
请参阅以下PHP代码,该代码通过在ECB模式下手动递增IV和加密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);
?>