ToB企服应用市场:ToB评测及商务社交产业平台

标题: SpringBoot集成ECDH密钥互换 [打印本页]

作者: 张春    时间: 2024-12-31 13:34
标题: SpringBoot集成ECDH密钥互换
简介

对称加解密算法都必要一把秘钥,但是很多情况下,互联网环境不适合传输这把对称密码,有密钥泄露的风险,为了解决这个问题ECDH密钥互换应运而生
EC:Elliptic Curve——椭圆曲线,生成密钥的方法
DH:Diffie-Hellman Key Exchange——互换密钥的方法
计划

数据传输的两方服务端(Server)和客户端(Client)
服务端生成密钥对Server-Public和Servier-Private
客户端生成密钥对Client-Public和Client-Private
客户端获取服务端的公钥和客户端的私钥进行计算CaculateKey(Server-Public,Client-Private)出共享密钥ShareKey1
服务端获取客户端的公钥和服务端的私钥进行计算CaculateKey(Client-Public,Server-Private)出共享密钥ShareKey2
ShareKey1和ShareKey2必定一致,ShareKey就是两边传输数据进行AES加密时的密钥
实现

生成密钥对

后端
  1.     public static ECDHKeyInfo generateKeyInfo(){
  2.         ECDHKeyInfo keyInfo = new ECDHKeyInfo();
  3.         try{
  4.             KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
  5.             ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");
  6.             keyPairGenerator.initialize(ecSpec, new SecureRandom());
  7.             KeyPair kp = keyPairGenerator.generateKeyPair();
  8.             ECPublicKey ecPublicKey = (ECPublicKey) kp.getPublic();
  9.             ECPrivateKey ecPrivateKey = (ECPrivateKey) kp.getPrivate();
  10.             // 获取公钥点的x和y坐标
  11.             BigInteger x = ecPublicKey.getW().getAffineX();
  12.             BigInteger y = ecPublicKey.getW().getAffineY();
  13.             // 将x和y坐标转换为十六进制字符串
  14.             String xHex = x.toString(16);
  15.             String yHex = y.toString(16);
  16.             String publicKey = xHex + "|" + yHex;
  17.             String privateKey = Base64.getEncoder().encodeToString(ecPrivateKey.getEncoded());
  18.             keyInfo.setPublicKey(publicKey);
  19.             keyInfo.setPrivateKey(privateKey);
  20.         }catch (Exception e){
  21.             e.printStackTrace();
  22.         }
  23.         return keyInfo;
  24.     }
  25.    
  26.     public static class ECDHKeyInfo{
  27.         private String publicKey;
  28.         private String privateKey;
  29.         public String getPublicKey() {
  30.             return publicKey;
  31.         }
  32.         public void setPublicKey(String publicKey) {
  33.             this.publicKey = publicKey;
  34.         }
  35.         public String getPrivateKey() {
  36.             return privateKey;
  37.         }
  38.         public void setPrivateKey(String privateKey) {
  39.             this.privateKey = privateKey;
  40.         }
  41.     }
  42.    
复制代码
前端

