使用与 openssl 命令行兼容的 AES-256-CTR 为大文件创建 openssl 加密的 php 代码

create php code for large files openssl encrypt using AES-256-CTR compatible with openssl command line

提问人:Bilal H 提问时间:11/15/2023 最后编辑:Bilal H 更新时间:11/16/2023 访问量:81

问:

我正在尝试创建一个PHP方法来复制OpenSSL命令行函数,以便我可以使用PHP进行加密,然后使用命令行对其进行解密。

我创建了PHP方法来加密文件,同时考虑到内存性能和限制问题,通过加密部分上的文件:

$salt= random_bytes(8);
$saltPrefix = "Salted__" . $salt;
$keyIV= EVP_BytesToKey($salt, file_get_contents('my.key'));
$key = substr($keyIV, 0, 32);
$iv= substr($keyIV, 32, openssl_cipher_iv_length('AES-256-CTR'));
$original = file_get_contents('confidential.txt');
$FILE_ENCRYPTION_BLOCKS = 10000;
if ($fpOut = fopen('php-encrypted.bin', 'w')) {
    fwrite($fpOut, $saltPrefix);
    if ($fpIn = fopen('confidential.txt', 'rb')) {
        while (!feof($fpIn)) {
            $plaintext = fread($fpIn, 16 * $FILE_ENCRYPTION_BLOCKS);
            $encrypted = openssl_encrypt($plaintext, 'AES-256-CTR', $key, (feof($fpIn) ? OPENSSL_RAW_DATA:OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING), $iv);
            fwrite($fpOut, $encrypted);
        }
        fclose($fpIn);
    }
    fclose($fpOut);
}

然后在运行此方法后,我可以使用以下命令行对其进行解密:

openssl  enc -p -d -aes-256-ctr -in php-encrypted.bin -out decrypted.txt -pass file:my.key

但是当我尝试使用上面的命令行解密生成的文件时,它不起作用。我需要更新上述方法以使用 CTR 模式模拟命令行加密。我读到$iv必须包含nonce和counter,但是必须如何完成以及生成计数器的函数,或者只是将固定的位置放在循环中的第二次调用中从to更改为或如何在上面的PHP代码中完成是安全的?$iv = $randomBytes . $counter;$randomBytes$counter12

php openssl aes 大文件 php-openssl

评论

0赞 Bilal H 11/15/2023
我尝试添加并在循环中添加,但也无法正常工作。$counter = 0$counterBytes = pack('J', ++$counter); $iv = substr($firstIV, 0, strlen($firstIV) - strlen($counterBytes)) . $counterBytes;
0赞 Topaco 11/15/2023
为什么要这样做?您对每个块使用相同的 IV/计数器(关于问题中的代码)。查看点击率流程图。您必须将每个块的计数器增加(例如,使用 gmp)。$FILE_ENCRYPTION_BLOCKS
0赞 Topaco 11/15/2023
此外,CTR 是一种流密码模式,PHP/OpenSSL 隐式禁用填充。
0赞 Bilal H 11/15/2023
在第一条评论中,我更新了代码,因此计数器全 IV 在循环中发生了变化,这是更新的脚本,但仍然无法成功解密。在示例中,我使用小数字,其中只是$FILE_ENCRYPTION_BLOCKS10

答:

1赞 Topaco 11/15/2023 #1

在 CTR 模式下,初始 IV 会随着每个后续模块的增加而增加,请参阅 CTR 流程图。这个所谓的计数器是加密的。结果,生成一个密钥流,该密钥流与明文进行异或运算。

如果加密是在由块(每个块 16 个字节)组成的块中执行的,则后续块的计数器必须相应地递增 。$FILE_ENCRYPTION_BLOCKS$FILE_ENCRYPTION_BLOCKS

$iv是一个二进制字符串,可以使用 gmp_import() 转换为数字(以及 和 的默认值),使用 gmp_export() 递增并转换回二进制字符串。一种可能的实现方式是:$word_size$flags

$FILE_ENCRYPTION_BLOCKS = 10000;
$salt= random_bytes(8);
$saltPrefix = 'Salted__' . $salt;
$keyIV = EVP_BytesToKey($salt, file_get_contents('my.key'));
$key = substr($keyIv, 0, 32);
$iv = substr($keyIv, 32, 16);
if ($fpOut = fopen('php-encrypted.bin', 'wb')) {
    fwrite($fpOut, $saltPrefix);
    if ($fpIn = fopen('confidential.txt', 'rb')) {
        while (!feof($fpIn)) {
            $plaintext = fread($fpIn, 16 * $FILE_ENCRYPTION_BLOCKS);
            $encrypted = openssl_encrypt($plaintext, 'AES-256-CTR', $key, OPENSSL_RAW_DATA, $iv);   // padding implicitly disabled for CTR
            $iv = gmp_export(gmp_import($iv) + $FILE_ENCRYPTION_BLOCKS);                            // increment counter
            fwrite($fpOut, $encrypted);
        }
        fclose($fpIn);
    }
    fclose($fpOut);
} 

以这种方式生成的密文可以通过以下方式解密:

openssl  enc -p -d -aes-256-ctr -in ciphertext.bin -out decrypted.txt -pass file:my.key

为了完整起见:流密码模式(如 CTR)不需要填充。OpenSSL(以及 PHP 包装器)会自动禁用此功能。

评论

0赞 Bilal H 11/16/2023
谢谢@Topaco,这是关键,$iv作为一个数字是完整的,而不是将固定字符串与计数器数字连接起来。$iv = gmp_export(gmp_import($iv) + $FILE_ENCRYPTION_BLOCKS);