欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot集成ECDH密鑰交換的方法

 更新時(shí)間:2025年01月03日 09:19:43   作者:code2roc  
ECDH密鑰交換算法通過(guò)橢圓曲線和Diffie-Hellman方法生成共享密鑰,用于前端和后端之間的AES加密通信,前端使用elliptic.js生成密鑰對(duì),后端使用crypto-js.min.js進(jìn)行AES加密,本文給大家介紹SpringBoot集成ECDH密鑰交換的相關(guān)知識(shí),感興趣的朋友一起看看吧

簡(jiǎn)介

對(duì)稱加解密算法都需要一把秘鑰,但是很多情況下,互聯(lián)網(wǎng)環(huán)境不適合傳輸這把對(duì)稱密碼,有密鑰泄露的風(fēng)險(xiǎn),為了解決這個(gè)問(wèn)題ECDH密鑰交換應(yīng)運(yùn)而生

EC:Elliptic Curve——橢圓曲線,生成密鑰的方法

DH:Diffie-Hellman Key Exchange——交換密鑰的方法

設(shè)計(jì)

數(shù)據(jù)傳輸?shù)膬煞椒?wù)端(Server)和客戶端(Client)

服務(wù)端生成密鑰對(duì)Server-Public和Servier-Private

客戶端生成密鑰對(duì)Client-Public和Client-Private

客戶端獲取服務(wù)端的公鑰和客戶端的私鑰進(jìn)行計(jì)算CaculateKey(Server-Public,Client-Private)出共享密鑰ShareKey1

服務(wù)端獲取客戶端的公鑰和服務(wù)端的私鑰進(jìn)行計(jì)算CaculateKey(Client-Public,Server-Private)出共享密鑰ShareKey2

ShareKey1和ShareKey2必定一致,ShareKey就是雙方傳輸數(shù)據(jù)進(jìn)行AES加密時(shí)的密鑰

實(shí)現(xiàn)

生成密鑰對(duì)

后端

    public static ECDHKeyInfo generateKeyInfo(){
        ECDHKeyInfo keyInfo = new ECDHKeyInfo();
        try{
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
            ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");
            keyPairGenerator.initialize(ecSpec, new SecureRandom());
            KeyPair kp = keyPairGenerator.generateKeyPair();
            ECPublicKey ecPublicKey = (ECPublicKey) kp.getPublic();
            ECPrivateKey ecPrivateKey = (ECPrivateKey) kp.getPrivate();
            // 獲取公鑰點(diǎn)的x和y坐標(biāo)
            BigInteger x = ecPublicKey.getW().getAffineX();
            BigInteger y = ecPublicKey.getW().getAffineY();
            // 將x和y坐標(biāo)轉(zhuǎn)換為十六進(jìn)制字符串
            String xHex = x.toString(16);
            String yHex = y.toString(16);
            String publicKey = xHex + "|" + yHex;
            String privateKey = Base64.getEncoder().encodeToString(ecPrivateKey.getEncoded());
            keyInfo.setPublicKey(publicKey);
            keyInfo.setPrivateKey(privateKey);
        }catch (Exception e){
            e.printStackTrace();
        }
        return keyInfo;
    }
    public static class ECDHKeyInfo{
        private String publicKey;
        private String privateKey;
        public String getPublicKey() {
            return publicKey;
        }
        public void setPublicKey(String publicKey) {
            this.publicKey = publicKey;
        }
        public String getPrivateKey() {
            return privateKey;
        }
        public void setPrivateKey(String privateKey) {
            this.privateKey = privateKey;
        }
    }

前端

