開放平台、金流、ERP 介接常要求請求/回應資料加密防止中間人竊取/竄改。AES-CBC + PKCS7 是業界標準配置。本文提供完整可用的 Crypt_AES 類、Client 端加密請求、Server 端解密驗證、簽名防重放完整流程。
| 參數 | 說明 | 範例 |
|---|---|---|
| Key | 16/24/32 位元組(AES-128/192/256) | 1qa2ws4rf3edzxcv (16 bytes) |
| IV | 16 位元組初始向量 | dfg452ws (8 chars → 需擴展) |
| Mode | CBC(需 IV) | CBC |
| Padding | PKCS#7 | 啟用 |
| 編碼 | 加密後 Base64 → URL encode | |
| 簽名 | MD5(content + md5Key) |
<?php
/**
* AES 加解密工具類(教學用,生產建議用 openssl_encrypt)
*/
class Crypt_AES {
private string $cipher = 'aes-128-cbc'; // OpenSSL cipher name
private string $key;
private string $iv;
private bool $pkcs7 = true;
public function __construct(string $key, string $iv, bool $pkcs7 = true) {
$this->key = $key;
$this->iv = $iv;
$this->pkcs7 = $pkcs7;
}
public function encrypt(string $data): string {
$data = $this->pkcs7 ? $this->pkcs7Pad($data) : $data;
return openssl_encrypt($data, $this->cipher, $this->key, OPENSSL_RAW_DATA, $this->iv);
}
public function decrypt(string $data): string {
$decrypted = openssl_decrypt($data, $this->cipher, $this->key, OPENSSL_RAW_DATA, $this->iv);
return $this->pkcs7 ? $this->pkcs7Unpad($decrypted) : $decrypted;
}
private function pkcs7Pad(string $data): string {
$block = 16;
$pad = $block - (strlen($data) % $block);
return $data . str_repeat(chr($pad), $pad);
}
private function pkcs7Unpad(string $data): string {
$pad = ord($data[strlen($data) - 1]);
if ($pad < 1 || $pad > 16) return $data; // 非法填充
return substr($data, 0, -$pad);
}
// 十六進制字串轉二進位
public static function hex2bin(string $hex): string {
return pack('H*', $hex);
}
// 字串轉十六進制
public static function strToHex(string $str): string {
return bin2hex($str);
}
}
?>
<?php
require 'Crypt_AES.php';
// 協商參數
$md5Key = 'ThisIsAMd5Key';
$aesKey = Crypt_AES::hex2bin(Crypt_AES::strToHex('1qa2ws4rf3edzxcv')); // 16 bytes
$aesIV = Crypt_AES::hex2bin(Crypt_AES::strToHex('dfg452ws')); // 需 16 bytes,這裡示範擴展
$aes = new Crypt_AES($aesKey, $aesIV, true);
$data = [
'name' => 'idoubi',
'sex' => 'male',
'age' => 23,
'signature' => '白天我是一個程序員,晚上我就是一個有夢想的演員。',
];
// 1. JSON 序列化
$json = json_encode($data, JSON_UNESCAPED_UNICODE);
// 2. AES 加密
$encrypted = $aes->encrypt($json);
// 3. Base64 編碼
$b64 = base64_encode($encrypted);
// 4. URL 編碼
$content = urlencode($b64);
// 5. 簽名
$sign = md5($content . $md5Key);
// 6. 發送 POST
$params = "version=1.0&sign=$sign&content=$content";
$ch = curl_init('https://localhost/aesdemo/api.php');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $params,
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
curl_close($ch);
var_dump(json_decode($response, true));
?>
<?php
require 'Crypt_AES.php';
$md5Key = 'ThisIsAMd5Key';
$aesKey = Crypt_AES::hex2bin(Crypt_AES::strToHex('1qa2ws4rf3edzxcv'));
$aesIV = Crypt_AES::hex2bin(Crypt_AES::strToHex('dfg452ws'));
$aes = new Crypt_AES($aesKey, $aesIV, true);
$content = $_POST['content'] ?? '';
$sign = $_POST['sign'] ?? '';
// 1. 驗證簽名(防竄改、防重放)
if (strcasecmp(md5($content . $md5Key), $sign) !== 0) {
exit(json_encode(['code' => 401, 'msg' => '簽名驗證失敗']));
}
// 2. 解密
$b64 = urldecode($content);
$encrypted = base64_decode($b64);
$decrypted = $aes->decrypt($encrypted);
if ($decrypted === false) {
exit(json_encode(['code' => 500, 'msg' => '解密失敗']));
}
$data = json_decode($decrypted, true);
// 業務邏輯...
$result = ['status' => 'ok', 'received' => $data];
// 3. 加密回應(同流程)
$respJson = json_encode($result, JSON_UNESCAPED_UNICODE);
$respEnc = $aes->encrypt($respJson);
$respB64 = base64_encode($respEnc);
$respContent = urlencode($respB64);
$respSign = md5($respContent . $md5Key);
echo json_encode([
'version' => '1.0',
'sign' => $respSign,
'content' => $respContent
]);
?>
timestamp + nonce,Server 驗證時間窗(±5min)+ Redis 去重 nonce。AES-256-GCM(認證加密,無需額外簽名),PHP 用 openssl_encrypt(..., 'aes-256-gcm', ..., $tag)。code/msg,避免暴露堆疊、金鑰長度等資訊。<?php
function aesGcmEncrypt(string $data, string $key): string {
$iv = random_bytes(12);
$tag = '';
$ct = openssl_encrypt($data, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag, '');
return base64_encode($iv . $tag . $ct);
}
function aesGcmDecrypt(string $payload, string $key): ?string {
$raw = base64_decode($payload);
$iv = substr($raw, 0, 12);
$tag = substr($raw, 12, 16);
$ct = substr($raw, 28);
return openssl_decrypt($ct, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag, '');
}
?>
AES-CBC + PKCS7 + Base64 + MD5 簽名是舊版開放平台常見協議。理解其流程有助於維護舊系統,但新專案請直接用 AES-GCM 或 libsodium,減少自行實現的風險。