最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java實現(xiàn)后端和前端的接口數(shù)據(jù)加密方案詳解

 更新時間:2026年04月03日 09:23:46   作者:咘嚕biu  
這篇文章主要為大家詳細介紹了一種基于橢圓曲線集成加密方案(ECIES)的前后端數(shù)據(jù)安全交互方法,該方案通過橢圓曲線密碼學(xué)實現(xiàn)密鑰協(xié)商,結(jié)合AES對稱加密保障數(shù)據(jù)傳輸安全,感興趣的小伙伴可以了解下

1.解決問題

前后端在交互過程中,請求和響應(yīng)的數(shù)據(jù)需要加密處理,并保證安全和性能。

方案名稱:ECIES (Elliptic Curve Integrated Encryption Scheme,橢圓曲線集成加密方案)

2.核心思路

服務(wù)端準備

  • 生成密鑰對(EC-橢圓曲線),私鑰servPriKey放在服務(wù)端。
  • 公鑰servPubKey安全地預(yù)置或下發(fā)給客戶端(如通過初始化接口)。

客戶端加密(每次請求)

  • 生成臨時密鑰對(EC-橢圓曲線),公鑰pubKey,私鑰priKey
  • 私鑰priKey + 服務(wù)端公鑰servPubKey計算共享密鑰Key(ECDH-協(xié)商+sha256)
  • 加密請求業(yè)務(wù)數(shù)據(jù)(AES):encryptData = AES.encrypt(data, Key)
  • 發(fā)送加密后的業(yè)務(wù)數(shù)據(jù)encryptData公鑰pubKey給服務(wù)端

服務(wù)端解密

  • 私鑰servPriKey + 客戶端公鑰pubKey計算共享密鑰Key(ECDH-協(xié)商+sha256)
  • 解密請求數(shù)據(jù),data = AES.decrypt(encryptData, Key)

服務(wù)端加密:通過3中計算的Key直接加密數(shù)據(jù)即可

客戶端解密:通過2中計算的Key直接解密數(shù)據(jù)即可

3.代碼示例

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.Consumer;