引入elliptic.js(https://cdn.bootcdn.net/ajax/libs/elliptic/6.5.6/elliptic.js)

    const EC = elliptic.ec;
    const ec = new EC('p256'); // P-256曲線
    // 生成密鑰對(duì)
    const keyPair = ec.genKeyPair();
	const publicKey = keyPair.getPublic().getX().toString('hex') + "|" + keyPair.getPublic().getY().toString('hex');

共享密鑰計(jì)算

后端

    public static String caculateShareKey(String serverPrivateKey,String receivePublicKey){
        String shareKey = "";
        try{
            // 1. 后端私鑰 Base64 字符串
            // 2. 從 Base64 恢復(fù)后端私鑰
            ECPrivateKey privKey = loadPrivateKeyFromBase64(serverPrivateKey);
            // 3. 前端傳遞的公鑰坐標(biāo) (x 和 y 坐標(biāo),假設(shè)為十六進(jìn)制字符串)
            // 假設(shè)這是從前端接收到的公鑰的 x 和 y 坐標(biāo)
            String xHex = receivePublicKey.split("\\|")[0];  // 用前端傳遞的 x 坐標(biāo)替換
            String yHex = receivePublicKey.split("\\|")[1];  // 用前端傳遞的 y 坐標(biāo)替換
            // 4. 將 x 和 y 轉(zhuǎn)換為 BigInteger
            BigInteger x = new BigInteger(xHex, 16);
            BigInteger y = new BigInteger(yHex, 16);
            // 5. 創(chuàng)建 ECPoint 對(duì)象 (公鑰坐標(biāo))
            ECPoint ecPoint = new ECPoint(x, y);
            // 6. 獲取 EC 參數(shù)(例如 secp256r1)
            ECParameterSpec ecSpec = getECParameterSpec();
            // 7. 恢復(fù)公鑰
            ECPublicKey pubKey = recoverPublicKey(ecPoint, ecSpec);
            // 8. 使用 ECDH 計(jì)算共享密鑰
            byte[] sharedSecret = calculateSharedSecret(privKey, pubKey);
            // 9. 打印共享密鑰
            shareKey = bytesToHex(sharedSecret);
        }catch (Exception e){
            e.printStackTrace();
        }
        return shareKey;
    }
    // 從 Base64 加載 ECPrivateKey
    private static ECPrivateKey loadPrivateKeyFromBase64(String privateKeyBase64) throws Exception {
        byte[] decodedKey = Base64.getDecoder().decode(privateKeyBase64);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
        KeyFactory keyFactory = KeyFactory.getInstance("EC");
        return (ECPrivateKey) keyFactory.generatePrivate(keySpec);
    }
    // 獲取 EC 參數(shù)(例如 secp256r1)
    private static ECParameterSpec getECParameterSpec() throws Exception {
        // 手動(dòng)指定 EC 曲線(例如 secp256r1)
        AlgorithmParameters params = AlgorithmParameters.getInstance("EC");
        params.init(new ECGenParameterSpec("secp256r1"));  // 使用標(biāo)準(zhǔn)的 P-256 曲線
        return params.getParameterSpec(ECParameterSpec.class);
    }
    // 恢復(fù)公鑰
    private static ECPublicKey recoverPublicKey(ECPoint ecPoint, ECParameterSpec ecSpec) throws Exception {
        ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(ecPoint, ecSpec);
        KeyFactory keyFactory = KeyFactory.getInstance("EC");
        return (ECPublicKey) keyFactory.generatePublic(pubKeySpec);
    }
    // 使用 ECDH 計(jì)算共享密鑰
    private static byte[] calculateSharedSecret(ECPrivateKey privKey, ECPublicKey pubKey) throws Exception {
        KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
        keyAgreement.init(privKey);
        keyAgreement.doPhase(pubKey, true);
        return keyAgreement.generateSecret();
    }
    // 將字節(jié)數(shù)組轉(zhuǎn)換為十六進(jìn)制字符串
    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            hexString.append(String.format("%02x", b));
        }
        return hexString.toString();
    }    

前端

    var keyArray = serverPublicPointKey.split("|")
    const otherKey = ec.keyFromPublic({ x: keyArray[0], y: keyArray[1] }, 'hex');
    const sharedSecret = keyPair.derive(otherKey.getPublic());

AES加密

