You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用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来测试:

  1. 先运行指定salt的OpenSSL命令(比如salt是abcdefgh,十六进制是6162636465666768):
echo "TEST" | openssl enc -e -aes-256-cbc -k 123456 -a -md md5 -S 6162636465666768
  1. 在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

火山引擎 最新活动