/**
 * ECIES (Elliptic Curve Integrated Encryption Scheme,橢圓曲線集成加密方案)
 * 這個方案是橢圓曲線密碼學(xué)中的密碼學(xué)方案,用于實現(xiàn)前后端間通信的數(shù)據(jù)加密,是應(yīng)用內(nèi)的加密機制。
 * 核心是密鑰協(xié)商:使用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. 指定橢圓曲線參數(shù),例如 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 myPrivateKey = toPrivateKey(myPriKey);
            PublicKey otherPublicKey = toPublicKey(otherPubKey);

            KeyAgreement ka = KeyAgreement.getInstance("ECDH");
            ka.init(myPrivateKey);
            ka.doPhase(otherPublicKey, true);

            byte[] rawSecret = ka.generateSecret();
            //用SHA-256哈希一次,得到32字節(jié)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("【共享密鑰】計算-結(jié)果:"+shearedSecret);
            System.out.println("---------------------------------");
            return shearedSecret;
        }

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

        private static PrivateKey toPrivateKey(String priKey) throws Exception {
            byte[] decodedBytes = BASE64.decode(priKey);
            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 {
        //服務(wù)端公鑰,固定值,后續(xù)不再更新
        private final String servPubKey;

        public Client(String servPubKey){
            this.servPubKey = servPubKey;
            System.out.println("【客戶端】初始化完成,服務(wù)端公鑰: " + servPubKey);
            System.out.println("---------------------------------");
        }

        public void request(Server server, String message, Consumer<String> callback) throws Exception {
            //生成密鑰對
            StrKeyPair keyPair = genKeyPair();
            //計算共享密鑰
            String sharedSecret = getSharedSecret(keyPair.getPriKey());
            //對稱加密數(shù)據(jù)
            String reqData = Aes.encrypt(message, sharedSecret);
            //封裝傳輸通道
            Channel channel = new Channel(keyPair.getPubKey(), reqData, resData -> {
                //解密數(shù)據(jù)
                String data = Aes.decrypt(resData, sharedSecret);
                //將數(shù)據(jù)傳給回調(diào)
                callback.accept(data);
            });
            //發(fā)送給服務(wù)端
            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);
        }
    }

    //模擬后臺服務(wù)端
    static class Server {
        //服務(wù)端密鑰對,只生成一次
        private final StrKeyPair keyPair;

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

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

        public void receive(Channel channel) throws Exception {
            //客戶端公鑰
            String clientPubKey = channel.getClientPubKey();
            //計算共享密鑰
            //因為服務(wù)端私鑰是固定的
            //如果客戶端不是每次請求都刷新密鑰對,可以按clientPubKey直接緩存Key
            //但是要注意內(nèi)存溢出,可以設(shè)置緩存有效時間,指定最大緩存?zhèn)€數(shù)等
            String sharedSecret = getSharedSecret(clientPubKey);
            //解密數(shù)據(jù)
            String message = Aes.decrypt(channel.read(), sharedSecret);
            System.out.println("【服務(wù)端】收到消息: " + message);
            System.out.println("【服務(wù)端】客戶端公鑰:" + clientPubKey);
            System.out.println("---------------------------------");

            //響應(yīng)消息
            String resMsg = "服務(wù)端已收到消息->"+ message;
            //加密
            String data = Aes.encrypt(resMsg, sharedSecret);
            //發(fā)送
            channel.write(data);
        }

        /**
         * 計算共享密鑰(服務(wù)端私鑰 + 客戶端公鑰)
         */
        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;
        }

        //讀取數(shù)據(jù)
        public String read() {
            return this.reqData;
        }

        //寫入數(shù)據(jù)
        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 {
        //創(chuàng)建服務(wù)端
        Server server = new Server();
        //創(chuàng)建客戶端
        Client client = new Client(server.getPubKey());

        //向服務(wù)端發(fā)送請求
        client.request(server, "hello world", data -> {
            System.out.println("【客戶端】收到消息: " + data);
        });
        System.out.println("============================================");
        //向服務(wù)端發(fā)送請求
        client.request(server, "{\"name\": \"張三\"}", data -> {
            System.out.println("【客戶端】收到消息: " + data);
        });
        System.out.println("============================================");
    }
}

4.輸出

