RSA 非對稱加密是資料傳輸安全、數位簽名、API 鑑權的基石。PHP 透過 OpenSSL 擴展提供完整 RSA 功能。本文涵蓋:金鑰對生成、PKCS#1/PKCS#8 格式、公鑰加密/私鑰解密、私鑰簽名/公鑰驗證、Base64 編碼安全傳輸、常見坑與最佳實踐。
2048, // 建議 2048+ 位,1024 已不安全
'private_key_type' => OPENSSL_KEYTYPE_RSA,
];
// 生成私鑰資源
$res = openssl_pkey_new($config);
// 導出私鑰 (PKCS#1 格式,傳統 PEM)
openssl_pkey_export($res, $private_key_pem);
// 可選加密: openssl_pkey_export($res, $private_key_pem, 'passphrase');
// 導出公鑰
$pubkey_detail = openssl_pkey_get_details($res);
$public_key_pem = $pubkey_detail['key'];
echo "=== 私鑰 ===\n$private_key_pem\n";
echo "=== 公鑰 ===\n$public_key_pem\n";
// 儲存到檔案 (生產環境請設定嚴格權限 600)
file_put_contents('private_key.pem', $private_key_pem);
file_put_contents('public_key.pem', $public_key_pem);
# 生成 2048 位私鑰 (加密) openssl genrsa -aes256 -out private_key.pem 2048 # 導出公鑰 openssl rsa -in private_key.pem -pubout -out public_key.pem # 轉換為 PKCS#8 格式 (Java/.NET 相容) openssl pkcs8 -topk8 -inform PEM -in private_key.pem -out private_key_pkcs8.pem -nocrypt
3. 加密與解密
關鍵限制:資料長度
| 金鑰長度 | 最大加密資料 (PKCS#1 v1.5 padding) | 最大加密資料 (OAEP padding) |
|---|---|---|
| 1024 bits | 117 bytes | 86 bytes |
| 2048 bits | 245 bytes | 214 bytes |
| 4096 bits | 501 bytes | 470 bytes |
比對 PKCS#1 v1.5 結構中的 hash
2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA];
$res = openssl_pkey_new($config);
openssl_pkey_export($res, $private_key_pem);
$public_key_pem = openssl_pkey_get_details($res)['key'];
file_put_contents('private_key.pem', $private_key_pem);
file_put_contents('public_key.pem', $public_key_pem);
} else {
$private_key_pem = file_get_contents('private_key.pem');
$public_key_pem = file_get_contents('public_key.pem');
}
$priv = openssl_pkey_get_private($private_key_pem);
$pub = openssl_pkey_get_public($public_key_pem);
// 測試資料
$tests = [
"短字串",
"中等長度字串: 這是一段測試資料,包含中文、English、數字 123、符號 !@#。",
str_repeat("長字串測試 ", 50), // 超過 RSA 直接加密限制
];
foreach ($tests as $i => $data) {
echo "=== 測試 $i: 長度 " . strlen($data) " ===\n";
// 混合加密演示 (簡化版)
if (strlen($data) > 200) {
echo "資料過長,實際應用請用混合加密 (AES + RSA)\n";
continue;
}
// 公鑰加密
$enc = '';
openssl_public_encrypt($data, $enc, $pub);
$enc_b64 = base64_encode($enc);
// 私鑰解密
$dec = '';
openssl_private_decrypt(base64_decode($enc_b64), $dec, $priv);
echo "原文: " . mb_substr($data, 0, 50) . "...\n";
echo "解密: " . mb_substr($dec, 0, 50) . "...\n";
echo "匹配: " . ($data === $dec ? "✓" : "✗") . "\n\n";
}
// 釋放資源
openssl_free_key($priv);
openssl_free_key($pub);
| 問題 | 原因 | 解決 |
|---|---|---|
| openssl_pkey_get_private 失敗 | 私鑰有密語、格式非 PKCS#1、檔案權限、換行符 | 傳入密語陣列 `[$pem, 'pass']`、轉 PKCS#8、chmod 600、確保 PEM 格式正確 |
| 加密資料過長錯誤 | 超過金鑰長度限制 | 使用混合加密 (AES + RSA) |
| Base64 編碼 URL 不安全 | 標準 Base64 含 + / = |
用 strtr($b64, '+/=', '-_,') 或 base64url_encode |
| 跨語言互通失敗 | Padding 模式不同 (PKCS#1 v1.5 vs OAEP)、金鑰格式 (PKCS#1 vs PKCS#8) | 統一 OAEP + PKCS#8,或雙方協商參數 |
| 效能差 | RSA 運算慢,頻繁生成金鑰 | 金鑰重用、混合加密、考慮 ECC (openssl_pkey_new(['curve_name'=>'secp256r1'])) |
更多 PHP 相關內容可參考本站專題:《PHP 加密方法總結》、《PHP 運算與運算符用法總結》、《PHP 網絡編程技巧總結》、《PHP 基本語法入門教程》、《PHP 操作 Office 文檔技巧總結》、《PHP 日期與時間用法總結》、《PHP 面向對象程序設計入門教程》、《PHP 字符串用法總結》、《PHP+MySQL 數據庫操作入門教程》及《PHP 常見數據庫操作技巧匯總》。