Java后端和前端的接口数据加密方案(椭圆曲线集成加密方案)

1.解决问题

前后端在交互过程中,请求和响应的数据需要加密处理,并保证安全和性能。

方案名称:ECIES (Elliptic Curve Integrated Encryption Scheme,椭圆曲线集成加密方案)

2.核心思路
  1. 服务端准备
    • 生成密钥对(EC-椭圆曲线),私钥servPriKey放在服务端。
    • 公钥servPubKey安全地预置或下发给客户端(如通过初始化接口)。
  2. 客户端加密(每次请求)
    • 生成临时密钥对(EC-椭圆曲线),公钥pubKey私钥priKey
    • 计算共享密钥Key (ECDH-协商+sha256),用私钥priKey + 服务端公钥servPubKey
    • 加密请求业务数据(AES):encryptData = AES.encrypt(data, Key)
    • 发送加密后的业务数据encryptData公钥pubKey给服务端
  3. 服务端解密
    • 计算共享密钥Key (ECDH-协商+sha256),用私钥servPriKey + 客户端公钥pubKey
    • 解密请求数据,data = AES.decrypt(encryptData, Key)
  4. 服务端加密
    • 通过3中计算的Key直接加密数据即可
  5. 客户端解密
    • 通过2中计算的Key直接解密数据即可
3.代码示例
java 复制代码
package com.visy.utils;

import cn.hutool.crypto.symmetric.AES;

import javax.crypto.KeyAgreement;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.function.BiConsumer;
import java.util.function.Consumer;


/**
 * ECIES (Elliptic Curve Integrated Encryption Scheme,椭圆曲线集成加密方案)
 * 这个方案是椭圆曲线密码学中的密码学方案,用于实现前后端间通信的数据加密,是应用内的加密机制。
 * 核心是密钥协商:使用ECDH算法生成密钥对,并计算出共享密钥。
 */
public class ECIESDemo {

    //Base64工具类
    static class BASE64 {
        public static String encode(byte[] data) {
            return Base64.getEncoder().encodeToString(data);
        }

        public static byte[] decode(String base64Str) {
            return Base64.getDecoder().decode(base64Str);
        }
    }

    //ECDH工具
    static class ECDH {
        public static StrKeyPair generateKeyPair(String curveName) throws Exception {
            // 1. 指定椭圆曲线参数,例如 secp256r1 (NIST P-256)
            ECGenParameterSpec ecSpec = new ECGenParameterSpec(curveName);
            // 2. 生成ECC密钥对
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
            kpg.initialize(ecSpec, new SecureRandom());
            KeyPair keyPair = kpg.generateKeyPair();
            return StrKeyPair.of(keyPair);
        }

        /**
         * ECDH核心方法:计算共享秘密
         * @param myPriKey 己方的私钥(Base64字符串)
         * @param otherPubKey 对方的公钥(Base64字符串)
         * @return Base64编码的共享秘密字符串
         */
        public static String deriveSharedSecret(String myPriKey, String otherPubKey) throws Exception {
            PrivateKey _myPriKey = privateKeyFromBase64(myPriKey);
            PublicKey _otherPubKey = publicKeyFromBase64(otherPubKey);

            KeyAgreement ka = KeyAgreement.getInstance("ECDH");
            ka.init(_myPriKey);
            ka.doPhase(_otherPubKey, true);

            byte[] rawSecret = ka.generateSecret();
            //用SHA-256哈希一次,得到32字节AES-256密钥
            MessageDigest sha256 = MessageDigest.getInstance("SHA-256");

            String shearedSecret = BASE64.encode(sha256.digest(rawSecret));
            System.out.println("【共享密钥】计算-私钥:"+myPriKey);
            System.out.println("【共享密钥】计算-公钥:"+otherPubKey);
            System.out.println("【共享密钥】计算-结果:"+shearedSecret);
            System.out.println("---------------------------------");
            return shearedSecret;
        }

        private static PublicKey publicKeyFromBase64(String publicKeyBase64) throws Exception {
            byte[] decodedBytes = BASE64.decode(publicKeyBase64);
            KeyFactory keyFactory = KeyFactory.getInstance("EC");
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedBytes);
            return keyFactory.generatePublic(keySpec);
        }