【服務(wù)端】公鑰: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUutqZsAN1Kh+FFvS95lKFob0zKZZY0mtBKNOYFSZ/WzaLO8CHH1zjcH3nV82MxXwyj0t+yhP/Dugfriy8VIWbg==
【服務(wù)端】私鑰: MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAaJ/UnsK4Tt+1Vyt9vyAnYqinr8uobgPZOlvCMeihKkg==
【服務(wù)端】初始化完成!
---------------------------------
【客戶端】初始化完成,服務(wù)端公鑰: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUutqZsAN1Kh+FFvS95lKFob0zKZZY0mtBKNOYFSZ/WzaLO8CHH1zjcH3nV82MxXwyj0t+yhP/Dugfriy8VIWbg==
---------------------------------
【客戶端】公鑰: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDz5YRdfRm1l0Wj0BqIBMEq2zLca96eidX/DEIZipQol8gzV4YKiLwtWxmS2rplAay1/4stiuofvLFZZAJ0rM/w==
【客戶端】私鑰: MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCDLGtJ2Lxm+8cd+o1iDbxkigojjFdy+9k/wVhQ0XJTRQw==
【客戶端】已刷新密鑰對!
---------------------------------
【共享密鑰】計算-私鑰:MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCDLGtJ2Lxm+8cd+o1iDbxkigojjFdy+9k/wVhQ0XJTRQw==
【共享密鑰】計算-公鑰:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUutqZsAN1Kh+FFvS95lKFob0zKZZY0mtBKNOYFSZ/WzaLO8CHH1zjcH3nV82MxXwyj0t+yhP/Dugfriy8VIWbg==
【共享密鑰】計算-結(jié)果:fIPyLCxiu26stP2C4bGyCU8Eh+uYUWp3w8PjRWdxh8k=
---------------------------------
【共享密鑰】計算-私鑰:MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAaJ/UnsK4Tt+1Vyt9vyAnYqinr8uobgPZOlvCMeihKkg==
【共享密鑰】計算-公鑰:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDz5YRdfRm1l0Wj0BqIBMEq2zLca96eidX/DEIZipQol8gzV4YKiLwtWxmS2rplAay1/4stiuofvLFZZAJ0rM/w==
【共享密鑰】計算-結(jié)果:fIPyLCxiu26stP2C4bGyCU8Eh+uYUWp3w8PjRWdxh8k=
---------------------------------
【服務(wù)端】收到消息: hello world
【服務(wù)端】客戶端公鑰:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDz5YRdfRm1l0Wj0BqIBMEq2zLca96eidX/DEIZipQol8gzV4YKiLwtWxmS2rplAay1/4stiuofvLFZZAJ0rM/w==
---------------------------------
【客戶端】收到消息: 服務(wù)端已收到消息->hello world
============================================
【客戶端】公鑰: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGbyRS8JTE32TLAMXYaFpi8pY8Cf2R7u9gdta+PnB7thgyAZ5FEBGjgRhNEL2MFD7g5B8yLX3cTghpjisajs4Tw==
【客戶端】私鑰: MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCADaXXP8AmBXXhrDJIXpFBH9byFr+ak38DAY1J1+zCktQ==
【客戶端】已刷新密鑰對!
---------------------------------
【共享密鑰】計算-私鑰:MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCADaXXP8AmBXXhrDJIXpFBH9byFr+ak38DAY1J1+zCktQ==
【共享密鑰】計算-公鑰:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUutqZsAN1Kh+FFvS95lKFob0zKZZY0mtBKNOYFSZ/WzaLO8CHH1zjcH3nV82MxXwyj0t+yhP/Dugfriy8VIWbg==
【共享密鑰】計算-結(jié)果:7ngHo0nz8W2Zv9vMKORu5jC7uPIhgsZaS/657vCpcUs=
---------------------------------
【共享密鑰】計算-私鑰:MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAaJ/UnsK4Tt+1Vyt9vyAnYqinr8uobgPZOlvCMeihKkg==
【共享密鑰】計算-公鑰:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGbyRS8JTE32TLAMXYaFpi8pY8Cf2R7u9gdta+PnB7thgyAZ5FEBGjgRhNEL2MFD7g5B8yLX3cTghpjisajs4Tw==
【共享密鑰】計算-結(jié)果:7ngHo0nz8W2Zv9vMKORu5jC7uPIhgsZaS/657vCpcUs=
---------------------------------
【服務(wù)端】收到消息: {"name": "張三"}
【服務(wù)端】客戶端公鑰:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGbyRS8JTE32TLAMXYaFpi8pY8Cf2R7u9gdta+PnB7thgyAZ5FEBGjgRhNEL2MFD7g5B8yLX3cTghpjisajs4Tw==
---------------------------------
【客戶端】收到消息: 服務(wù)端已收到消息->{"name": "張三"}
============================================

5.落地建議

服務(wù)端的加解密數(shù)據(jù)操作應(yīng)該添加全局處理,比如攔截器,AOP切面等。

客戶端的加解密數(shù)據(jù)操作同樣應(yīng)該全局處理,比如 axios的攔截器內(nèi)。

全局處理加解密數(shù)據(jù)可以讓編寫業(yè)務(wù)的人不用關(guān)心加解密,使用起來是無感的(就像不加解密之前一樣)

前后端應(yīng)該協(xié)商好傳輸?shù)母袷剑热纾?/p>

POST提交,JSON格式:

{
	"key": "客戶端公鑰",
	"data": "加密數(shù)據(jù)"
}

GET提交:url?key=客戶端公鑰&data=加密數(shù)據(jù)

6.結(jié)合前端完整示例

本示例中為了和前端js插件統(tǒng)一,所有密鑰均用十六進制(hex)表示,不再用Base64的形式

6.1后端代碼

package com.visy.utils;

