核心概念:非對稱加密雙模式
| 模式 | 加密端 | 解密端 | 典型場景 |
| 公鑰加密 / 私鑰解密 | 公開金鑰 | 私鑰 | 機密資料傳輸(僅持有私鑰者可讀) |
| 私鑰加密 / 公鑰解密 | 私鑰 | 公開金鑰 | 數位簽章/身份驗證(人人可驗證來源) |
PHP OpenSSL 關鍵函數對照
| 函數 | 用途 | 參數順序 |
openssl_get_privatekey() | 從 PEM 字串/檔案載入私鑰資源 | ($pem, $passphrase?) |
openssl_get_publickey() | 載入公鑰資源 | ($pem) |
openssl_public_encrypt() | 公鑰加密 | ($data, &$crypted, $pubKey) |
openssl_private_decrypt() | 私鑰解密 | ($crypted, &$decrypted, $privKey) |
openssl_private_encrypt() | 私鑰加密(簽名用) | ($data, &$crypted, $privKey) |
openssl_public_decrypt() | 公鑰解密(驗證用) | ($crypted, &$decrypted, $pubKey) |
openssl_sign() | 私鑰簽名(含雜湊) | ($data, &$sig, $privKey, $algo) |
openssl_verify() | 公鑰驗證簽名 | ($data, $sig, $pubKey, $algo) |
openssl_free_key() | 釋放金鑰資源 | ($keyResource) |
完整可執行範例
<?php
// 假設已有金鑰檔:private.pem (PKCS#1), public.pem
$data = '敏感業務資料: 訂單號 20231001001, 金額 999.00';
// === 1. 載入金鑰 ===
$privKey = openssl_get_privatekey(file_get_contents('private.pem'));
$pubKey = openssl_get_publickey(file_get_contents('public.pem'));
if (!$privKey || !$pubKey) {
die('金鑰載入失敗: ' . openssl_error_string());
}
// === 2. 模式 A:公鑰加密 -> 私鑰解密(機密傳輸) ===
openssl_public_encrypt($data, $encrypted, $pubKey);
echo "Encrypted (Base64): " . base64_encode($encrypted) . "\n";
openssl_private_decrypt($encrypted, $decrypted, $privKey);
echo "Decrypted: $decrypted\n";
assert($decrypted === $data);
// === 3. 模式 B:私鑰加密 -> 公鑰解密(簽名驗證等價) ===
openssl_private_encrypt($data, $signed, $privKey);
openssl_public_decrypt($signed, $verified, $pubKey);
assert($verified === $data);
// === 4. 標準簽名/驗證(推薦:含 SHA-256 雜湊) ===
openssl_sign($data, $signature, $privKey, OPENSSL_ALGO_SHA256);
$sigB64 = base64_encode($signature);
echo "Signature: $sigB64\n";
$verify = openssl_verify($data, $signature, $pubKey, OPENSSL_ALGO_SHA256);
echo "Verify result: $verify (1=OK, 0=Fail, -1=Error)\n";
// === 5. 釋放資源 ===
openssl_free_key($privKey);
openssl_free_key($pubKey);
?>
金鑰檔案準備(對應快錢/支付寶等支付閘道)
- 支付方提供:**商戶私鑰**(
merchant_private.pem)、**支付平台公鑰**(platform_public.cer)。
- 商戶自行生成:**商戶公鑰**(對應私鑰導出)提交給支付平台。
- 注意: 範例中
99bill-rsa.pem 和 99bill-rsa.cer **不構成一對**;實際部署時務必使用匹配的金鑰對。
常見坑點
- 資料長度限制: RSA-2048 直接加密最多 245 Bytes。超長資料請採用 混合加密:AES-256-GCM 加密資料 + RSA 加密 AES Key。
- 填充模式: PHP 預設
OPENSSL_PKCS1_PADDING。若對接 Java (PKCS1Padding) / .NET (OAEP) 需確認一致,或用 openssl_encrypt 手動控制。
- 編碼傳輸: 加密結果為二進位,**必須 Base64 編碼** 後才能放入 JSON/URL/表單傳輸。
- 錯誤處理: 所有函數失敗回傳
false,務必檢查並記錄 openssl_error_string()。
混合加密範例(大資料)
<?php
function hybridEncrypt(string $data, $pubKey): array {
$aesKey = random_bytes(32); // 256-bit
$iv = random_bytes(16); // 128-bit
$tag = '';
$ct = openssl_encrypt($data, 'aes-256-gcm', $aesKey, OPENSSL_RAW_DATA, $iv, $tag);
openssl_public_encrypt($aesKey, $encKey, $pubKey);
return [
'key' => base64_encode($encKey),
'iv' => base64_encode($iv),
'tag' => base64_encode($tag),
'data' => base64_encode($ct),
];
}
function hybridDecrypt(array $pkg, $privKey): string {
$aesKey = base64_decode($pkg['key']);
openssl_private_decrypt($aesKey, $aesKeyRaw, $privKey);
return openssl_decrypt(
base64_decode($pkg['data']),
'aes-256-gcm',
$aesKeyRaw,
OPENSSL_RAW_DATA,
base64_decode($pkg['iv']),
base64_decode($pkg['tag'])
);
}
?>