如何用PHP实现openssl_encrypt模拟指定AES256-CBC加密命令?
实现OpenSSL命令对应的PHP加密逻辑
我之前也踩过这个坑!OpenSSL命令行的-k参数藏了不少隐式操作,而PHP的openssl_encrypt不会自动帮你做这些,得手动还原整个流程才行。
先拆解原OpenSSL命令的行为
你的命令echo "TEST" | openssl enc -e -aes-256-cbc -k 123456 -a -md md5背地里做了这些事:
- 自动生成一个8字节的随机salt(没加
-nosalt的话默认会这么干) - 用EVP_BytesToKey算法,结合密码
123456、生成的salt、MD5哈希,生成32字节的AES-256密钥和16字节的CBC初始化向量(IV) - 对输入内容加密(注意:
echo "TEST"在bash下会自动加换行符,所以实际加密的是TEST\n;如果要加密不带换行的TEST,得去掉这个换行),用的是PKCS#7填充(OpenSSL默认的填充方式) - 把前缀
Salted__+ salt + 加密后的密文拼在一起,最后做Base64编码输出
PHP实现代码
下面是完整的可运行代码,每一步都加了注释:
<?php function openssl_encrypt_compatible($plaintext, $password, $digest = 'md5') { // 生成8字节随机salt,和OpenSSL命令默认行为对齐 $salt = openssl_random_pseudo_bytes(8); // 用EVP_BytesToKey算法生成密钥和IV $keyIvPair = evpBytesToKey($password, $salt, $digest, 32, 16); $key = $keyIvPair['key']; $iv = $keyIvPair['iv']; // 执行AES-256-CBC加密,输出原始二进制数据(不是base64) $ciphertext = openssl_encrypt( $plaintext, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv ); // 拼接前缀、salt、密文后做base64编码 $result = base64_encode("Salted__" . $salt . $ciphertext); return $result; } // 手动实现EVP_BytesToKey算法,这是和命令行对齐的关键 function evpBytesToKey($password, $salt, $digest, $keyLength, $ivLength) { $data = ''; $key = ''; $iv = ''; // 循环生成足够长度的key和iv while (strlen($key) < $keyLength || strlen($iv) < $ivLength) { $data .= $password . $salt; $hash = hash($digest, $data, true); // 输出二进制哈希 $data = $hash; // 从哈希中截取需要的key部分 if (strlen($key) < $keyLength) { $key .= substr($hash, 0, $keyLength - strlen($key)); } // 截取iv部分(在key之后的哈希内容) if (strlen($iv) < $ivLength) { $remaining = strlen($hash) - strlen($key) + ($keyLength - strlen($key)); if ($remaining > 0) { $iv .= substr($hash, strlen($key) - $keyLength, $ivLength - strlen($iv)); } } } return [ 'key' => $key, 'iv' => $iv ]; } // 测试:这里的换行符对应echo "TEST"的输出 $plaintext = "TEST\n"; $password = "123456"; $encrypted = openssl_encrypt_compatible($plaintext, $password); echo $encrypted . "\n"; ?>
验证和命令行的一致性
如果想确认代码和OpenSSL命令的结果完全一致,可以固定salt来测试:
- 先运行指定salt的OpenSSL命令(比如salt是
abcdefgh,十六进制是6162636465666768):
echo "TEST" | openssl enc -e -aes-256-cbc -k 123456 -a -md md5 -S 6162636465666768
- 在PHP代码里把
$salt = openssl_random_pseudo_bytes(8);改成$salt = hex2bin('6162636465666768');,运行代码后得到的base64结果应该和命令输出完全一样。
几个要注意的点
- 如果你不需要加密带换行的内容,把
$plaintext = "TEST\n";改成$plaintext = "TEST";就行 - EVP_BytesToKey是比较老的密钥派生算法,新项目更推荐用PBKDF2或者Argon2,但为了和你这条OpenSSL命令兼容,必须用它
- 确保你的PHP已经启用了OpenSSL扩展(一般默认都是启用的)
内容的提问来源于stack exchange,提问作者daziwuso