import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;

import javax.crypto.KeyAgreement;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.*;
import java.util.Arrays;
import java.util.Base64;

/**
 * Hex 十六進制版本的ECDH工具
 */
public class HexECDHDemo {
    static class Hex {
        /**
         * BigInteger轉(zhuǎn)64字符hex(補零)
         */
        private static String to64Hex(BigInteger num) {
            String hex = num.toString(16);
            if (hex.length() < 64) {
                return repeatedStr('0', 64 - hex.length()) + hex;
            }
            return hex;
        }

        private static String bytesToHex(byte[] bytes) {
            return HexUtil.encodeHexStr(bytes);
        }

        public static byte[] hexToBytes(String hex){
            return HexUtil.decodeHex(hex);
        }

        private static String repeatedStr(char ch, int count) {
            if (count <= 0) {
                return "";
            }
            char[] chars = new char[count];
            Arrays.fill(chars, ch);
            return new String(chars);
        }
    }

    public static class StrKeyPair {
        private final String priKey;
        private final String pubKey;

        private StrKeyPair(String priKey, String pubKey) {
            this.priKey = priKey;
            this.pubKey = pubKey;
        }

        public static StrKeyPair of(KeyPair keyPair) {
            // 1. 提取公鑰坐標
            ECPublicKey ecPubKey = (ECPublicKey) keyPair.getPublic();
            ECPoint point = ecPubKey.getW(); //返回橢圓曲線上的點 (x, y坐標)

            // 2. 格式化為04+x+y格式
            BigInteger x = point.getAffineX(), y = point.getAffineY();
            String xHex = Hex.to64Hex(x), yHex = Hex.to64Hex(y);
            String publicKey = "04" + xHex + yHex;

            // 3. 提取私鑰原始值
            ECPrivateKey ecPriKey = (ECPrivateKey) keyPair.getPrivate();
            String privateKey = Hex.to64Hex(ecPriKey.getS()); //返回私鑰的標量值(大整數(shù))

            return new StrKeyPair(privateKey, publicKey);
        }

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

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

    public static class ECDH {
        private static final String CURVE_NAME = "secp256r1";
        /**
         * 生成密鑰對(返回hex格式)
         */
        public static StrKeyPair genKeyPair() throws Exception {
            // 1. 生成標準密鑰對
            ECGenParameterSpec ecSpec = new ECGenParameterSpec(CURVE_NAME);
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
            kpg.initialize(ecSpec, new SecureRandom());
            KeyPair keyPair = kpg.generateKeyPair();

            //轉(zhuǎn)換成Hex字符串的密鑰對
            return StrKeyPair.of(keyPair);
        }

        public static String derive(String myPriKey, String otherPubKey) throws Exception {
            PrivateKey myPrivateKey = toPrivateKey(myPriKey);
            PublicKey otherPublicKey = toPublicKey(otherPubKey);

            // 3. 執(zhí)行ECDH
            KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
            keyAgreement.init(myPrivateKey);
            keyAgreement.doPhase(otherPublicKey, true);

            // 4. 獲取共享密鑰(原始字節(jié))
            byte[] sharedSecret = keyAgreement.generateSecret();

            // 5. 返回hex
            return Hex.bytesToHex(sharedSecret);
        }

        /**
         * 將04+x+y格式的hex公鑰轉(zhuǎn)換為Java PublicKey
         */
        private static PublicKey toPublicKey(String pubKey) throws Exception {
            if (!pubKey.startsWith("04")) {
                throw new IllegalArgumentException("公鑰必須以04開頭");
            }else if(pubKey.length() !=  130){
                throw new IllegalArgumentException("公鑰格式有誤");
            }

            // 提取x, y坐標
            String xHex = pubKey.substring(2, 66), yHex = pubKey.substring(66, 130);
            BigInteger x = new BigInteger(xHex, 16), y = new BigInteger(yHex, 16);

            // 獲取曲線參數(shù)
            ECParameterSpec ecParams = getECParameterSpec();

            // 創(chuàng)建EC點
            ECPoint point = new ECPoint(x, y);

            // 創(chuàng)建公鑰規(guī)范
            ECPublicKeySpec keySpec = new ECPublicKeySpec(point, ecParams);
            KeyFactory keyFactory = KeyFactory.getInstance("EC");

            return keyFactory.generatePublic(keySpec);
        }

