在 PHP 中实现与 OpenSSL 兼容的 ‌AES 加密(含 Salted__ 头)‌,需手动处理盐值生成和密钥派生。以下是完整实现代码及分步说明:

一、加密实现代码

<?php
/**
 * AES 加密(兼容 OpenSSL 的 "Salted__" 格式)
 * @param string $data 明文数据
 * @param string $password 加密密码
 * @param string $method 加密算法,如 'AES-256-CBC'
 * @return string Base64 编码的加密数据(含 Salted__ 头)
 */
function opensslEncryptWithSalt($data, $password, $method = 'AES-256-CBC') {
    // 生成 8 字节随机盐
    $salt = openssl_random_pseudo_bytes(8);
    
    // 通过盐和密码派生密钥与 IV
    $keyIv = deriveKeyAndIV($password, $salt, $method);
    $key = $keyIv['key'];
    $iv = $keyIv['iv'];
    
    // 加密数据
    $encrypted = openssl_encrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);
    
    // 拼接 Salted__ 头 + 盐 + 密文
    $result = "Salted__" . $salt . $encrypted;
    
    // Base64 编码便于传输
    return base64_encode($result);
}

/**
 * 派生密钥和 IV(模拟 OpenSSL 的 EVP_BytesToKey)
 */
function deriveKeyAndIV($password, $salt, $method) {
    $digest = 'md5'; // OpenSSL 默认使用 MD5
    $keySize = 32;   // AES-256 需要 32 字节密钥
    $ivSize = 16;    // CBC 模式需要 16 字节 IV
    
    $derived = '';
    $data = '';
    $length = 0;
    
    while ($length < $keySize + $ivSize) {
        $data = $data . $password . $salt;
        $md5 = hash($digest, $data, true);
        $data = $md5;
        $derived .= $md5;
        $length += strlen($md5);
    }
    
    return [
        'key' => substr($derived, 0, $keySize),
        'iv'  => substr($derived, $keySize, $ivSize)
    ];
}

二、解密实现代码

<?php
/**
 * AES 解密(兼容 OpenSSL 的 "Salted__" 格式)
 * @param string $encryptedData Base64 编码的加密数据
 * @param string $password 解密密码
 * @param string $method 加密算法,如 'AES-256-CBC'
 * @return string 解密后的明文
 */
function opensslDecryptWithSalt($encryptedData, $password, $method = 'AES-256-CBC') {
    // 解码 Base64
    $data = base64_decode($encryptedData);
    
    // 提取盐(前 8 字节,跳过 "Salted__" 头)
    $salt = substr($data, 8, 8);
    
    // 提取密文(去除头部的 16 字节:Salted__ + 盐)
    $encrypted = substr($data, 16);
    
    // 派生密钥和 IV
    $keyIv = deriveKeyAndIV($password, $salt, $method);
    $key = $keyIv['key'];
    $iv = $keyIv['iv'];
    
    // 解密数据
    return openssl_decrypt($encrypted, $method, $key, OPENSSL_RAW_DATA, $iv);
}

三、使用示例

<?php
// 加密示例
$plaintext = "Hello, World!";
$password = "mySecretPassword";
$encrypted = opensslEncryptWithSalt($plaintext, $password);
echo "加密结果: " . $encrypted . "\n";

// 解密示例
$decrypted = opensslDecryptWithSalt($encrypted, $password);
echo "解密结果: " . $decrypted . "\n";

四、核心逻辑说明

‌1. 盐值生成‌

使用 openssl_random_pseudo_bytes(8) 生成 8 字节随机盐,确保每次加密结果不同。

2‌. 密钥派生‌

通过 ‌EVP_BytesToKey‌ 算法(模拟 OpenSSL 默认行为)派生密钥和 IV:

使用 MD5 哈希(出于兼容性考虑,尽管不够安全)
多次迭代拼接 $password + $salt 生成足够长度的密钥和 IV

‌3. 数据格式‌

加密结果格式为:

Base64编码( "Salted__" + 8字节盐 + 密文 )
  1. ‌算法兼容性‌

支持 AES-128-CBC、AES-256-CBC 等常见算法
与 OpenSSL 命令行加密结果互解:

# 加密
openssl enc -aes-256-cbc -pass pass:mySecretPassword -S $(echo -n "SALT_HEX" | xxd -r -p) -a -p

五、注意事项

1‌. 安全性建议‌

优先使用更安全的密钥派生方式(如 PBKDF2),但需放弃与 OpenSSL 的兼容性
避免使用弱密码或固定盐值

‌2. 错误处理‌

添加异常捕获:

<?php
try {
    $encrypted = openssl_encrypt(...);
    if ($encrypted === false) {
        throw new Exception('加密失败: ' . openssl_error_string());
    }
} catch (Exception $e) {
    die("错误: " . $e->getMessage());
}

3‌. 编码格式‌

明文数据需为二进制格式(若含中文需统一编码,如 UTF-8)
密文通过 Base64 编码避免传输乱码

通过上述代码,可实现与 OpenSSL Salted__ 格式完全兼容的 AES 加密/解密,确保跨平台数据交换的可靠性。

标签: none

添加新评论