早期 PHP 專案常用 mcrypt 擴充套件實現 AES 加密,但 mcrypt 已於 PHP 7.2 棄用、PHP 8.0 移除。本文提供一個符合現代標準的 AES 加密類,使用 OpenSSL 替代 mcrypt,支援 AES-128/192/256、ECB/CBC/GCM 模式、標準 PKCS7 Padding、隨機 IV 生成,並附遷移對照表。
<?php
class AesEncryption
{
private string $key; // 原始二進位金鑰(16/24/32 bytes)
private string $cipher; // OpenSSL cipher 名稱
private bool $useGcm; // 是否使用 GCM 模式
/**
* @param string $keyBase64 Base64 編碼的金鑰
* @param int $keyBits 128, 192, 或 256
* @param string $mode 'ecb', 'cbc', 'gcm'
*/
public function __construct(string $keyBase64, int $keyBits = 256, string $mode = 'cbc')
{
$this->key = base64_decode($keyBase64);
$expectedLen = $keyBits / 8;
if (strlen($this->key) !== $expectedLen) {
throw new InvalidArgumentException("金鑰長度錯誤:需 {$expectedLen} 位元組({$keyBits}-bit),實際 " . strlen($this->key));
}
$mode = strtolower($mode);
if ($mode === 'gcm') {
$this->cipher = "aes-{$keyBits}-gcm";
$this->useGcm = true;
} elseif ($mode === 'cbc') {
$this->cipher = "aes-{$keyBits}-cbc";
$this->useGcm = false;
} elseif ($mode === 'ecb') {
$this->cipher = "aes-{$keyBits}-ecb";
$this->useGcm = false;
} else {
throw new InvalidArgumentException("不支援的模式: $mode");
}
}
/** 加密:回傳 Base64 字串(格式:IV + 密文 + [GCM tag]) */
public function encrypt(string $plaintext): string
{
if ($this->useGcm) {
$iv = random_bytes(12); // GCM 建議 96-bit IV
$ciphertext = openssl_encrypt($plaintext, $this->cipher, $this->key, OPENSSL_RAW_DATA, $iv, $tag, '', 12);
if ($ciphertext === false) throw new RuntimeException('加密失敗: ' . openssl_error_string());
// 組合:IV (12) + 密文 + Tag (16)
return base64_encode($iv . $ciphertext . $tag);
} else {
$ivLen = openssl_cipher_iv_length($this->cipher);
$iv = $ivLen > 0 ? random_bytes($ivLen) : '';
$ciphertext = openssl_encrypt($plaintext, $this->cipher, $this->key, OPENSSL_RAW_DATA, $iv);
if ($ciphertext === false) throw new RuntimeException('加密失敗: ' . openssl_error_string());
// 組合:IV + 密文(ECB 模式 IV 為空)
return base64_encode($iv . $ciphertext);
}
}
/** 解密:輸入 Base64 字串,回傳明文 */
public function decrypt(string $ciphertextB64): string
{
$data = base64_decode($ciphertextB64, true);
if ($data === false) throw new InvalidArgumentException('Base64 解碼失敗');
if ($this->useGcm) {
if (strlen($data) < 12 + 16) throw new InvalidArgumentException('資料長度不足');
$iv = substr($data, 0, 12);
$tag = substr($data, -16);
$ciphertext = substr($data, 12, -16);
$plaintext = openssl_decrypt($ciphertext, $this->cipher, $this->key, OPENSSL_RAW_DATA, $iv, $tag, '', 12);
} else {
$ivLen = openssl_cipher_iv_length($this->cipher);
if ($ivLen > 0) {
if (strlen($data) < $ivLen) throw new InvalidArgumentException('資料長度不足含 IV');
$iv = substr($data, 0, $ivLen);
$ciphertext = substr($data, $ivLen);
} else {
$iv = '';
$ciphertext = $data;
}
$plaintext = openssl_decrypt($ciphertext, $this->cipher, $this->key, OPENSSL_RAW_DATA, $iv);
}
if ($plaintext === false) throw new RuntimeException('解密失敗: ' . openssl_error_string());
return $plaintext;
}
/** 靜態工廠:從隨機生成金鑰建立實例 */
public static function generateKey(int $keyBits = 256): string
{
return base64_encode(random_bytes($keyBits / 8));
}
}
?>
<?php
// 1. 產生金鑰(僅首次執行,妥善保存)
$keyB64 = AesEncryption::generateKey(256); // 例如:K7gNuZs9...==
// 存入 .env 或金鑰管理系統
// 2. 實例化(建議 CBC 或 GCM)
$aes = new AesEncryption($keyB64, 256, 'cbc'); // 或 'gcm'
// 3. 加密
$plaintext = '敏感資料:身分證 A123456789、信用卡 4242-4242-4242-4242';
$encrypted = $aes->encrypt($plaintext);
echo "加密結果:$encrypted\n";
// 4. 解密
$decrypted = $aes->decrypt($encrypted);
echo "解密還原:$decrypted\n";
assert($decrypted === $plaintext);
?>
| mcrypt | OpenSSL | 備註 |
|---|---|---|
MCRYPT_RIJNDAEL_128 | aes-128-*, aes-192-*, aes-256-* | Rijndael-128 區塊大小固定 128-bit,金鑰長度可變。OpenSSL cipher 名稱含金鑰長度。 |
MCRYPT_MODE_ECB | aes-*-ecb | 不需 IV,不安全,僅相容舊系統 |
MCRYPT_MODE_CBC | aes-*-cbc | 需 16-byte IV,標準模式 |
mcrypt_create_iv() | random_bytes() | mcrypt 的 IV 來源較弱,OpenSSL 強制用 CSPRNG |
| 手動 PKCS7 Padding | 內建自動處理 | OpenSSL 自動依標準填充/移除,無需手寫 |
mcrypt_encrypt() | openssl_encrypt() | 參數順序、常數名稱不同,請參考官方文件 |
區塊大小 16 bytes。若資料長度非 16 倍數,補齊 N 個值為 N 的位元組。
0x010x10(完整區塊也要補,以便解密時區分)解密時讀取最後一個位元組值 N,移除尾部 N 個位元組。
掌握 OpenSSL 版 AES 實現,不僅解決 mcrypt 移除帶來的遷移痛點,更能建構符合現代密碼學標準的資料保護體系。