        /**
         * 將hex私鑰轉(zhuǎn)換為Java PrivateKey
         */
        private static PrivateKey toPrivateKey(String priKey) throws Exception {
            BigInteger s = new BigInteger(priKey, 16);

            // 獲取曲線參數(shù)
            ECParameterSpec ecParams = getECParameterSpec();

            // 創(chuàng)建私鑰規(guī)范
            ECPrivateKeySpec keySpec = new ECPrivateKeySpec(s, ecParams);
            KeyFactory keyFactory = KeyFactory.getInstance("EC");

            return keyFactory.generatePrivate(keySpec);
        }

        /**
         * 獲取橢圓曲線參數(shù)
         */
        private static ECParameterSpec getECParameterSpec() throws Exception {
            // 通過生成臨時密鑰對獲取曲線參數(shù)
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
            kpg.initialize(new ECGenParameterSpec(CURVE_NAME));
            KeyPair tempKeyPair = kpg.generateKeyPair();

            return ((ECPublicKey) tempKeyPair.getPublic()).getParams();
        }
    }

    //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);
        }
    }

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

    public static void main(String[] args) throws Exception {
        // 生成密鑰對,只生成一次,將公鑰和前端共享
        //StrKeyPair strKeyPair = ECDH.genKeyPair();
        //04122670c45aa251ecef1528f76c248e288cd35dd728538764cbdaf3efa1c91bccc184daec61cc9abd660f947a530da82e3b4a76a07271d0d96a92660592722d39
        //System.out.println("服務(wù)端公鑰:" + strKeyPair.getPubKey());
        //9353c717bb2d65b5516e0e6c56fa7574592767d8abe70e796c1719e8f9d4d55b
        //System.out.println("服務(wù)端私鑰:" + strKeyPair.getPriKey());

        //服務(wù)端私鑰(需保密)
        String servPrivateKey = "9353c717bb2d65b5516e0e6c56fa7574592767d8abe70e796c1719e8f9d4d55b";
        //客戶端公鑰,來自前端請求參數(shù)
        String clientPublicKey = "04dd42dadd5d99539c15410aede7943a9c55ecb175d687feac37a602293e8a90fc76daa2d1d0186147628cb8ca24e86e7710b5e024e5a0d7ff55d0b342cf4f2d6e";
        //計算共享秘密(Aes 加密密鑰)
        String secret = ECDH.derive(servPrivateKey, clientPublicKey);
        System.out.println("共享秘密:"+ secret);

        //來自前端的向量iv和加密后的數(shù)據(jù)
        String iv = "34f5653b77a3b85db7826a40768d12a3";
        String encryptData = "pBkwIDR15BWS7xk2R5YPVmnXasrQpyybTq+C1N8/XTU=";

        //解密并輸出結(jié)果
        System.out.println("解密結(jié)果:"+Aes.decrypt(encryptData, secret, iv));
    }
}

6.2 前端代碼

