PHP与OpenSSL命令行实现大文件互加解密的兼容问题
PHP大文件分块加密与OpenSSL命令行兼容方案
问题背景
需要实现PHP与OpenSSL命令行的互加解密:PHP加密的文件能通过OpenSSL命令解密,反之亦然。小文件加密方案可正常通过以下命令解密:
openssl enc -p -d -aes-256-cbc -in php-encrypted.bin -out decrypted.txt -pass file:my.key
但针对大文件修改为分块加密后,生成的文件无法被上述命令解密,需调整分块逻辑,兼顾内存占用与兼容性。
问题根源
原分块加密代码存在两个关键问题:
- CBC模式下,每一块加密的IV应该是上一块加密后的密文(初始IV由EVP_BytesToKey生成),但原代码每次都使用初始IV,破坏了CBC的链式加密逻辑。
- 未正确处理分块加密时的状态延续,导致密文格式与OpenSSL命令行生成的不兼容。
修正后的大文件分块加密代码
1. 实现OpenSSL兼容的EVP_BytesToKey函数
PHP没有内置该函数,需手动实现:
function EVP_BytesToKey(string $salt, string $password): string { $keyIv = ''; $data = ''; while (strlen($keyIv) < 32 + 16) { // AES-256需要32位key + 16位IV $data = hash('md5', $data . $password . $salt, true); $keyIv .= $data; } return $keyIv; }
2. 修正后的分块加密逻辑
$salt = random_bytes(8); $saltPrefix = "Salted__" . $salt; $password = file_get_contents('my.key'); $keyIv = EVP_BytesToKey($salt, $password); $key = substr($keyIv, 0, 32); $iv = substr($keyIv, 32, 16); // 初始IV $FILE_ENCRYPTION_BLOCKS = 64; // 每块处理64*16=1024字节 $blockSize = 16; // AES块大小 if ($fpOut = fopen('php-encrypted.bin', 'w')) { fwrite($fpOut, $saltPrefix); if ($fpIn = fopen('confidential.txt', 'rb')) { while (!feof($fpIn)) { $plaintext = fread($fpIn, $blockSize * $FILE_ENCRYPTION_BLOCKS); // 加密:OPENSSL_RAW_DATA会自动添加PKCS#7填充 $encrypted = openssl_encrypt( $plaintext, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv ); fwrite($fpOut, $encrypted); // 更新IV:CBC模式下,下一块的IV是当前块的密文最后16字节 $iv = substr($encrypted, -$blockSize); } fclose($fpIn); } fclose($fpOut); }
验证方法
用上述代码加密大文件后,执行原OpenSSL解密命令即可正常解密:
openssl enc -p -d -aes-256-cbc -in php-encrypted.bin -out decrypted.txt -pass file:my.key
额外说明
- 确保
my.key文件的编码和权限正确(命令行读取时不会出现问题)。 - 分块大小可根据内存情况调整,建议保持为AES块大小(16字节)的整数倍。
内容的提问来源于stack exchange,提问作者Bilal H
相关产品推荐
相关产品推荐

