OpenSSL 擴展簡介
php_openssl.dll (Windows) / openssl.so (Linux) 是 PHP 內建的 OpenSSL 封裝擴展,提供 加密、解密、簽章、驗證、憑證管理、SSL/TLS 通訊 等核心密碼學功能。現代 PHP 應用 (HTTPS 客戶端、API 簽章、支付網關、JWT、加密儲存) 均高度依賴此擴展。
核心功能分類
| 功能領域 | 代表函數/用途 |
| 非對稱加密 (RSA) | openssl_public_encrypt, openssl_private_decrypt, openssl_private_encrypt, openssl_public_decrypt |
| 對稱加密 (AES 等) | openssl_encrypt, openssl_decrypt, openssl_get_cipher_methods |
| 數位簽章/驗證 | openssl_sign, openssl_verify, OPENSSL_ALGO_SHA256 |
| 金鑰/憑證管理 | openssl_pkey_new, openssl_pkey_export, openssl_x509_read, openssl_csr_new |
| SSL/TLS 通訊 | stream_socket_client (ssl://), stream_context_set_option (SSL 選項) |
| 雜湊/摘要 | openssl_digest, hash() (Hash 擴展) |
| 隨機數生成 | openssl_random_pseudo_bytes |
啟用與驗證
; php.ini (Windows)
extension=php_openssl.dll
; php.ini (Linux)
extension=openssl.so
; 驗證
php -m | grep openssl
php -r "var_dump(extension_loaded('openssl'));"
php -r "print_r(openssl_get_cipher_methods());"
實戰應用場景
1. HTTPS API 客戶端 (cURL + SSL Context)
<?php
$url = 'https://api.example.com/data';
$ctx = stream_context_create([
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'cafile' => '/etc/ssl/certs/ca-certificates.crt', // CA 憑證束
'ciphers' => 'HIGH:!aNULL:!MD5:!3DES',
],
'http' => [
'timeout' => 30,
'header' => "Authorization: Bearer $token\r\nAccept: application/json\r\n",
],
]);
$response = file_get_contents($url, false, $ctx);
// 或搭配 cURL CURLOPT_SSL_VERIFYPEER => true
2. AES-256-CBC 對稱加密儲存敏感資料
<?php
function encryptData(string $data, string $key, string $iv): string {
// $key: 32 bytes (256-bit), $iv: 16 bytes (128-bit)
$encrypted = openssl_encrypt($data, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
return base64_encode($iv . $encrypted); // IV 前綴儲存
}
function decryptData(string $encoded, string $key): string {
$raw = base64_decode($encoded);
$iv = substr($raw, 0, 16);
$encrypted = substr($raw, 16);
return openssl_decrypt($encrypted, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
}
// 使用
$key = hash('sha256', 'master-secret-key', true); // 從主密鑰衍生
$iv = random_bytes(16);
$encrypted = encryptData('身分證字號: A123456789', $key, $iv);
$decrypted = decryptData($encrypted, $key);
3. RSA 非對稱加密/簽章 (支付、API 簽名)
<?php
// 載入金鑰
$privKey = openssl_pkey_get_private(file_get_contents('private.pem'));
$pubKey = openssl_pkey_get_public(file_get_contents('public.pem'));
// 公鑰加密 (傳輸機密資料)
openssl_public_encrypt($data, $encrypted, $pubKey, OPENSSL_PKCS1_OAEP_PADDING);
// 私鑰解密
openssl_private_decrypt($encrypted, $decrypted, $privKey, OPENSSL_PKCS1_OAEP_PADDING);
// 私鑰簽名 (防篡改、身份驗證)
openssl_sign($data, $signature, $privKey, OPENSSL_ALGO_SHA256);
// 公鑰驗證
$valid = openssl_verify($data, $signature, $pubKey, OPENSSL_ALGO_SHA256); // 1=通過
4. 產生自簽憑證 (開發/測試環境 HTTPS)
<?php
$dn = [
'countryName' => 'TW',
'stateOrProvinceName' => 'Taipei',
'localityName' => 'Taipei',
'organizationName' => 'MyDev',
'commonName' => 'localhost',
'emailAddress' => 'admin@localhost',
];
$privKey = openssl_pkey_new(['private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA]);
$csr = openssl_csr_new($dn, $privKey);
$cert = openssl_csr_sign($csr, null, $privKey, 365, ['digest_alg' => 'sha256']);
openssl_x509_export($cert, $certPem);
openssl_pkey_export($privKey, $keyPem);
file_put_contents('localhost.crt', $certPem);
file_put_contents('localhost.key', $keyPem);
// Nginx/Apache 設定 ssl_certificate, ssl_certificate_key
5. JWT Token 簽發與驗證 (RS256)
<?php
function jwtEncode(array $payload, string $privateKeyPem): string {
$header = ['alg' => 'RS256', 'typ' => 'JWT'];
$segments = [
base64UrlEncode(json_encode($header)),
base64UrlEncode(json_encode($payload)),
];
$signingInput = implode('.', $segments);
$privKey = openssl_pkey_get_private($privateKeyPem);
openssl_sign($signingInput, $signature, $privKey, OPENSSL_ALGO_SHA256);
openssl_free_key($privKey);
return $signingInput . '.' . base64UrlEncode($signature);
}
function jwtDecode(string $jwt, string $publicKeyPem): ?array {
[$headerB64, $payloadB64, $sigB64] = explode('.', $jwt);
$signingInput = "$headerB64.$payloadB64";
$pubKey = openssl_pkey_get_public($publicKeyPem);
$valid = openssl_verify($signingInput, base64UrlDecode($sigB64), $pubKey, OPENSSL_ALGO_SHA256);
openssl_free_key($pubKey);
if ($valid !== 1) return null;
return json_decode(base64UrlDecode($payloadB64), true);
}
function base64UrlEncode($data) { return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); }
function base64UrlDecode($data) { return base64_decode(strtr($data, '-_', '+/')); }
常見加密演算法對照
| 類別 | 演算法 | PHP 常數/字串 | 備註 |
| 對稱 | AES-256-CBC | 'aes-256-cbc' | 推薦標準,需 32B 金鑰 + 16B IV |
| AES-256-GCM | 'aes-256-gcm' | AEAD 認證加密,PHP 7.1+,含標籤 |
| ChaCha20-Poly1305 | 'chacha20-poly1305' | 高效能、抗時序攻擊,PHP 7.2+ |
| 非對稱 | RSA-2048 | OPENSSL_KEYTYPE_RSA, 2048 bits | 通用標準 |
| RSA-4096 | 4096 bits | 高安全性,效能較慢 |
| ECDSA P-256 | OPENSSL_KEYTYPE_EC, 'prime256v1' | 金鑰較小、速度快,現代標準 |
| 雜湊 | SHA-256 | OPENSSL_ALGO_SHA256 | 簽章標準 |
| SHA-384 | OPENSSL_ALGO_SHA384 | 更強 |
| SHA-512 | OPENSSL_ALGO_SHA512 | 最強 |
錯誤排查與最佳實踐
- ❌
openssl_pkey_get_private() 回傳 false → PEM 格式錯誤、密碼錯誤、檔案權限 (0600)
- ❌
openssl_encrypt() 回傳 false → 金鑰/IV 長度不符、演算法不支援
- ✅ 生產環境嚴禁硬編碼金鑰:使用環境變數、Vault、KMS、HSM
- ✅ IV 每次加密必須隨機產生 (CBC 模式),不可重複使用
- ✅ 金鑰輪換機制:定期更換加密金鑰,舊資料重新加密
- ✅ 使用認證加密 (GCM/CCM/ChaCha20-Poly1305) 同時保證機密性與完整性
- ✅ 檢查 openssl_error_string() 取得詳細錯誤訊息