深入理解敏感信息加密:AES+RSA混合使用前后端对接

在前后端交互中,敏感信息(手机号、身份证号、支付密码等)的传输安全是重中之重。单纯用AES或RSA加密,要么存在密钥泄露风险,要么加密效率低下,而"AES+RSA"混合加密方案,恰好兼顾了安全性与高效性,成为企业级项目的首选。

一、为什么是AES+RSA?

先明确一个核心问题:为什么不只用一种加密方式?

1. 两种算法的核心差异

AES(对称加密):加密和解密用同一个密钥,就像用同一把钥匙锁门和开门。优点是 加密速度极快,适合大批量数据(比如整个请求体)加密;缺点是密钥一旦泄露,所有加密信息都会被破解,且密钥传输过程中容易被拦截。

RSA(非对称加密):有一对密钥------公钥(公开给前端)和私钥(后端保密),用公钥加密的内容,只有对应的私钥能解密;用私钥加密的内容,只有对应的公钥能解密。优点是 安全性极高,密钥无需直接传输;缺点是加密速度慢,不适合大批量数据加密。

2. 混合加密的核心逻辑

AES+RSA的核心思路的是"取长补短":用AES加密敏感数据(高效),用RSA加密AES的密钥(安全),既保证了加密效率,又避免了密钥泄露风险。简单流程拆解:

  1. 前端生成一个随机的AES密钥(临时密钥,每次请求可不同);

  2. 前端用AES密钥加密敏感信息(如用户手机号、密码);

  3. 前端用后端提供的RSA公钥,加密生成的AES密钥;

  4. 前端将"加密后的敏感信息 + 加密后的AES密钥"一起发送给后端;

  5. 后端用自己的RSA私钥,解密出AES密钥;

  6. 后端用解密出的AES密钥,解密出原始的敏感信息。

重点:AES密钥是临时的、随机生成的,且通过RSA加密传输,即使被拦截,没有后端私钥也无法破解,从根本上保证了安全。

二、前后端代码示例

先提前准备:后端需要生成RSA公钥和私钥,公钥提供给前端,私钥自己保存(严禁泄露)。

第一步:后端准备(SpringBoot)

1. 生成RSA公钥/私钥(工具类)

先写一个RSA工具类,用于生成密钥对、加密和解密,核心代码如下:

java 复制代码
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * RSA工具类(生成密钥对、加密、解密)
 */
public class RSAUtil {
    // 密钥长度(推荐2048位,安全性足够,兼容性好)
    private static final int KEY_SIZE = 2048;
    // 算法名称
    private static final String ALGORITHM = "RSA";

    // 生成RSA密钥对(公钥+私钥)
    public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
        keyPairGenerator.initialize(KEY_SIZE);
        return keyPairGenerator.generateKeyPair();
    }

    // 公钥加密(前端用公钥加密AES密钥)
    public static String encryptByPublicKey(String data, String publicKeyStr) throws Exception {
        // 解码公钥
        byte[] publicKeyBytes = Base64.decodeBase64(publicKeyStr);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PublicKey publicKey = keyFactory.generatePublic(keySpec);

        // 加密
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedBytes = cipher.doFinal(data.getBytes("UTF-8"));
        return Base64.encodeBase64String(encryptedBytes);
    }

    // 私钥解密(后端用私钥解密AES密钥)
    public static String decryptByPrivateKey(String encryptedData, String privateKeyStr) throws Exception {
        // 解码私钥
        byte[] privateKeyBytes = Base64.decodeBase64(privateKeyStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

        // 解密
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedBytes = cipher.doFinal(Base64.decodeBase64(encryptedData));
        return new String(decryptedBytes, "UTF-8");
    }

    // 测试生成密钥对(运行后复制公钥给前端)
    public static void main(String[] args) throws NoSuchAlgorithmException {
        KeyPair keyPair = generateKeyPair();
        String publicKey = Base64.encodeBase64String(keyPair.getPublic().getEncoded());
        String privateKey = Base64.encodeBase64String(keyPair.getPrivate().getEncoded());
        System.out.println("RSA公钥(给前端):" + publicKey);
        System.out.println("RSA私钥(后端保存,严禁泄露):" + privateKey);
    }
}

运行main方法,会生成一对公钥和私钥,复制公钥(publicKey),后续给前端使用;私钥(privateKey)保存到后端配置文件(如application.yml),建议用环境变量或配置中心管理,不要硬编码!

2. AES工具类(后端解密敏感信息用)

AES加密和解密的工具类,前端和后端要保持一致(加密模式、填充方式、偏移量统一),核心代码:

java 复制代码
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * AES工具类(前后端加密解密规则必须一致)
 */
