php 实现【AES + RSA】安全通信全流程
在数据安全领域,单一加密算法已无法兼顾「效率」与「安全」——AES 加密快但密钥传输风险高,RSA 安全但加密大数据效率低。AES+RSA 混合加密方案恰好互补二者短板,成为 HTTPS、API 通信、敏感数据传输的工业级标准。本文将基于 PHP 语言,从原理拆解到代码实现,完整讲解混合加密的落地过程,适合开发者直接复刻到项目中。
一、混合加密核心逻辑(先懂原理,再写代码)
1.1 为什么选择 AES+RSA?
| 加密算法 | 核心特点 | 适用场景 | 核心痛点 |
|---|---|---|---|
| AES(对称加密) | 加密 / 解密用同一密钥,运算速度极快 | 加密海量业务数据(如 JSON、文件) | 密钥传输易被截获,重复使用风险高 |
| RSA(非对称加密) | 公钥加密、私钥解密,安全性极高 | 加密短小的密钥 / 签名 | 运算速度慢,不适合加密大数据 |
混合加密的核心思路:
-
用AES加密「业务数据」(保证效率);
-
用RSA 公钥加密「AES 密钥」(保证密钥安全);
-
传输「AES 加密后的业务密文」+「RSA 加密后的 AES 密钥密文」;
-
接收方用RSA 私钥解密出 AES 密钥,再用 AES 密钥解密业务数据。
1.2 完整流程示意图

