在 PHP 的 mcrypt 中解密加密 AES-CTR 文件的随机块
Decrypt random chunk of an encrypted AES-CTR file in PHP's mcrypt
我有一个 1MB 的测试文件,我想从 500KB 开始解密它,而不是从头开始。它不需要正好从文件的 500KB 开始,它可以从任何块的开头开始,只要它不是第一个,我只是想学习如何做。
用这个脚本我可以解密文件,只要它从 0KB 开始。
$file = file_get_contents("file.dat");
$aeskey = base64_decode("sVv2g7boc/pzCDepDfV1VA==");
$iv = base64_decode("A5chWWE3D4cAAAAAAAAAAA==");
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'ctr', '');
mcrypt_generic_init($td, $aeskey, $iv);
echo mdecrypt_generic($td, $file);
至少有人可以解释一下吗?
在CTR mode中,一个计数器(AES 为 128 位)被加密以产生一个密钥流,然后与明文或密文进行异或运算。通常,假设 IV 为 64 位或 96 位,其余位实际上设置为 0。最初的 64 位或 96 位称为 nonce。
nonce 的大小决定了在不创建多次填充的情况下一次可以加密多少数据:nonce 越大,安全消息长度越小,但两个 nonce 发生冲突的概率也越低它们是随机生成的。由于没有指定 nonce 的大小,因此许多框架不会将 nonce 的大小限制为特定大小。
您可以在 mcrypt 中为随机数使用完整的块大小。
你可以
- 取一开始使用的IV,
- 将 IV 解析为一个大整数(它不适合 PHP 整数类型),
- 向其添加一个数字,它代表您想要的块数(AES 为 16 字节块)跳过,
- 将数字转换回二进制表示并
- 从后面的字节开始解密。
步骤 2-4 由以下代码中的 add
函数完成。
假设您有一个大文件,但想从字节 512(为简单起见,块大小的倍数)开始解密。您可以将 512/16=32 添加到 IV.
下面是一些示例代码:
<?php
$d = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
$k = "k0k1k2k3k4k5k6k7"; // 16 byte AES key
$bs = 16; // 16 byte block size
$iv = mcrypt_create_iv($bs);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'ctr', '');
mcrypt_generic_init($td, $k, $iv);
$ct = mcrypt_generic($td, $d);
$dec_offset = 32;
$ct_slice = substr($ct, $dec_offset);
$iv_slice = add($iv, $dec_offset / $bs);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'ctr', '');
mcrypt_generic_init($td, $k, $iv_slice);
$d_slice = mdecrypt_generic($td, $ct_slice);
var_dump($d_slice);
function add($big_num_str, $to_add){
$big_num = str_split(strrev($big_num_str));
for($i = 0; $i < count($big_num) ; $i++){
$tmp = ord($big_num[$i]) + $to_add;
$big_num[$i] = $tmp % 256;
$to_add = floor( $tmp / 256 );
}
while($to_add){
$big_num[$i++] = $to_add % 256;
$to_add = floor( $to_add / 256 );
}
for($i = 0; $i < count($big_num) ; $i++){
$big_num[$i] = chr($big_num[$i]);
}
return strrev(implode('', $big_num) );
}
输出:
string(32) "101112131415161718191a1b1c1d1e1f"
对于 PHP 中的 OpenSSL 扩展,这也以完全相同的方式工作。 Here是代码。
当然,如果你想获得一个不在块边界上开始的块,这会稍微复杂一些。您必须更早开始一个块并删除多余的字节。
我有一个 1MB 的测试文件,我想从 500KB 开始解密它,而不是从头开始。它不需要正好从文件的 500KB 开始,它可以从任何块的开头开始,只要它不是第一个,我只是想学习如何做。
用这个脚本我可以解密文件,只要它从 0KB 开始。
$file = file_get_contents("file.dat");
$aeskey = base64_decode("sVv2g7boc/pzCDepDfV1VA==");
$iv = base64_decode("A5chWWE3D4cAAAAAAAAAAA==");
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'ctr', '');
mcrypt_generic_init($td, $aeskey, $iv);
echo mdecrypt_generic($td, $file);
至少有人可以解释一下吗?
在CTR mode中,一个计数器(AES 为 128 位)被加密以产生一个密钥流,然后与明文或密文进行异或运算。通常,假设 IV 为 64 位或 96 位,其余位实际上设置为 0。最初的 64 位或 96 位称为 nonce。
nonce 的大小决定了在不创建多次填充的情况下一次可以加密多少数据:nonce 越大,安全消息长度越小,但两个 nonce 发生冲突的概率也越低它们是随机生成的。由于没有指定 nonce 的大小,因此许多框架不会将 nonce 的大小限制为特定大小。
您可以在 mcrypt 中为随机数使用完整的块大小。
你可以
- 取一开始使用的IV,
- 将 IV 解析为一个大整数(它不适合 PHP 整数类型),
- 向其添加一个数字,它代表您想要的块数(AES 为 16 字节块)跳过,
- 将数字转换回二进制表示并
- 从后面的字节开始解密。
步骤 2-4 由以下代码中的 add
函数完成。
假设您有一个大文件,但想从字节 512(为简单起见,块大小的倍数)开始解密。您可以将 512/16=32 添加到 IV.
下面是一些示例代码:
<?php
$d = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
$k = "k0k1k2k3k4k5k6k7"; // 16 byte AES key
$bs = 16; // 16 byte block size
$iv = mcrypt_create_iv($bs);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'ctr', '');
mcrypt_generic_init($td, $k, $iv);
$ct = mcrypt_generic($td, $d);
$dec_offset = 32;
$ct_slice = substr($ct, $dec_offset);
$iv_slice = add($iv, $dec_offset / $bs);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'ctr', '');
mcrypt_generic_init($td, $k, $iv_slice);
$d_slice = mdecrypt_generic($td, $ct_slice);
var_dump($d_slice);
function add($big_num_str, $to_add){
$big_num = str_split(strrev($big_num_str));
for($i = 0; $i < count($big_num) ; $i++){
$tmp = ord($big_num[$i]) + $to_add;
$big_num[$i] = $tmp % 256;
$to_add = floor( $tmp / 256 );
}
while($to_add){
$big_num[$i++] = $to_add % 256;
$to_add = floor( $to_add / 256 );
}
for($i = 0; $i < count($big_num) ; $i++){
$big_num[$i] = chr($big_num[$i]);
}
return strrev(implode('', $big_num) );
}
输出:
string(32) "101112131415161718191a1b1c1d1e1f"
对于 PHP 中的 OpenSSL 扩展,这也以完全相同的方式工作。 Here是代码。
当然,如果你想获得一个不在块边界上开始的块,这会稍微复杂一些。您必须更早开始一个块并删除多余的字节。