public class AESUtil {
    // 加密模式(推荐CBC模式,安全性高于ECB)
    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
    // 偏移量(16位,前后端必须一致,可自定义,建议和AES密钥一起保管)
    private static final String IV = "1234567890abcdef";

    // AES加密
    public static String encrypt(String data, String key) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
        IvParameterSpec iv = new IvParameterSpec(IV.getBytes("UTF-8"));
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
        byte[] encryptedBytes = cipher.doFinal(data.getBytes("UTF-8"));
        return Base64.encodeBase64String(encryptedBytes);
    }

    // AES解密
    public static String decrypt(String encryptedData, String key) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
        IvParameterSpec iv = new IvParameterSpec(IV.getBytes("UTF-8"));
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
        byte[] decryptedBytes = cipher.doFinal(Base64.decodeBase64(encryptedData));
        return new String(decryptedBytes, "UTF-8");
    }
}
3. 后端接口(接收前端加密数据,解密并响应)

写一个测试接口,接收前端发送的"加密后的敏感信息"和"加密后的AES密钥",解密后返回原始数据:

java 复制代码
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
public class EncryptController {

    // 从配置文件读取RSA私钥(不要硬编码)
    @Value("${rsa.private-key}")
    private String rsaPrivateKey;

    /**
     * 接收前端加密数据,解密后返回原始信息
     * @param requestMap 前端传递的参数:encryptedData(AES加密后的敏感信息)、encryptedAesKey(RSA加密后的AES密钥)
     * @return 解密后的原始信息
     */
    @PostMapping("/api/encrypt/test")
    public Map<String, String> encryptTest(@RequestBody Map<String, String> requestMap) throws Exception {
        // 1. 获取前端传递的加密数据
        String encryptedData = requestMap.get("encryptedData");
        String encryptedAesKey = requestMap.get("encryptedAesKey");

        // 2. 用RSA私钥解密AES密钥
        String aesKey = RSAUtil.decryptByPrivateKey(encryptedAesKey, rsaPrivateKey);

        // 3. 用AES密钥解密敏感信息
        String originalData = AESUtil.decrypt(encryptedData, aesKey);

        // 4. 模拟业务逻辑(实际项目中可替换为存储、校验等操作)
        System.out.println("解密后的原始敏感信息:" + originalData);

        // 5. 响应结果(可根据需求加密响应数据,此处简化处理)
        return Map.of("code", "200", "msg", "解密成功", "originalData", originalData);
    }
}

配置文件(application.yml)添加RSA私钥配置:

yaml 复制代码
rsa:
  private-key: 此处粘贴之前生成的RSA私钥(完整复制,不要换行)

第二步:前端准备(Vue3+Axios)

前端需要两个核心依赖:crypto-js(处理AES加密)、jsencrypt(处理RSA加密),先安装依赖:

bash 复制代码
npm install crypto-js jsencrypt --save
1. 封装加密工具类(utils/encrypt.js)

和后端保持一致的加密规则(AES模式、偏移量),封装AES和RSA加密方法:

javascript 复制代码
import CryptoJS from 'crypto-js';
import JSEncrypt from 'jsencrypt';

// AES配置(和后端完全一致!)
const AES_CONFIG = {
  mode: CryptoJS.mode.CBC,
  padding: CryptoJS.pad.Pkcs7,
  iv: CryptoJS.enc.Utf8.parse('1234567890abcdef') // 偏移量,和后端IV一致
};

