You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP实现兼容OpenSSL命令行的AES-256-CTR大文件加密问题

适配OpenSSL命令行的AES-256-CTR分块加密PHP实现

问题背景

需要编写PHP分块加密代码,复刻OpenSSL命令行的AES-256-CTR加密逻辑,确保PHP加密后的文件可通过以下命令解密:

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

现有分块加密代码无法兼容命令行解密,核心问题出在CTR模式的IV/计数器处理逻辑上。

核心问题分析

OpenSSL的AES-256-CTR模式遵循以下规则:

  • IV总长度为16字节(128位),由8字节随机nonce + 8字节大端序计数器组成,初始计数器值为0
  • 每加密一个16字节的块,计数器自动递增1
  • PHP的openssl_encrypt函数在分块调用时不会自动维护计数器状态,必须手动跟踪并更新计数器值
  • CTR模式属于流加密,不需要填充,OpenSSL命令行默认不启用填充,因此PHP代码中不应添加OPENSSL_ZERO_PADDING

修正后的代码实现

// 生成8字节随机salt
$salt = random_bytes(8);
$saltPrefix = "Salted__" . $salt;

// 通过EVP_BytesToKey生成密钥和初始IV(与OpenSSL命令行逻辑一致)
$keyIV = EVP_BytesToKey($salt, file_get_contents('my.key'));
$key = substr($keyIV, 0, 32); // AES-256需要32字节密钥
$initialIV = substr($keyIV, 32, 16); // CTR模式IV长度为16字节

$FILE_ENCRYPTION_BLOCKS = 10000;
$blockSize = 16; // AES块大小固定16字节
$currentCounter = unpack('J', substr($initialIV, 8, 8))[1]; // 提取初始计数器(大端64位无符号整数)

if ($fpOut = fopen('php-encrypted.bin', 'wb')) {
    fwrite($fpOut, $saltPrefix);
    
    if ($fpIn = fopen('confidential.txt', 'rb')) {
        while (!feof($fpIn)) {
            $plaintext = fread($fpIn, $blockSize * $FILE_ENCRYPTION_BLOCKS);
            $plaintextLen = strlen($plaintext);
            if ($plaintextLen === 0) break;
            
            // 生成当前块对应的IV:nonce + 更新后的计数器
            $currentIV = substr($initialIV, 0, 8) . pack('J', $currentCounter);
            
            // CTR模式不需要填充,仅用OPENSSL_RAW_DATA
            $encrypted = openssl_encrypt($plaintext, 'AES-256-CTR', $key, OPENSSL_RAW_DATA, $currentIV);
            fwrite($fpOut, $encrypted);
            
            // 计算本次加密的块数,更新计数器
            $blockCount = (int)ceil($plaintextLen / $blockSize);
            $currentCounter += $blockCount;
        }
        fclose($fpIn);
    }
    fclose($fpOut);
}

// 实现OpenSSL兼容的EVP_BytesToKey算法
function EVP_BytesToKey($salt, $password) {
    $keyIV = '';
    $data = '';
    while (strlen($keyIV) < 32 + 16) { // 32字节密钥 + 16字节IV
        $data .= $password . $salt;
        $data = md5($data, true);
        $keyIV .= $data;
    }
    return $keyIV;
}

关键细节说明

  1. EVP_BytesToKey实现:严格遵循OpenSSL默认的密钥派生逻辑(MD5哈希、无迭代次数),确保生成的密钥和初始IV与命令行完全一致
  2. 计数器维护:从初始IV中提取8字节计数器(大端64位),每次加密后根据处理的块数递增计数器,确保后续分块使用正确的IV
  3. 无填充处理:CTR模式为流加密,无需填充,因此去掉OPENSSL_ZERO_PADDING,避免与OpenSSL命令行的默认行为冲突
  4. 分块加密性能:保持原有的分块读取逻辑,避免一次性加载大文件到内存,适合加密大体积文件

内容的提问来源于stack exchange,提问作者Bilal H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 13:05:36