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

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

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

需要加密的典型业务场景

  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.xdnf.cn/news/14909.html

相关文章:

  • 1️⃣理解大语言模型
  • 深度学习——损失函数
  • 使用python 将多个docx文件合并为一个word
  • 电网的智能觉醒——人工智能重构能源生态的技术革命与公平悖论
  • vue3面试题(个人笔记)
  • 并发编程第一节
  • 首批 | 云轴科技ZStack加入施耐德电气技术本地化创新生态
  • Caffeine的tokenCache与Spring的CaffeineCacheManager缓存区别
  • 一文读懂动态规划:多种经典问题和思路
  • VScode SSH远程连接Ubuntu(通过SSH密钥对的方式)
  • 深度学习遇到的问题
  • C++如何进行性能优化?
  • qt绘制饼状图并实现点击即放大点击部分
  • 前端接收流式数据demo,并用markdown解析数据,包括EventSource和fetch两种方式
  • 前端交互自定义封装类:“双回调自定义信息弹窗”
  • 香港维尔利健康科技集团AI健康云平台通过国际信息安全认证,打造全球健康数据合规新标杆
  • Transformer-BiGRU、Transformer、CNN-BiGRU、BiGRU、CNN五模型回归预测对比,Matlab代码实现
  • Ollama+OpenWebUI 0.42+0.3.35 最新版一键安装教程,解决手动更新失败问题
  • 传输层协议TCP、UDP
  • [NOIP][C++]洛谷P1376 [USACO05MAR] Yogurt factory 机器工厂
  • 实战Linux进程状态观察:R、S、D、T、Z状态详解与实验模拟
  • 智能推荐社交分享小程序(websocket即时通讯、协同过滤算法、时间衰减因子模型、热度得分算法)
  • 自动驾驶感知系统
  • 爬虫-request处理POST
  • 当DMA想“越狱”:IOMMU怎么硬核拦截?
  • c语言学习_函数递归
  • 深度学习-多分类
  • Linux网络:UDP socket创建流程与简单通信
  • AWS OpenSearch Dev Tools使用
  • FastAPI Docker环境管理脚本使用指南