// RSA公钥(从后端获取,此处直接复制后端生成的公钥)
const RSA_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
此处粘贴后端生成的RSA公钥(完整复制,包括BEGIN和END行)
-----END PUBLIC KEY-----`;

/**
 * AES加密
 * @param data 要加密的敏感信息(字符串)
 * @param key 随机生成的AES密钥(16位,可自定义长度,建议16/24/32位)
 * @returns 加密后的字符串(Base64编码)
 */
export const aesEncrypt = (data, key) => {
  const keyHex = CryptoJS.enc.Utf8.parse(key);
  const encrypted = CryptoJS.AES.encrypt(data, keyHex, {
    iv: AES_CONFIG.iv,
    mode: AES_CONFIG.mode,
    padding: AES_CONFIG.padding
  });
  return encrypted.toString(); // 返回Base64编码的加密结果
};

/**
 * RSA加密(用公钥加密AES密钥)
 * @param data 要加密的AES密钥(字符串)
 * @returns 加密后的字符串(Base64编码)
 */
export const rsaEncrypt = (data) => {
  const encryptor = new JSEncrypt();
  encryptor.setPublicKey(RSA_PUBLIC_KEY); // 设置RSA公钥
  return encryptor.encrypt(data); // 返回加密后的结果
};

/**
 * 生成随机AES密钥(16位,每次请求生成一个新的)
 * @returns 16位随机字符串(AES密钥)
 */
export const generateAesKey = () => {
  // 生成16位随机字符串,可自定义规则
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  let key = '';
  for (let i = 0; i < 16; i++) {
    key += chars.charAt(Math.floor(Math.random() * chars.length));
  }
  return key;
};
2. 前端请求封装(对接后端接口)

在组件中使用加密工具类,实现敏感信息加密后发送请求,核心代码:

javascript 复制代码
import { ref } from 'vue';
import axios from 'axios';
import { aesEncrypt, rsaEncrypt, generateAesKey } from '@/utils/encrypt';

// 模拟敏感信息(如用户登录信息)
const sensitiveData = ref({
  phone: '13800138000',
  password: '12345678' // 实际项目中密码应先做哈希处理,再加密
});

// 发送加密请求
const sendEncryptRequest = async () => {
  try {
    // 1. 生成随机AES密钥(每次请求生成一个新的)
    const aesKey = generateAesKey();
    console.log('生成的AES密钥:', aesKey);

    // 2. 用AES密钥加密敏感信息(先将对象转为字符串)
    const dataStr = JSON.stringify(sensitiveData.value);
    const encryptedData = aesEncrypt(dataStr, aesKey);
    console.log('AES加密后的敏感信息:', encryptedData);

    // 3. 用RSA公钥加密AES密钥
    const encryptedAesKey = rsaEncrypt(aesKey);
    console.log('RSA加密后的AES密钥:', encryptedAesKey);

    // 4. 发送请求(将加密后的两个参数传给后端)
    const response = await axios.post('/api/encrypt/test', {
      encryptedData: encryptedData,
      encryptedAesKey: encryptedAesKey
    });

    // 5. 处理响应(此处简化,实际项目可根据后端响应逻辑处理)
    console.log('接口响应:', response.data);
    alert(`解密成功,原始信息:${response.data.originalData}`);
  } catch (error) {
    console.error('加密请求失败:', error);
    alert('请求失败,请重试');
  }
};

在模板中添加一个按钮,触发请求:

vue 复制代码
<template>
  <button @click="sendEncryptRequest">发送加密请求</button>
</template>

三、关键注意事项

  1. 前后端加密规则必须完全一致:AES的加密模式(CBC/ECB)、填充方式(PKCS5Padding/PKCS7Padding)、偏移量(IV),必须和后端保持统一,否则会解密失败。

  2. RSA密钥格式要正确 :前端使用的公钥,必须包含-----BEGIN PUBLIC KEY----------END PUBLIC KEY-----,后端私钥同理,缺少会导致加密/解密失败。

  3. AES密钥建议随机生成:每次请求生成一个新的AES密钥,避免密钥长期使用导致泄露风险;密钥长度建议16/24/32位(符合AES加密规范)。

  4. 私钥绝对不能泄露:后端的RSA私钥,严禁存储在前端、配置文件明文、代码仓库中,建议用配置中心、环境变量管理,定期更换密钥对。

  5. 敏感信息预处理:如密码等敏感信息,建议先做哈希处理(如MD5、SHA256),再进行AES加密,双重保障安全(即使AES密钥泄露,哈希后的密码也无法还原)。

四、总结

AES+RSA混合加密,本质是"用AES解决效率问题,用RSA解决安全问题",是前后端敏感信息传输的最优解之一。

记住:加密的核心是"密钥管理",只要保证RSA私钥不泄露、前后端加密规则一致,就能最大程度保障敏感信息的传输安全。

相关推荐
陈eaten5 天前
AES加解密流程解析
aes·aes加解密·分组密码·对称密码·现代密码学·aes指令集·rijndael
Asurplus6 天前
【VUE】17、使用JSEncrypt对数据加解密
javascript·vue.js·jsencrypt·rsa
修先生10 天前
Hive Udf函数AES加密
hive·aes·udf
小新同学^O^11 天前
简单学习 --> 数据加密
java·数据库·学习·数据加密
勿忘初心122118 天前
Java 国密 SM4 加密工具类实战(Hutool + BouncyCastle)|企业级数据加密 + 兼容 JDK8
java·数据安全·数据加密·后端开发·企业级开发·国密 sm4
一几文1 个月前
软考高级系统架构师25年下半年案例分析真题回顾带解析1,质量属性+质量属性场景+AES-256加密算法
架构·系统架构·软考高级·软考·aes·考证·质量属性
aovenus1 个月前
RSA 算法介绍
rsa
酿情师1 个月前
2026软件系统安全赛初赛RSA(赛后复盘)
android·网络·安全·密码学·rsa
温中志2 个月前
RSA加密算法入门
rsa·计算机密码学