后端

    public static String encryptData(String data,String shareKey){
        String result = "";
        try{
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] aesKey = digest.digest(shareKey.getBytes());  // 獲取 256 位密鑰
            SecretKey key = new SecretKeySpec(aesKey, "AES");
            byte[] resultData = encrypt(data,key);
            result = Base64.getEncoder().encodeToString(resultData);
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }
    public static String decryptData(String data,String shareKey){
        String result = "";
        try{
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] aesKey = digest.digest(shareKey.getBytes());  // 獲取 256 位密鑰
            SecretKey key = new SecretKeySpec(aesKey, "AES");
            byte[] resultData = decrypt(Base64.getDecoder().decode(data),key);
            result = new String(resultData);
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }
    private static final String KEY_ALGORITHM = "AES";
    private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
    private static final String IV = "0102030405060708"; // 16 bytes key
    // 使用AES密鑰加密數(shù)據(jù)
    private static byte[] encrypt(String plaintext, SecretKey aesKey) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(aesKey.getEncoded(), KEY_ALGORITHM);
        IvParameterSpec iv = new IvParameterSpec(IV.getBytes());
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
        byte[] encrypted = cipher.doFinal(plaintext.getBytes());
        return encrypted;
    }
    // 使用AES密鑰解密數(shù)據(jù)
    private static byte[] decrypt(byte[] encryptedData, SecretKey aesKey) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(aesKey.getEncoded(), KEY_ALGORITHM);
        IvParameterSpec iv = new IvParameterSpec(IV.getBytes());
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
        byte[] original = cipher.doFinal(encryptedData);
        return original;
    }    

前端

引入crypto-js.min.js(https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js)

