必须先确认加密方式、密钥、iv及填充方式,再验证单文件解密正确性;批量解密需用openssl_decrypt配合openssl_raw_data,注意密钥/iv长度、二进制处理与错误排查,大文件应改用sodium或系统命令流式处理。

解密前先确认加密方式和密钥管理
PHP 本身不自带“批量解密”功能,所有解密操作都依赖于你明确知道原始加密方式(比如 openssl_encrypt、mcrypt 已废弃、或自定义 XOR)、密钥、IV(如果用了 CBC 模式)、以及填充方式。常见错误是拿到一堆 .enc 文件后直接写循环调用 base64_decode,结果解出来全是乱码——那只是编码,不是加密。
必须先验:用同一个文件手动跑一次单文件解密逻辑,验证输出是否正确。否则批量只是放大错误。
- 检查文件头或扩展名是否暗示加密方式(如
file.enc可能是 AES-128-CBC,file.xor可能是单字节异或) - 密钥不能硬编码在脚本里;建议从环境变量读取:
getenv('DECRYPT_KEY'),避免提交到 Git - 如果加密时用了随机 IV,它通常会 prepended 到密文前(如前 16 字节),解密时要先分离:
substr($data, 0, 16)和substr($data, 16)
用 openssl_decrypt 批量处理 AES 加密文件
这是目前最常见也最稳妥的批量解密路径。假设所有文件都是用 openssl_encrypt($plaintext, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv) 加密,并把 $iv . $ciphertext 存为二进制文件。
对应解密脚本核心逻辑如下:
$key = hex2bin(getenv('DECRYPT_KEY')); // 确保 key 是二进制,不是字符串
$files = glob('/path/to/*.enc');
foreach ($files as $file) {
$raw = file_get_contents($file);
$iv = substr($raw, 0, 16);
$ciphertext = substr($raw, 16);
$plaintext = openssl_decrypt($ciphertext, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
if ($plaintext === false) {
echo "解密失败: {$file} -> " . openssl_error_string() . "\n";
continue;
}
$outFile = preg_replace('/\.enc$/', '.dec', $file);
file_put_contents($outFile, $plaintext);
}
- 必须用
OPENSSL_RAW_DATA,不用OPENSSL_ZERO_PADDING(除非你确定加密时没补位) - 密钥长度必须匹配算法:AES-128 要 16 字节,AES-256 要 32 字节;
strlen($key) !== 16会导致静默失败 - 不要用
base64_encode包裹密文再存——那样得先base64_decode,但二进制直存更高效、无编码歧义
遇到 “data not valid” 或空字符串怎么办
这几乎总是 openssl_decrypt 返回 false 的表现,不是 PHP 报错,所以容易被忽略。真正原因往往藏在细节里:
- IV 长度不对:AES-CBC 要 16 字节,
strlen($iv) !== 16就会失败 - 密钥被意外 trim 或换行:用
var_dump(bin2hex($key))看真实字节,别信echo $key - 加密和解密用的 OpenSSL 后端不一致(比如加密用 LibreSSL,解密用旧版 OpenSSL),极少见但可能影响 padding 解析
- 文件被传输损坏:校验一下
md5_file($file)是否和原始加密后一致
调试时加一句:error_log("IV len: " . strlen($iv) . ", key len: " . strlen($key));,比猜快得多。
小文件够用,大文件(>100MB)要流式处理
上面的 file_get_contents 会把整个文件读进内存,解密一个 500MB 文件可能直接 OOM。此时必须用流式解密,但 PHP 的 openssl 扩展不支持流式 AES 解密——得换思路:
- 改用
ext-sodium(PHP 7.2+):它提供sodium_crypto_secretbox_open,支持分块,且 API 更安全 - 或者用系统命令委托:
exec("openssl enc -d -aes-128-cbc -K " . bin2hex($key) . " -iv " . bin2hex($iv) . " -in {$file} -out {$outFile} 2>&1", $output, $return),绕过内存限制 - 注意:用
exec时确保$key和$iv已 hex 编码,不能传二进制字符串进去
批量场景下,没人会手动点开每个文件验证结果。务必加校验环节:解密后对 .dec 文件做 hash_equals($expected_hash, md5_file($outFile)),不然 silently 错过几个文件,后期很难追溯。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