<!doctype html>
<html>
  <head >
	<title>ECDH Test</title>
  </head>
  <body >
	  <div style="text-align: center">
		<div style = "margin-top:200px;display:flex;flex-direction: column;align-items: center;gap: 10px;">
		  <div style="width: 800px;display:flex;flex-direction: row">
			<div style="width:150px;text-align:right;margin-right:10px">服務(wù)端公鑰: </div>
			<textarea rows="3" cols="80" id="serv_pub_key_ipt" style="font-size: 20px"></textarea>
			<button id="compute" style="margin-left:20px">生成并計算共享密鑰</button>
		  </div>
		  <div style="width: 800px;display:flex;flex-direction: row">
			<div style="width:150px;text-align:right;margin-right:10px">本地公鑰: </div>
			<textarea rows="3" cols="80" id="pub_key_ipt" style="font-size: 20px"></textarea>
		  </div>
		  <div style="width: 800px;display:flex;flex-direction: row">
			<div style="width:150px;text-align:right;margin-right:10px">本地私鑰: </div>
			<textarea rows="3" cols="80" id="pri_key_ipt" style="font-size: 20px"></textarea>
		  </div>
		  <div style="width: 800px;display:flex;flex-direction: row">
			<div style="width:150px;text-align:right;margin-right:10px">共享秘密: </div>
			<textarea rows="3" cols="80" id="sheared_key_ipt" style="font-size: 20px; font-weight:bold"></textarea>
		  </div>
		  <div style="width: 800px;display:flex;flex-direction: row">
			<div style="width:150px;text-align:right;margin-right:10px">發(fā)送內(nèi)容: </div>
			<textarea rows="3" cols="80" id="data_ipt" style="font-size: 20px"></textarea>
			<button id="do_encrypt" style="margin-left:20px">加密</button>
		  </div>
		  <div style="width: 800px;display:flex;flex-direction: row">
			<div style="width:150px;text-align:right;margin-right:10px">IV: </div>
			<textarea rows="1" cols="80" id="iv_ipt" style="font-size: 20px; font-weight:bold"></textarea>
		  </div>
		  <div style="width: 800px;display:flex;flex-direction: row">
			<div style="width:150px;text-align:right;margin-right:10px">加密結(jié)果: </div>
			<textarea rows="3" cols="80" id="encrypt_data_ipt" style="font-size: 20px; font-weight:bold"></textarea>
		  </div>
		</div>
	  </div>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> 
	<!-- ecdh用到的插件,生成密鑰對,計算共享秘密 -->
	<script src="https://cdnjs.cloudflare.com/ajax/libs/elliptic/6.6.1/elliptic.min.js"></script>
	<!-- 數(shù)據(jù)加密插件,需用到AES -->
	<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js"></script>
	<script>
		(function(){
			const EC = elliptic.ec;
			const ec = new EC('p256'); // 選擇橢圓曲線secp256k1的別名是p256
			//生成共享秘密
			$("#compute").on("click", function (){
			  const servPubKey = $("#serv_pub_key_ipt").val();
				if(servPubKey==null || !(servPubKey+"").trim()){
					alert("請先輸入服務(wù)端公鑰!");
					return;
				}
				const keyPair = ec.genKeyPair(); //生成密鑰對
				const priKey = keyPair.getPrivate("hex");
				const pubKey = keyPair.getPublic("hex");
				$("#pub_key_ipt").val(pubKey);
				$("#pri_key_ipt").val(priKey);
				const secret = derive(priKey, servPubKey);
				$("#sheared_key_ipt").val(secret);
			})
			//加密
			$("#do_encrypt").on("click", function(){
				const data = $("#data_ipt").val();
				if(data==null || !(data+"").trim()){
					alert("請先輸入發(fā)送內(nèi)容!");
					return;
				}
				const secret = $("#sheared_key_ipt").val();
				if(secret==null || !(secret+"").trim()){
					alert("請先生成共享秘密!");
					return;
				}
			    const iv = generateIV();
				$("#iv_ipt").val(iv);
			    const encryptData = aesEncrypt(data, secret, iv);
				$("#encrypt_data_ipt").val(encryptData);
			})
			// 2. 使用對方公鑰和自己的私鑰生成共享密鑰
			function derive(myPriKey, otherPubKey) {
			  try {
				// 從十六進制字符串加載自己的私鑰
				const myPrivateKey = ec.keyFromPrivate(myPriKey, 'hex');
				// 從十六進制字符串加載對方的公鑰
				const otherPublicKey = ec.keyFromPublic(otherPubKey, 'hex').getPublic();
				// 計算共享密鑰
				const sharedSecret = myPrivateKey.derive(otherPublicKey);
				return sharedSecret.toString(16);
			  } catch (error) {
				console.error('生成共享密鑰失敗:', error);
				return null;
			  }
			}
			/**
			 * AES加密函數(shù)
			 * @param {string} plaintext - 明文
			 * @param {string} keyHex - 密鑰(64字符hex,對應(yīng)32字節(jié))
			 * @param {string} ivHex - 初始向量(32字符hex,對應(yīng)16字節(jié))
			 * @returns {string} Base64格式的加密結(jié)果
			 */
			function aesEncrypt(plaintext, keyHex, ivHex) {
				// 將hex字符串轉(zhuǎn)換為CryptoJS格式
				var key = CryptoJS.enc.Hex.parse(keyHex);
				var iv = CryptoJS.enc.Hex.parse(ivHex);
				// 執(zhí)行AES-256-CBC加密
				var encrypted = CryptoJS.AES.encrypt(plaintext, key, {
					iv: iv,
					mode: CryptoJS.mode.CBC,
					padding: CryptoJS.pad.Pkcs7
				});
				// 返回Base64字符串
				return encrypted.toString();
			}
			/**
			 * AES解密函數(shù)
			 * @param {string} ciphertextBase64 - Base64格式的密文
			 * @param {string} keyHex - 密鑰(64字符hex)
			 * @param {string} ivHex - 初始向量(32字符hex)
			 * @returns {string} 解密后的明文
			 */
			function aesDecrypt(ciphertextBase64, keyHex, ivHex) {
				// 將hex字符串轉(zhuǎn)換為CryptoJS格式
				var key = CryptoJS.enc.Hex.parse(keyHex);
				var iv = CryptoJS.enc.Hex.parse(ivHex);
				// 執(zhí)行AES-256-CBC解密
				var decrypted = CryptoJS.AES.decrypt(ciphertextBase64, key, {
					iv: iv,
					mode: CryptoJS.mode.CBC,
					padding: CryptoJS.pad.Pkcs7
				});
				// 轉(zhuǎn)換為UTF-8字符串
				return decrypted.toString(CryptoJS.enc.Utf8);
			}
			/**
			 * 生成隨機IV(16字節(jié))
			 * @returns {string} 32字符的hex IV
			 */
			function generateIV() {
				// 生成16字節(jié)隨機數(shù)
				var randomBytes = CryptoJS.lib.WordArray.random(16);
				// 轉(zhuǎn)換為hex字符串
				return randomBytes.toString(CryptoJS.enc.Hex);
			}
		})();
	</script>
  </body>