        private static PrivateKey privateKeyFromBase64(String privateKeyBase64) throws Exception {
            byte[] decodedBytes = BASE64.decode(privateKeyBase64);
            KeyFactory keyFactory = KeyFactory.getInstance("EC");
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedBytes);
            return keyFactory.generatePrivate(keySpec);
        }
    }

    //AES加解密工具
    static class Aes {
        public static String  encrypt(String data, String key){
            AES aes = new AES(BASE64.decode(key));
            return aes.encryptBase64(data);
        }
        public static String decrypt(String data, String key){
            AES aes = new AES(BASE64.decode(key));
            return aes.decryptStr(BASE64.decode(data));
        }
    }

    //模拟客户端(如浏览器)
    static class Client {
        //服务端公钥,固定值,后续不再更新
        private final String servPubKey;

        public Client(String servPubKey){
            this.servPubKey = servPubKey;
            System.out.println("【客户端】初始化完成,服务端公钥: " + servPubKey);
            System.out.println("---------------------------------");
        }

        public void request(Server server, String message, BiConsumer<String,String> callback) throws Exception {
            //生成密钥对
            StrKeyPair keyPair = genKeyPair();
            //计算共享密钥
            String sharedSecret = getSharedSecret(keyPair.getPriKey());
            //对称加密数据
            String reqData = Aes.encrypt(message, sharedSecret);
            //封装传输通道
            Channel channel = new Channel(keyPair.getPubKey(), reqData, resData -> {
                callback.accept(resData, sharedSecret); //回调把请求的共享密钥带上,用于解密
            });
            //发送给服务端
            server.receive(channel);
        }

        private StrKeyPair genKeyPair() throws Exception {
            StrKeyPair keyPair = ECDH.generateKeyPair("secp256r1");
            System.out.println("【客户端】公钥: " + keyPair.getPubKey());
            System.out.println("【客户端】私钥: " + keyPair.getPriKey());
            System.out.println("【客户端】已刷新密钥对!");
            System.out.println("---------------------------------");
            return keyPair;
        }

        private String getSharedSecret(String priKey) throws Exception {
            return ECDH.deriveSharedSecret(priKey, this.servPubKey);
        }
    }

    //模拟后台服务端
    static class Server {
        //服务端密钥对,只生成一次
        private final StrKeyPair keyPair;

        public Server() throws Exception {
            this.keyPair = ECDH.generateKeyPair("secp256r1");
            System.out.println("【服务端】公钥: " + this.keyPair.getPubKey());
            System.out.println("【服务端】私钥: " + this.keyPair.getPriKey());
            System.out.println("【服务端】初始化完成!");
            System.out.println("---------------------------------");
        }

        public String getPubKey(){
            return this.keyPair.getPubKey();
        }

        public void receive(Channel channel) throws Exception {
            //客户端公钥
            String clientPubKey = channel.getClientPubKey();
            //计算共享密钥
            String sharedSecret = getSharedSecret(clientPubKey);
            //解密数据
            String message = Aes.decrypt(channel.read(), sharedSecret);
            System.out.println("【服务端】收到消息: " + message);
            System.out.println("【服务端】客户端公钥:" + clientPubKey);
            System.out.println("---------------------------------");

            //响应消息
            String resMsg = "服务端已收到消息->"+ message;
            //加密
            String data = Aes.encrypt(resMsg, sharedSecret);
            //发送
            channel.write(data);
        }

        /**
         * 计算共享密钥(服务端私钥 + 客户端公钥)
         */
        private String getSharedSecret(String clientPubKey) throws Exception {
            return ECDH.deriveSharedSecret(this.keyPair.getPriKey(), clientPubKey);
        }
    }

    //模拟请求通道
    static class Channel {
        private final String reqData;
        private final String clientPubKey;
        private final Consumer<String> callback;

        public Channel(String clientPubKey, String reqData, Consumer<String> callback) {
            this.reqData = reqData;
            this.clientPubKey = clientPubKey;
            this.callback = callback;
        }

        //获取客户端公钥
        public String getClientPubKey(){
            return this.clientPubKey;
        }

        //读取数据
        public String read() {
            return this.reqData;
        }

        //写入数据
        public void write(String resData){
            this.callback.accept(resData);
        }
    }

    //密钥对(base64字符串)
    static class StrKeyPair {
        private final String priKey;
        private final String pubKey;

        private StrKeyPair(KeyPair keyPair) {
            this.priKey = BASE64.encode(keyPair.getPrivate().getEncoded());
            this.pubKey = BASE64.encode(keyPair.getPublic().getEncoded());
        }

        public static StrKeyPair of(KeyPair keyPair) {
            return new StrKeyPair(keyPair);
        }

        public String getPriKey() {
            return this.priKey;
        }

        public String getPubKey() {
            return this.pubKey;
        }
    }

    //测试
    public static void main(String[] args) throws  Exception {
        //创建服务端
        Server server = new Server();
        //创建客户端
        Client client = new Client(server.getPubKey());

        //向服务端发送请求
        client.request(server, "hello world", (resData, sharedSecret) -> {
            //解密数据
            String message = Aes.decrypt(resData, sharedSecret);
            System.out.println("【客户端】收到消息: " + message);
        });
        System.out.println("============================================");
        //向服务端发送请求
        client.request(server, "{\"name\": \"张三\"}", (resData, sharedSecret) -> {
            //解密数据
            String message = Aes.decrypt(resData, sharedSecret);
            System.out.println("【客户端】收到消息: " + message);
        });
        System.out.println("============================================");
    }
}
4.输出
java 复制代码
【服务端】公钥: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUutqZsAN1Kh+FFvS95lKFob0zKZZY0mtBKNOYFSZ/WzaLO8CHH1zjcH3nV82MxXwyj0t+yhP/Dugfriy8VIWbg==
【服务端】私钥: MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAaJ/UnsK4Tt+1Vyt9vyAnYqinr8uobgPZOlvCMeihKkg==
【服务端】初始化完成!
---------------------------------
【客户端】初始化完成,服务端公钥: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUutqZsAN1Kh+FFvS95lKFob0zKZZY0mtBKNOYFSZ/WzaLO8CHH1zjcH3nV82MxXwyj0t+yhP/Dugfriy8VIWbg==
---------------------------------
【客户端】公钥: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDz5YRdfRm1l0Wj0BqIBMEq2zLca96eidX/DEIZipQol8gzV4YKiLwtWxmS2rplAay1/4stiuofvLFZZAJ0rM/w==
【客户端】私钥: MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCDLGtJ2Lxm+8cd+o1iDbxkigojjFdy+9k/wVhQ0XJTRQw==
【客户端】已刷新密钥对!
---------------------------------
【共享密钥】计算-私钥:MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCDLGtJ2Lxm+8cd+o1iDbxkigojjFdy+9k/wVhQ0XJTRQw==
【共享密钥】计算-公钥:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUutqZsAN1Kh+FFvS95lKFob0zKZZY0mtBKNOYFSZ/WzaLO8CHH1zjcH3nV82MxXwyj0t+yhP/Dugfriy8VIWbg==
【共享密钥】计算-结果:fIPyLCxiu26stP2C4bGyCU8Eh+uYUWp3w8PjRWdxh8k=
---------------------------------
【共享密钥】计算-私钥:MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAaJ/UnsK4Tt+1Vyt9vyAnYqinr8uobgPZOlvCMeihKkg==
【共享密钥】计算-公钥:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDz5YRdfRm1l0Wj0BqIBMEq2zLca96eidX/DEIZipQol8gzV4YKiLwtWxmS2rplAay1/4stiuofvLFZZAJ0rM/w==
【共享密钥】计算-结果:fIPyLCxiu26stP2C4bGyCU8Eh+uYUWp3w8PjRWdxh8k=
---------------------------------
【服务端】收到消息: hello world
【服务端】客户端公钥:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDz5YRdfRm1l0Wj0BqIBMEq2zLca96eidX/DEIZipQol8gzV4YKiLwtWxmS2rplAay1/4stiuofvLFZZAJ0rM/w==
---------------------------------
【客户端】收到消息: 服务端已收到消息->hello world
============================================
【客户端】公钥: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGbyRS8JTE32TLAMXYaFpi8pY8Cf2R7u9gdta+PnB7thgyAZ5FEBGjgRhNEL2MFD7g5B8yLX3cTghpjisajs4Tw==
【客户端】私钥: MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCADaXXP8AmBXXhrDJIXpFBH9byFr+ak38DAY1J1+zCktQ==
【客户端】已刷新密钥对!
---------------------------------
【共享密钥】计算-私钥:MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCADaXXP8AmBXXhrDJIXpFBH9byFr+ak38DAY1J1+zCktQ==
【共享密钥】计算-公钥:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUutqZsAN1Kh+FFvS95lKFob0zKZZY0mtBKNOYFSZ/WzaLO8CHH1zjcH3nV82MxXwyj0t+yhP/Dugfriy8VIWbg==
【共享密钥】计算-结果:7ngHo0nz8W2Zv9vMKORu5jC7uPIhgsZaS/657vCpcUs=
---------------------------------
【共享密钥】计算-私钥:MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAaJ/UnsK4Tt+1Vyt9vyAnYqinr8uobgPZOlvCMeihKkg==
【共享密钥】计算-公钥:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGbyRS8JTE32TLAMXYaFpi8pY8Cf2R7u9gdta+PnB7thgyAZ5FEBGjgRhNEL2MFD7g5B8yLX3cTghpjisajs4Tw==
【共享密钥】计算-结果:7ngHo0nz8W2Zv9vMKORu5jC7uPIhgsZaS/657vCpcUs=
---------------------------------
【服务端】收到消息: {"name": "张三"}
【服务端】客户端公钥:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGbyRS8JTE32TLAMXYaFpi8pY8Cf2R7u9gdta+PnB7thgyAZ5FEBGjgRhNEL2MFD7g5B8yLX3cTghpjisajs4Tw==
---------------------------------
【客户端】收到消息: 服务端已收到消息->{"name": "张三"}
============================================
相关推荐
CodeSheep2 小时前
百度又一知名产品,倒下了!
前端·后端·程序员
零雲2 小时前
java面试:@Resource和@Autowired的区别
java·开发语言·面试
阿芯爱编程2 小时前
前端面试题linux操作
前端·面试
007php0072 小时前
Git 操作偏门指南:常用和隐藏命令与问题解决
java·git·面试·职场和发展·golang·jenkins·php
li.wz2 小时前
溯源数据清洗:一次由“可控”到“失控”的复盘
java·后端·doris
仅此,2 小时前
Java请求进入Python FastAPI 后,请求体为空,参数不合法
java·spring boot·python·组合模式·fastapi
毕设源码-郭学长2 小时前
【开题答辩全过程】以 基于springboot的健身房信息管理为例,包含答辩的问题和答案
java·spring boot·后端
Bug.ink2 小时前
BUUCTF——WEB(4)
前端·网络安全·靶场·ctf·buuctf
L Jiawen2 小时前
【Web】RESTful风格
前端·后端·restful