Loading

php 实现【AES + RSA】安全通信全流程

在数据安全领域,单一加密算法已无法兼顾「效率」与「安全」——AES 加密快密钥传输风险高RSA 安全但加密大数据效率低。AES+RSA 混合加密方案恰好互补二者短板,成为 HTTPS、API 通信、敏感数据传输的工业级标准。本文将基于 PHP 语言,从原理拆解到代码实现,完整讲解混合加密的落地过程,适合开发者直接复刻到项目中。

一、混合加密核心逻辑(先懂原理,再写代码)

1.1 为什么选择 AES+RSA?

加密算法 核心特点 适用场景 核心痛点
AES(对称加密) 加密 / 解密用同一密钥,运算速度极快 加密海量业务数据(如 JSON、文件) 密钥传输易被截获,重复使用风险高
RSA(非对称加密) 公钥加密、私钥解密,安全性极高 加密短小的密钥 / 签名 运算速度慢,不适合加密大数据

混合加密的核心思路:

  1. AES加密「业务数据」(保证效率);

  2. RSA 公钥加密「AES 密钥」(保证密钥安全);

  3. 传输「AES 加密后的业务密文」+「RSA 加密后的 AES 密钥密文」;

  4. 接收方用RSA 私钥解密出 AES 密钥,再用 AES 密钥解密业务数据。

1.2 完整流程示意图

image

发送方

生成一次性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 前置准备

  1. 生成 RSA 密钥对(公钥给发送方,私钥由接收方保管):
     
    # 生成RSA私钥(2048位,推荐生产环境使用,1024位已不安全)
    openssl genrsa -out private_key.pem 2048
    # 从私钥提取公钥
    openssl rsa -in private_key.pem -pubout -out public_key.pem
  2. 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 密钥管理(核心安全点)

  1. RSA 私钥保护
    • 禁止明文存储在代码 / 配置文件中,推荐加密存储(如服务器加密磁盘、云密钥管理服务 KMS);

    • 私钥文件权限设置为600(仅属主可读),防止被其他用户读取。

  2. AES 密钥一次性:每次通信生成新的 AES 密钥,禁止重复使用(避免密钥泄露后批量解密数据)。

  3. 公钥防篡改:公钥可公开,但需通过 CA 证书签名(如 HTTPS),防止中间人替换公钥。

3.2 加密参数选择

  1. AES 模式:优先选择aes-128-cbcaes-256-gcm(GCM 模式自带完整性校验,更安全),禁止使用ECB模式(无 IV,相同明文加密后密文相同,易被破解)。

  2. RSA 填充方式:推荐OPENSSL_PKCS1_OAEP_PADDING(抗攻击能力强),避免OPENSSL_PKCS1_PADDING(存在漏洞风险)。

  3. 编码方式:加密后的二进制密文需转base64hex编码,避免传输过程中二进制数据丢失。

3.3 性能优化

  1. 仅用 RSA 加密 AES 密钥(16 字节),避免用 RSA 加密大数据(RSA 加密 1KB 数据耗时约 10ms,AES 仅需 0.1ms);

  2. 大文件传输分块加密(每块生成独立 AES 密钥,用 RSA 加密密钥列表);

  3. 复用 openssl 资源:频繁加密 / 解密时,避免重复加载密钥(可封装为单例类)。

四、实际应用场景(丰富博客内容)

  1. 前后端 API 通信:前端用 JS 实现 AES 加密请求参数,RSA 加密 AES 密钥;后端用 PHP 解密,返回数据同理加密。

  2. 敏感数据存储:用户手机号、身份证等数据,用 AES 加密存储,AES 密钥用 RSA 加密后存于另一位置。

  3. 跨系统数据传输:企业间对接时,通过混合加密保证数据在公网传输的安全性。

五、总结

  1. 混合加密的核心是「AES 加密数据RSA 加密 AES 密钥」,兼顾效率安全

  2. PHP 实现的关键是正确使用openssl扩展,注意填充方式IV 生成编码转换等细节;

  3. 工业级落地的核心是密钥管理,私钥保护和 AES 密钥一次性是安全的重中之重。

posted @ 2026-03-07 20:07  Carvers  阅读(1)  评论(0)    收藏  举报