</html>

前端頁面

前端傳參

{
	"clientPublicKey": "04dd42dadd5d99539c15410aede7943a9c55ecb175d687feac37a602293e8a90fc76daa2d1d0186147628cb8ca24e86e7710b5e024e5a0d7ff55d0b342cf4f2d6e",
	"iv": "34f5653b77a3b85db7826a40768d12a3",
	"encryptData": "pBkwIDR15BWS7xk2R5YPVmnXasrQpyybTq+C1N8/XTU="
}

后端輸出

后端需接收前端的本地公鑰 + IV + 加密結(jié)果,然后計算出共享秘密,解密數(shù)據(jù)

共享秘密:f2e5a1b995524d818c9b6c5076e5bcace40641a6244f91dfe72b560f3723fc3d
解密結(jié)果:{"name":"張三","age":25}

到此這篇關(guān)于Java實現(xiàn)后端和前端的接口數(shù)據(jù)加密方案詳解的文章就介紹到這了,更多相關(guān)Java接口數(shù)據(jù)加密內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

安国市| 阿勒泰市| 商都县| 隆德县| 阿瓦提县| 乌拉特前旗| 彰化县| 肇东市| 巨鹿县| 保定市| 雷州市| 陈巴尔虎旗| 讷河市| 堆龙德庆县| 福海县| 临高县| 新乐市| 鄄城县| 武强县| 长岭县| 东明县| 绥阳县| 淳化县| 鹿泉市| 镇赉县| 滨州市| 卢氏县| 三门县| 绵阳市| 弥渡县| 临桂县| 隆安县| 阳新县| 中山市| 延寿县| 滕州市| 襄汾县| 甘南县| 泾源县| 嘉荫县| 灵武市|