引入elliptic.js(https://cdn.bootcdn.net/ajax/libs/elliptic/6.5.6/elliptic.js)
  1.     const EC = elliptic.ec;
  2.     const ec = new EC('p256'); // P-256曲线
  3.     // 生成密钥对
  4.     const keyPair = ec.genKeyPair();
  5.         const publicKey = keyPair.getPublic().getX().toString('hex') + "|" + keyPair.getPublic().getY().toString('hex');
复制代码
共享密钥计算

后端
  1.     public static String caculateShareKey(String serverPrivateKey,String receivePublicKey){
  2.         String shareKey = "";
  3.         try{
  4.             // 1. 后端私钥 Base64 字符串
  5.             // 2. 从 Base64 恢复后端私钥
  6.             ECPrivateKey privKey = loadPrivateKeyFromBase64(serverPrivateKey);
  7.             // 3. 前端传递的公钥坐标 (x 和 y 坐标,假设为十六进制字符串)
  8.             // 假设这是从前端接收到的公钥的 x 和 y 坐标
  9.             String xHex = receivePublicKey.split("\\|")[0];  // 用前端传递的 x 坐标替换
  10.             String yHex = receivePublicKey.split("\\|")[1];  // 用前端传递的 y 坐标替换
  11.             // 4. 将 x 和 y 转换为 BigInteger
  12.             BigInteger x = new BigInteger(xHex, 16);
  13.             BigInteger y = new BigInteger(yHex, 16);
  14.             // 5. 创建 ECPoint 对象 (公钥坐标)
  15.             ECPoint ecPoint = new ECPoint(x, y);
  16.             // 6. 获取 EC 参数(例如 secp256r1)
  17.             ECParameterSpec ecSpec = getECParameterSpec();
  18.             // 7. 恢复公钥
  19.             ECPublicKey pubKey = recoverPublicKey(ecPoint, ecSpec);
  20.             // 8. 使用 ECDH 计算共享密钥
  21.             byte[] sharedSecret = calculateSharedSecret(privKey, pubKey);
  22.             // 9. 打印共享密钥
  23.             shareKey = bytesToHex(sharedSecret);
  24.         }catch (Exception e){
  25.             e.printStackTrace();
  26.         }
  27.         return shareKey;
  28.     }
  29.    
  30.     // 从 Base64 加载 ECPrivateKey
  31.     private static ECPrivateKey loadPrivateKeyFromBase64(String privateKeyBase64) throws Exception {
  32.         byte[] decodedKey = Base64.getDecoder().decode(privateKeyBase64);
  33.         PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
  34.         KeyFactory keyFactory = KeyFactory.getInstance("EC");
  35.         return (ECPrivateKey) keyFactory.generatePrivate(keySpec);
  36.     }
  37.     // 获取 EC 参数(例如 secp256r1)
  38.     private static ECParameterSpec getECParameterSpec() throws Exception {
  39.         // 手动指定 EC 曲线(例如 secp256r1)
  40.         AlgorithmParameters params = AlgorithmParameters.getInstance("EC");
  41.         params.init(new ECGenParameterSpec("secp256r1"));  // 使用标准的 P-256 曲线
  42.         return params.getParameterSpec(ECParameterSpec.class);
  43.     }
  44.     // 恢复公钥
  45.     private static ECPublicKey recoverPublicKey(ECPoint ecPoint, ECParameterSpec ecSpec) throws Exception {
  46.         ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(ecPoint, ecSpec);
  47.         KeyFactory keyFactory = KeyFactory.getInstance("EC");
  48.         return (ECPublicKey) keyFactory.generatePublic(pubKeySpec);
  49.     }
  50.     // 使用 ECDH 计算共享密钥
  51.     private static byte[] calculateSharedSecret(ECPrivateKey privKey, ECPublicKey pubKey) throws Exception {
  52.         KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
  53.         keyAgreement.init(privKey);
  54.         keyAgreement.doPhase(pubKey, true);
  55.         return keyAgreement.generateSecret();
  56.     }
  57.     // 将字节数组转换为十六进制字符串
  58.     private static String bytesToHex(byte[] bytes) {
  59.         StringBuilder hexString = new StringBuilder();
  60.         for (byte b : bytes) {
  61.             hexString.append(String.format("%02x", b));
  62.         }
  63.         return hexString.toString();
  64.     }   
复制代码
前端
  1.     var keyArray = serverPublicPointKey.split("|")
  2.     const otherKey = ec.keyFromPublic({ x: keyArray[0], y: keyArray[1] }, 'hex');
  3.     const sharedSecret = keyPair.derive(otherKey.getPublic());
复制代码
AES加密

后端
  1.     public static String encryptData(String data,String shareKey){
  2.         String result = "";
  3.         try{
  4.             MessageDigest digest = MessageDigest.getInstance("SHA-256");
  5.             byte[] aesKey = digest.digest(shareKey.getBytes());  // 获取 256 位密钥
  6.             SecretKey key = new SecretKeySpec(aesKey, "AES");
  7.             byte[] resultData = encrypt(data,key);
  8.             result = Base64.getEncoder().encodeToString(resultData);
  9.         }catch (Exception e){
  10.             e.printStackTrace();
  11.         }
  12.         return result;
  13.     }
  14.     public static String decryptData(String data,String shareKey){
  15.         String result = "";
  16.         try{
  17.             MessageDigest digest = MessageDigest.getInstance("SHA-256");
  18.             byte[] aesKey = digest.digest(shareKey.getBytes());  // 获取 256 位密钥
  19.             SecretKey key = new SecretKeySpec(aesKey, "AES");
  20.             byte[] resultData = decrypt(Base64.getDecoder().decode(data),key);
  21.             result = new String(resultData);
  22.         }catch (Exception e){
  23.             e.printStackTrace();
  24.         }
  25.         return result;
  26.     }
  27.    
  28.     private static final String KEY_ALGORITHM = "AES";
  29.     private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
  30.     private static final String IV = "0102030405060708"; // 16 bytes key
  31.     // 使用AES密钥加密数据
  32.     private static byte[] encrypt(String plaintext, SecretKey aesKey) throws Exception {
  33.         SecretKeySpec keySpec = new SecretKeySpec(aesKey.getEncoded(), KEY_ALGORITHM);
  34.         IvParameterSpec iv = new IvParameterSpec(IV.getBytes());
  35.         Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
  36.         cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
  37.         byte[] encrypted = cipher.doFinal(plaintext.getBytes());
  38.         return encrypted;
  39.     }
  40.     // 使用AES密钥解密数据
  41.     private static byte[] decrypt(byte[] encryptedData, SecretKey aesKey) throws Exception {
  42.         SecretKeySpec keySpec = new SecretKeySpec(aesKey.getEncoded(), KEY_ALGORITHM);
  43.         IvParameterSpec iv = new IvParameterSpec(IV.getBytes());
  44.         Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
  45.         cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
  46.         byte[] original = cipher.doFinal(encryptedData);
  47.         return original;
  48.     }   
复制代码
前端

引入crypto-js.min.js(https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js)
  1. function encryptByECDH(message, shareKey) {
  2.     const aesKey = CryptoJS.SHA256(shareKey);
  3.     const key = CryptoJS.enc.Base64.parse(aesKey.toString(CryptoJS.enc.Base64));
  4.     return encryptByAES(message,key)
  5. }
  6. function decryptByECDH(message, shareKey) {
  7.     const aesKey = CryptoJS.SHA256(shareKey);
  8.     const key = CryptoJS.enc.Base64.parse(aesKey.toString(CryptoJS.enc.Base64));
  9.     return decryptByAES(message,key)
  10. }
  11. function encryptByAES(message, key) {
  12.     const iv = CryptoJS.enc.Utf8.parse("0102030405060708");
  13.     const encrypted = CryptoJS.AES.encrypt(message, key, { iv: iv , mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
  14.     return encrypted.toString();
  15. }
  16. function decryptByAES(message, key) {
  17.     const iv = CryptoJS.enc.Utf8.parse("0102030405060708");
  18.     const bytes = CryptoJS.AES.decrypt(message, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
  19.     const originalText = bytes.toString(CryptoJS.enc.Utf8);
  20.     return originalText;
  21. }
复制代码
注意


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4