发送方
生成一次性AES密钥+IV
AES加密业务数据 → 密文A
RSA公钥加密AES密钥 → 密文B
传输密文A + 密文B
接收方
RSA私钥解密密文B → 还原AES密钥
AES密钥解密密文A → 还原业务数据
A[发送方] --> A1[生成一次性AES密钥+IV]
A1 --> A2[AES加密业务数据 → 密文A]
A1 --> A3[RSA公钥加密AES密钥 → 密文B]
A2 & A3 --> A4[传输密文A+密文B]
A4 --> B[接收方]
B --> B1[RSA私钥解密密文B → 还原AES密钥]
B1 --> B2[AES密钥解密密文A → 还原业务数据]
二、PHP 实现混合加密完整代码
2.1 前置准备
- 生成 RSA 密钥对(公钥给发送方,私钥由接收方保管):
- PHP 环境需开启
openssl扩展(默认已开启,可通过phpinfo()验证)。
2.2 发送方:加密流程实现
发送方核心任务:生成 AES 密钥→加密业务数据→加密 AES 密钥→组装传输数据。
<?php
/**
* 发送方 - 混合加密实现
*/
class EncryptSender
{
// RSA公钥路径(接收方提供)
private $rsaPublicKeyPath = 'public_key.pem';
/**
* 生成一次性AES密钥和初始向量(IV)
* AES-128-CBC模式:密钥16字节,IV 16字节
* @return array [aes_key, aes_iv]
*/
public function generateAesKey(): array
{
// 生成随机AES密钥(16字节=128位)
$aesKey = openssl_random_pseudo_bytes(16);
// 生成AES初始向量(CBC模式必需,16字节)
$aesIv = openssl_random_pseudo_bytes(16);
return [$aesKey, $aesIv];
}
/**
* AES-CBC加密业务数据
* @param string $data 原始业务数据(字符串)
* @param string $aesKey AES密钥
* @param string $aesIv 初始向量
* @return string 加密后的密文(base64编码,方便传输)
*/
public function aesEncrypt(string $data, string $aesKey, string $aesIv): string
{
// PKCS7补码(PHP默认用PKCS5,与PKCS7兼容)
$blockSize = openssl_cipher_iv_length('aes-128-cbc');
$padding = $blockSize - (strlen($data) % $blockSize);
$data .= str_repeat(chr($padding), $padding);
// AES加密
$cipherText = openssl_encrypt(
$data,
'aes-128-cbc',
$aesKey,
OPENSSL_RAW_DATA, // 输出原始二进制数据
$aesIv
);
// 转base64方便网络传输
return base64_encode($cipherText);
}
/**
* RSA公钥加密AES密钥
* @param string $aesKey AES密钥(二进制)
* @return string 加密后的密文(base64编码)
*/
public function rsaEncryptAesKey(string $aesKey): string
{
// 加载RSA公钥
$publicKey = openssl_pkey_get_public(file_get_contents($this->rsaPublicKeyPath));
if (!$publicKey) {
throw new Exception('RSA公钥加载失败:' . openssl_error_string());
}
// RSA加密(OAEP填充,更安全,替代PKCS1)
$encrypted = '';
$success = openssl_public_encrypt(
$aesKey,
$encrypted,
$publicKey,
OPENSSL_PKCS1_OAEP_PADDING // 推荐生产环境使用
);
if (!$success) {
throw new Exception('RSA加密失败:' . openssl_error_string());
}
// 释放公钥资源
openssl_pkey_free($publicKey);
// 转base64传输
return base64_encode($encrypted);
}
/**
* 完整加密流程:生成密钥→加密数据→加密密钥→返回传输数据
* @param string $businessData 原始业务数据
* @return array [cipher_a: AES加密后的数据, cipher_b: RSA加密后的AES密钥, aes_iv: AES初始向量]
*/
public function encrypt(string $businessData): array
{
// 1. 生成一次性AES密钥和IV
[$aesKey, $aesIv] = $this->generateAesKey();
// 2. AES加密业务数据(密文A)
$cipherA = $this->aesEncrypt($businessData, $aesKey, $aesIv);
// 3. RSA加密AES密钥(密文B)
$cipherB = $this->rsaEncryptAesKey($aesKey);
// 返回传输数据(IV需一起传输,接收方解密AES需要)
return [
'cipher_a' => $cipherA,
'cipher_b' => $cipherB,
'aes_iv' => base64_encode($aesIv) // IV转base64传输
];
}
}
// -------------------------- 发送方调用示例 --------------------------
try {
// 原始业务数据(示例:用户敏感信息)
$businessData = json_encode([
'user_id' => 12345,
'username' => '张三',
'phone' => '13800138000',
'order_amount' => 999.00
], JSON_UNESCAPED_UNICODE);
$sender = new EncryptSender();
$transmitData = $sender->encrypt($businessData);
echo "发送方传输数据:\n";
echo "密文A(AES加密业务数据):{$transmitData['cipher_a']}\n";
echo "密文B(RSA加密AES密钥):{$transmitData['cipher_b']}\n";
echo "AES初始向量IV:{$transmitData['aes_iv']}\n";
// 模拟网络传输:将transmitData传给接收方
} catch (Exception $e) {
echo "加密失败:{$e->getMessage()}\n";
}
?>
2.3 接收方:解密流程实现
接收方核心任务:接收密文 A / 密文 B/IV→RSA 解密 AES 密钥→AES 解密业务数据。
<?php
/**
* 接收方 - 混合解密实现
*/
class DecryptReceiver
{
// RSA私钥路径(仅接收方持有,需妥善保管)
private $rsaPrivateKeyPath = 'private_key.pem';
/**
* RSA私钥解密AES密钥
* @param string $cipherB RSA加密后的AES密钥(base64编码)
* @return string 原始AES密钥(二进制)
*/
public function rsaDecryptAesKey(string $cipherB): string
{
// 解码base64
$cipherB = base64_decode($cipherB);
// 加载RSA私钥
$privateKey = openssl_pkey_get_private(file_get_contents($this->rsaPrivateKeyPath));
if (!$privateKey) {
throw new Exception('RSA私钥加载失败:' . openssl_error_string());
}
// RSA解密(与加密的OAEP填充对应)
$decrypted = '';
$success = openssl_private_decrypt(
$cipherB,
$decrypted,
$privateKey,
OPENSSL_PKCS1_OAEP_PADDING
);
if (!$success) {
throw new Exception('RSA解密失败:' . openssl_error_string());
}
// 释放私钥资源
openssl_pkey_free($privateKey);
return $decrypted;
}
/**
* AES-CBC解密业务数据
* @param string $cipherA AES加密后的业务数据(base64编码)
* @param string $aesKey 原始AES密钥
* @param string $aesIv 初始向量(base64编码)
* @return string 原始业务数据
*/
public function aesDecrypt(string $cipherA, string $aesKey, string $aesIv): string
{
// 解码base64
$cipherA = base64_decode($cipherA);
$aesIv = base64_decode($aesIv);
// AES解密
$decrypted = openssl_decrypt(
$cipherA,
'aes-128-cbc',
$aesKey,
OPENSSL_RAW_DATA,
$aesIv
);
// 去除PKCS7补码
$padding = ord($decrypted[strlen($decrypted) - 1]);
$decrypted = substr($decrypted, 0, -$padding);
return $decrypted;
}
/**
* 完整解密流程:解密AES密钥→解密业务数据
* @param array $transmitData 接收的传输数据 [cipher_a, cipher_b, aes_iv]
* @return string 原始业务数据
*/
public function decrypt(array $transmitData): string
{
// 1. RSA解密AES密钥
$aesKey = $this->rsaDecryptAesKey($transmitData['cipher_b']);
// 2. AES解密业务数据
$businessData = $this->aesDecrypt(
$transmitData['cipher_a'],
$aesKey,
$transmitData['aes_iv']
);
return $businessData;
}
}
// -------------------------- 接收方调用示例 --------------------------
try {
// 模拟接收发送方的传输数据(实际项目中从请求参数/网络获取)
$transmitData = [
'cipher_a' => 'xxx', // 替换为发送方输出的cipher_a
'cipher_b' => 'xxx', // 替换为发送方输出的cipher_b
'aes_iv' => 'xxx' // 替换为发送方输出的aes_iv
];
$receiver = new DecryptReceiver();
$originalData = $receiver->decrypt($transmitData);
echo "接收方解密结果:\n";
echo "原始业务数据:{$originalData}\n";
// 解析JSON(可选)
$data = json_decode($originalData, true);
echo "解析后的数据:\n";
print_r($data);
} catch (Exception $e) {
echo "解密失败:{$e->getMessage()}\n";
}
?>
三、工业级落地避坑指南
3.1 密钥管理(核心安全点)
- RSA 私钥保护:
-
禁止明文存储在代码 / 配置文件中,推荐加密存储(如服务器加密磁盘、云密钥管理服务 KMS);
-
私钥文件权限设置为
600(仅属主可读),防止被其他用户读取。
-
-
AES 密钥一次性:每次通信生成新的 AES 密钥,禁止重复使用(避免密钥泄露后批量解密数据)。
- 公钥防篡改:公钥可公开,但需通过 CA 证书签名(如 HTTPS),防止中间人替换公钥。
3.2 加密参数选择
-
AES 模式:优先选择
aes-128-cbc或aes-256-gcm(GCM 模式自带完整性校验,更安全),禁止使用ECB模式(无 IV,相同明文加密后密文相同,易被破解)。 -
RSA 填充方式:推荐
OPENSSL_PKCS1_OAEP_PADDING(抗攻击能力强),避免OPENSSL_PKCS1_PADDING(存在漏洞风险)。 -
编码方式:加密后的二进制密文需转
base64或hex编码,避免传输过程中二进制数据丢失。
3.3 性能优化
-
仅用 RSA 加密 AES 密钥(16 字节),避免用 RSA 加密大数据(RSA 加密 1KB 数据耗时约 10ms,AES 仅需 0.1ms);
-
大文件传输:分块加密(每块生成独立 AES 密钥,用 RSA 加密密钥列表);
-
复用 openssl 资源:频繁加密 / 解密时,避免重复加载密钥(可封装为单例类)。
四、实际应用场景(丰富博客内容)
-
前后端 API 通信:前端用 JS 实现 AES 加密请求参数,RSA 加密 AES 密钥;后端用 PHP 解密,返回数据同理加密。
-
敏感数据存储:用户手机号、身份证等数据,用 AES 加密存储,AES 密钥用 RSA 加密后存于另一位置。
-
跨系统数据传输:企业间对接时,通过混合加密保证数据在公网传输的安全性。
五、总结
-
混合加密的核心是「AES 加密数据,RSA 加密 AES 密钥」,兼顾效率与安全;
-
PHP 实现的关键是正确使用
openssl扩展,注意填充方式、IV 生成、编码转换等细节; -
工业级落地的核心是密钥管理,私钥保护和 AES 密钥一次性是安全的重中之重。
本文来自博客园,作者:Carvers,转载请注明原文链接:https://www.cnblogs.com/carver/articles/19684013

浙公网安备 33010602011771号