当前位置: 首页 > news >正文

客户端与服务端数据加密方案及实现

在前后端交互中,以下业务场景通常需要进行数据加密:

需要加密的典型业务场景

  1. 用户认证信息:登录用户名、密码、短信验证码
  2. 个人敏感信息:身份证号、银行卡号、手机号
  3. 财务数据:交易金额、账户余额、支付信息
  4. 隐私内容:私信、医疗记录、位置信息
  5. 业务敏感数据:合同内容、商业机密、专利信息

推荐加密方案

方案1:HTTPS + 敏感字段单独加密(推荐)

  • 所有通信走HTTPS
  • 对特别敏感的数据在应用层额外加密

方案2:HTTPS + 全报文加密(高安全需求)

  • 所有通信走HTTPS
  • 整个请求/响应体进行加密

完整实现示例

前端实现 (Vue3/JavaScript)

安装加密库
npm install crypto-js
加密工具类 (utils/crypto.js)
import CryptoJS from 'crypto-js'// AES加密 (CBC模式)
export function encryptAES(data, key, iv) {const encrypted = CryptoJS.AES.encrypt(JSON.stringify(data),CryptoJS.enc.Utf8.parse(key),{iv: CryptoJS.enc.Utf8.parse(iv),mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7})return encrypted.toString()
}// AES解密
export function decryptAES(ciphertext, key, iv) {const decrypted = CryptoJS.AES.decrypt(ciphertext,CryptoJS.enc.Utf8.parse(key),{iv: CryptoJS.enc.Utf8.parse(iv),mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7})return JSON.parse(decrypted.toString(CryptoJS.enc.Utf8))
}// 生成随机密钥和IV (前端生成,通过RSA公钥加密传输给后端)
export function generateRandomKey() {const key = CryptoJS.lib.WordArray.random(32).toString()const iv = CryptoJS.lib.WordArray.random(16).toString()return { key, iv }
}
API请求封装示例
import { encryptAES, generateRandomKey } from '@/utils/crypto'// 假设这是从后端获取的RSA公钥
const RSA_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx6qHfR6pJZQ...`// 加密请求数据
async function encryptRequestData(data) {// 生成临时AES密钥const { key, iv } = generateRandomKey()// 用AES加密实际数据const encryptedData = encryptAES(data, key, iv)// 用RSA加密AES密钥const encryptedKey = await window.crypto.subtle.encrypt({ name: "RSA-OAEP" },await importRsaKey(RSA_PUBLIC_KEY),new TextEncoder().encode(key))return {data: encryptedData,key: arrayBufferToBase64(encryptedKey),iv: iv}
}// 发送加密请求
async function sendEncryptedRequest(url, payload) {const encrypted = await encryptRequestData(payload)return axios.post(url, encrypted)
}// 示例:登录请求
async function login(username, password) {const response = await sendEncryptedRequest('/api/login', {username,password})return response.data
}

后端实现 (Java Spring Boot)

添加依赖 (pom.xml)
<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.70</version>
</dependency>
加密工具类 (AesUtils.java)
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;public class AesUtils {private static final String AES_ALGORITHM = "AES/CBC/PKCS5Padding";public static String encrypt(String data, String key, String iv) throws Exception {SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));Cipher cipher = Cipher.getInstance(AES_ALGORITHM);cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);byte[] encryptedData = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));return Base64.getEncoder().encodeToString(encryptedData);}public static String decrypt(String encryptedData, String key, String iv) throws Exception {SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));Cipher cipher = Cipher.getInstance(AES_ALGORITHM);cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);byte[] decodedData = Base64.getDecoder().decode(encryptedData);byte[] decryptedData = cipher.doFinal(decodedData);return new String(decryptedData, StandardCharsets.UTF_8);}
}
RSA工具类 (RsaUtils.java)
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;public class RsaUtils {private static final String RSA_ALGORITHM = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";// 解密AES密钥public static String decryptAesKey(String encryptedKey, String privateKeyStr) throws Exception {byte[] encryptedBytes = Base64.getDecoder().decode(encryptedKey);PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyStr));PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);cipher.init(Cipher.DECRYPT_MODE, privateKey);byte[] decryptedBytes = cipher.doFinal(encryptedBytes);return new String(decryptedBytes, StandardCharsets.UTF_8);}
}
控制器示例 (LoginController.java)
@RestController
@RequestMapping("/api")
public class LoginController {@Value("${rsa.private-key}")private String rsaPrivateKey;@PostMapping("/login")public ResponseEntity<?> login(@RequestBody EncryptedRequest request) {try {// 1. 用RSA私钥解密AES密钥String aesKey = RsaUtils.decryptAesKey(request.getKey(), rsaPrivateKey);// 2. 用AES密钥解密数据String decryptedData = AesUtils.decrypt(request.getData(), aesKey, request.getIv());// 3. 处理业务逻辑LoginDTO loginDTO = objectMapper.readValue(decryptedData, LoginDTO.class);// ... 验证用户名密码等业务逻辑// 4. 加密响应数据String responseData = AesUtils.encrypt(objectMapper.writeValueAsString(response),aesKey,request.getIv());return ResponseEntity.ok(new EncryptedResponse(responseData));} catch (Exception e) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();}}// 请求体public static class EncryptedRequest {private String data;  // AES加密的业务数据private String key;    // RSA加密的AES密钥private String iv;     // AES IV// getters & setters}// 响应体public static class EncryptedResponse {private String data;  // AES加密的响应数据// constructor & getter}
}

密钥管理最佳实践

  1. 前端

    • 每次会话生成新的AES密钥
    • 使用RSA公钥加密传输AES密钥
    • 不要在前端存储长期有效的密钥
  2. 后端

    • 使用安全的密钥管理系统存储RSA私钥
    • 定期轮换RSA密钥对
    • 对不同业务使用不同的密钥
  3. 传输层

    • 必须使用HTTPS
    • 启用HSTS防止SSL剥离攻击

以下是加密解密流程及密钥来源说明:

客户端服务端密钥准备阶段生成随机AES密钥(key)和IV(iv)获取服务端RSA公钥(预置或动态获取)加密阶段用AES加密业务数据(key+iv)用RSA公钥加密AES密钥发送加密数据包:{data: AES加密的业务数据, key: RSA加密的AES密钥, iv: AES的IV向量}解密阶段用RSA私钥解密AES密钥用AES密钥+IV解密业务数据业务处理处理明文业务数据响应加密使用同一AES密钥加密响应数据返回加密响应:{data: AES加密的响应}响应解密用本地AES密钥解密响应数据客户端服务端

密钥来源说明(表格版)

组件密钥类型来源说明存储位置
RSA公钥非对称公钥1. 服务端生成密钥对
2. 公钥预置在客户端或首次连接时动态获取
客户端配置文件/内存
RSA私钥非对称私钥服务端生成后存储在安全位置(HSM/KMS/配置文件)服务端安全存储
AES密钥对称密钥1. 客户端每次会话随机生成
2. 通过RSA加密传输给服务端
会话期间内存存储
IV向量初始化向量客户端随机生成,随请求一起传输不存储,每次请求重新生成

关键流程图示

  1. 密钥交换流程

    [客户端]                   [服务端]|-- RSA公钥 (预置) ------>||                         ||-- RSA加密的AES密钥 ---->||                         |-- RSA私钥解密获得AES密钥
    
  2. 数据加密流程

    [客户端业务数据] --> AES加密(key+iv) --> 密文数据--> 通过HTTPS传输--> 服务端AES解密--> 明文业务数据
    
  3. 密钥生命周期

    AES密钥生成 -> RSA加密传输 -> 内存暂存 -> 会话结束销毁
    (客户端)       (HTTPS通道)    (服务端)     (自动清除)
    

这种设计结合了:

  • RSA的非对称加密安全性(用于密钥交换)
  • AES的对称加密高效性(用于数据加密)
  • 每次会话独立的密钥(前向安全性)
  • HTTPS的传输层保护(防窃听)

性能考虑

  1. 对于性能敏感场景,可以:

    • 会话期间缓存AES密钥
    • 对非敏感数据不加密或使用更轻量级的加密方式
    • 考虑使用ChaCha20-Poly1305替代AES-GCM(在移动设备上性能更好)
  2. 对于高安全需求场景:

    • 实现完整的端到端加密
    • 考虑使用专业的加密库如Google Tink
http://www.dtcms.com/a/271639.html

相关文章:

  • TCP的可靠传输机制
  • Linux系统-----nfs存储,共享配置文件
  • LLM 在预测下一个词的时候是怎么计算向量的,说明详细过程
  • QT6 源(158)模型视图架构里的文件系统模型 QFileSystemModel 篇一:属性,成员函数,与信号函数
  • 练习:对象数组 5
  • 【隐藏谷歌原生Qsb,将Widget中的Qsb组件替换至原位(解决GMS包添加后默认Qsb搜索框无法操作的问题)】
  • QT事件处理机制详解:从原理到实战
  • 深入浅出二分法:从实际问题看“最小化最大值”问题的求解之道
  • 技术支持丨解决 ServBay 在 Windows 启动时反复提示安装 .NET 的问题
  • 数据治理全景能力图谱与路线图:构建企业级数据治理的全貌视角
  • React 19 概览:新特性与生态系统变革
  • 缺乏项目进度数据沉淀,如何做好进度复盘
  • linux-用户和组
  • GIS使用方法详解
  • 在线生成树形目录文本
  • uniapp真机调试“没有检测到设备,请插入设备或启动模拟器后点击刷新再试”
  • TCP/IP常用协议
  • sftGRPO
  • 链表算法之【删除链表的倒数第n个节点】
  • 如何将FPGA设计的验证效率提升1000倍以上(3)
  • Spark流水线数据对比组件
  • vue3实战:.ts文件中的interface定义与抛出、其他文件的调用方式
  • Vue 中使用 Cesium 实现可拖拽点标记及坐标实时显示功能
  • 投机采样(Speculative Decoding)
  • Python—数据容器
  • 【解决方法】ollama在powershell或者cmd运行时乱码报错
  • C++11 std::move与std::move_backward深度解析
  • 7、整合前几篇插件列表
  • 单片机STM32F103:DMA的原理以及应用
  • 滚筒式茶叶杀青机设计【12张+总装图】+三维图+设计说明书+绛重