
MySQL 的 AES_ENCRYPT() 与 CodeIgniter 加密类默认行为不兼容,因二者在密钥处理、填充方式、编码格式及加密模式上存在本质差异;需统一为 ECB 模式、PKCS#7 填充、128 位密钥派生及二进制/十六进制双向转换,方可获得可互解的密文。
mysql 的 `aes_encrypt()` 与 codeigniter 加密类默认行为不兼容,因二者在密钥处理、填充方式、编码格式及加密模式上存在本质差异;需统一为 ecb 模式、pkcs#7 填充、128 位密钥派生及二进制/十六进制双向转换,方可获得可互解的密文。
要使 MySQL 的 AES_ENCRYPT() 与 CodeIgniter(v3.x,基于 OpenSSL 驱动)生成完全相同的加密结果,并确保双向解密(MySQL 解密 CodeIgniter 密文,或反之)成功,必须严格对齐以下 5 个关键参数:
✅ 1. 统一加密算法与模式
- MySQL
AES_ENCRYPT(str, key)默认使用 AES-128-ECB(注意:MySQL 5.7+ 实际为 ECB,且不支持 IV,无 CBC 等模式); - CodeIgniter 必须显式指定:
$this->encryption->initialize([ 'cipher' => 'aes-128', 'mode' => 'ecb', // 关键:必须为 'ecb' 'key' => 'sabar', // 原始密钥(后续需处理长度) 'driver' => 'openssl' ]);
✅ 2. 密钥标准化:补足至 16 字节(AES-128)
MySQL 会自动对密钥进行截断或填充(旧版行为不透明),而 OpenSSL 要求密钥长度严格匹配。'sabar' 仅 5 字节,需扩展为 16 字节。推荐使用 PKCS#5/PKCS#7 兼容的密钥派生:
function padKey($key, $length = 16) {
$key = substr($key, 0, $length);
return str_pad($key, $length, "\0", STR_PAD_RIGHT); // 简单零填充(与 MySQL 行为最接近)
}
$key_16 = padKey('sabar'); // → 'sabar' . "\0\0\0\0\0\0\0\0\0\0"
⚠️ 注意:MySQL 实际内部使用的是 零填充密钥(非哈希),因此避免使用 md5() 或 sha256() 等哈希派生——这将导致结果不一致。
✅ 3. 明文填充:强制 PKCS#7(CodeIgniter 默认不填充 ECB)
ECB 模式要求明文长度为块大小(16 字节)的整数倍。MySQL 自动执行 PKCS#7 填充,而 CodeIgniter 的 encrypt() 在 ECB 下默认不填充(导致长度错位)。必须手动填充:
function pkcs7_pad($data, $block_size = 16) {
$pad_len = $block_size - (strlen($data) % $block_size);
return $data . str_repeat(chr($pad_len), $pad_len);
}
$plain = 'sabar';
$padded = pkcs7_pad($plain); // 'sabar' → 'sabar\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b' (11 chars + 5 padding = 16)
// 使用填充后明文加密
$ciphertext_bin = openssl_encrypt($padded, 'AES-128-ECB', $key_16, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
✅ 4. 编码一致性:MySQL 输出是二进制,需转为十六进制比对
MySQL 的 AES_ENCRYPT() 返回 二进制 BLOB,但客户端常以十六进制字符串显示(如 hÕ nkPRºY]°c Z 是乱码显示,实际应 HEX(AES_ENCRYPT(...))):
SELECT HEX(AES_ENCRYPT('sabar', 'sabar'));
-- 输出类似:68D5A06E6B5052BAA8595DB063205A0C (32字符 hex)
而 CodeIgniter 默认输出 Base64。要对比,需将 PHP 结果转为 hex:
echo bin2hex($ciphertext_bin); // 与 MySQL HEX(...) 结果逐字节比对
✅ 5. 完整可验证示例(PHP + MySQL 对齐)
// Step 1: 准备密钥和明文
$key_raw = 'sabar';
$plain = 'sabar';
// Step 2: 标准化密钥(16字节零填充)
$key_16 = str_pad(substr($key_raw, 0, 16), 16, "\0", STR_PAD_RIGHT);
// Step 3: PKCS#7 填充明文
$pad_len = 16 - (strlen($plain) % 16);
$padded = $plain . str_repeat(chr($pad_len), $pad_len);
// Step 4: 加密(原始二进制输出)
$cipher_bin = openssl_encrypt($padded, 'AES-128-ECB', $key_16, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
// Step 5: 转为十六进制(用于与 MySQL 比对)
$hex_result = bin2hex($cipher_bin);
echo "PHP hex: " . $hex_result . "\n"; // 如:68d5a06e6b5052baa8595db063205a0c
// Step 6: MySQL 中执行:
// SELECT HEX(AES_ENCRYPT('sabar', 'sabar')); → 应得相同 hex 字符串
⚠️ 重要注意事项
- ECB 模式不安全:仅用于兼容遗留系统,生产环境务必改用 CBC/GCM 模式(需同步 IV、HMAC 校验等);
- CodeIgniter v4 已弃用
Encryption类,推荐迁移到CodeIgniter4\Security\Encryption或原生openssl_*; - MySQL 8.0.30+ 引入
AES_ENCRYPT(str, key, init_vector)支持 CBC,但需三方完全对齐 IV 和填充; - 解密时,MySQL
AES_DECRYPT()会自动移除 PKCS#7 填充,PHP 侧需用pkcs7_unpad()手动清理。
通过以上五步对齐,即可实现 MySQL 与 CodeIgniter 的 AES 加密结果 100% 一致,并支持双向无损解密。核心在于:密钥零填充、明文 PKCS#7 填充、二进制流处理、十六进制比对——而非依赖高层封装的“开箱即用”行为。