function encryptByECDH(message, shareKey) {
    const aesKey = CryptoJS.SHA256(shareKey);
    const key = CryptoJS.enc.Base64.parse(aesKey.toString(CryptoJS.enc.Base64));
    return encryptByAES(message,key)
}
function decryptByECDH(message, shareKey) {
    const aesKey = CryptoJS.SHA256(shareKey);
    const key = CryptoJS.enc.Base64.parse(aesKey.toString(CryptoJS.enc.Base64));
    return decryptByAES(message,key)
}
function encryptByAES(message, key) {
    const iv = CryptoJS.enc.Utf8.parse("0102030405060708");
    const encrypted = CryptoJS.AES.encrypt(message, key, { iv: iv , mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
    return encrypted.toString();
}
function decryptByAES(message, key) {
    const iv = CryptoJS.enc.Utf8.parse("0102030405060708");
    const bytes = CryptoJS.AES.decrypt(message, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
    const originalText = bytes.toString(CryptoJS.enc.Utf8);
    return originalText;
}

注意

  • 前端生成的密鑰對(duì)和后端生成的密鑰對(duì)形式不一致,需要將前端的公鑰拆解成坐標(biāo)點(diǎn)到后端進(jìn)行公鑰還原
  • 同理后端的公鑰也要拆分成坐標(biāo)點(diǎn)傳輸?shù)角岸诉M(jìn)行計(jì)算
  • 生成的ShareKey共享密鑰為了滿足AES的密鑰長(zhǎng)度要求需要進(jìn)行Share256計(jì)算
  • 前后端AES互通需要保證IV向量為同一值

到此這篇關(guān)于SpringBoot集成ECDH密鑰交換的文章就介紹到這了,更多相關(guān)SpringBoot集成ECDH密鑰交換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring注解Autowired的底層實(shí)現(xiàn)原理詳解

    Spring注解Autowired的底層實(shí)現(xiàn)原理詳解

    從當(dāng)前springboot的火熱程度來(lái)看,java?config的應(yīng)用是越來(lái)越廣泛了,在使用java?config的過(guò)程當(dāng)中,我們不可避免的會(huì)有各種各樣的注解打交道,其中,我們使用最多的注解應(yīng)該就是@Autowired注解了。本文就來(lái)聊聊Autowired的底層實(shí)現(xiàn)原理
    2022-10-10
  • java模擬http請(qǐng)求的錯(cuò)誤問(wèn)題整理

    java模擬http請(qǐng)求的錯(cuò)誤問(wèn)題整理

    本文是小編給大家整理的在用java模擬http請(qǐng)求的時(shí)候遇到的錯(cuò)誤問(wèn)題整理,以及相關(guān)分析,有興趣的朋友參考下。
    2018-05-05
  • 詳解Java的日期時(shí)間新特性

    詳解Java的日期時(shí)間新特性

    隨著時(shí)間的不斷推移,現(xiàn)實(shí)的需求也在不斷更新,原先的一些API已經(jīng)難以滿足開(kāi)發(fā)需求了,從JDK?8之后,為了滿足更多的開(kāi)發(fā)需求,Java給我們?cè)黾恿瞬簧訇P(guān)于日期時(shí)間的新特性,接下來(lái)就帶各位來(lái)看看這些新特性有哪些,需要的朋友可以參考下
    2023-06-06
  • JavaWeb三大組件之Filter過(guò)濾器詳解

    JavaWeb三大組件之Filter過(guò)濾器詳解

    這篇文章主要介紹了JavaWeb三大組件之Filter過(guò)濾器詳解,過(guò)濾器Filter是Java?Web應(yīng)用中的一種組件,它在請(qǐng)求到達(dá)Servlet或JSP之前或者響應(yīng)送回客戶端之前,對(duì)請(qǐng)求和響應(yīng)進(jìn)行預(yù)處理和后處理操作,需要的朋友可以參考下
    2023-10-10
  • 使用Java編寫一個(gè)圖片word互轉(zhuǎn)工具

    使用Java編寫一個(gè)圖片word互轉(zhuǎn)工具

    這篇文章主要介紹了使用Java編寫一個(gè)PDF?Word文件轉(zhuǎn)換工具的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • MyBatis常用的jdbcType數(shù)據(jù)類型

    MyBatis常用的jdbcType數(shù)據(jù)類型

    這篇文章主要介紹了MyBatis常用的jdbcType數(shù)據(jù)類型的相關(guān)資料,需要的朋友可以參考下
    2016-12-12
  • java基于反射得到對(duì)象屬性值的方法

    java基于反射得到對(duì)象屬性值的方法

    這篇文章主要介紹了java基于反射得到對(duì)象屬性值的方法,結(jié)合實(shí)例形式分析了java基于反射獲取對(duì)象屬性值的相關(guān)實(shí)現(xiàn)方法與操作技巧,需要的朋友可以參考下
    2017-03-03
  • C++/java 繼承類的多態(tài)詳解及實(shí)例代碼

    C++/java 繼承類的多態(tài)詳解及實(shí)例代碼

    這篇文章主要介紹了C++/java 繼承類的多態(tài)詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Java批量轉(zhuǎn)換文件編碼格式的實(shí)現(xiàn)方法及實(shí)例代碼

    Java批量轉(zhuǎn)換文件編碼格式的實(shí)現(xiàn)方法及實(shí)例代碼

    這篇文章主要介紹了Java實(shí)現(xiàn) 批量轉(zhuǎn)換文件編碼格式的方法及實(shí)例代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-04-04
  • 詳解Java如何實(shí)現(xiàn)在PDF中插入,替換或刪除圖像

    詳解Java如何實(shí)現(xiàn)在PDF中插入,替換或刪除圖像

    圖文并茂的內(nèi)容往往讓人看起來(lái)更加舒服,如果只是文字內(nèi)容的累加,往往會(huì)使讀者產(chǎn)生視覺(jué)疲勞。搭配精美的文章配圖則會(huì)使文章內(nèi)容更加豐富。那我們要如何在PDF中插入、替換或刪除圖像呢?別擔(dān)心,今天為大家介紹一種高效便捷的方法
    2023-01-01

最新評(píng)論