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

SpringBoot算法實(shí)現(xiàn)數(shù)據(jù)加密傳輸

 更新時(shí)間:2026年03月29日 08:53:28   作者:一線大碼  
這篇文章主要為大家詳細(xì)介紹了SpringBoot算法實(shí)現(xiàn)數(shù)據(jù)加密傳輸?shù)南嚓P(guān)知識(shí),本文主要是混合加密,前端 SM2 + SM4,后端 Spring Boot + Hutool 解密,希望對(duì)大家有所幫助

本文是混合加密:前端 SM2 + SM4,后端 Spring Boot + Hutool 解密的完整示例。

方案的邏輯是:

  • 前端隨機(jī)生成一個(gè) SM4 key
  • SM4 加密整個(gè)業(yè)務(wù) JSON
  • 用后端提供的 SM2 公鑰 加密這個(gè) SM4 key
  • 后端先用 SM2 私鑰 解出 SM4 key
  • 再用 SM4 解出業(yè)務(wù) JSON

Hutool 官方文檔明確支持 SM2 / SM3 / SM4,并給出了 SmUtil.sm2(...)、SmUtil.sm4(...) 以及 encryptHex / decryptStr 這類用法;同時(shí)文檔說(shuō)明國(guó)密算法需要引入 Bouncy Castle 依賴。sm-crypto 系列前端庫(kù)也支持 SM2 / SM3 / SM4。

方案統(tǒng)一用:

  • 前端公鑰:SM2 原始公鑰 Hex,04 + X + Y
  • SM2 密文:Hex
  • SM4 密文:Hex
  • SM4 key:16 字節(jié)字符串
  • SM2 模式C1C3C2

一、前后端協(xié)議

前端原始數(shù)據(jù):

{
  "username": "admin",
  "password": "123456",
  "timestamp": 1710000000000
}

前端最終提交給后端:

{
  "key": "SM2加密后的SM4密鑰(hex)",
  "data": "SM4加密后的業(yè)務(wù)JSON(hex)"
}

二、后端 Spring Boot 代碼

Maven 依賴

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Hutool -->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.29</version>
    </dependency>
    <!-- Bouncy Castle,Hutool 國(guó)密依賴 -->
    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcpkix-jdk18on</artifactId>
        <version>1.83</version>
    </dependency>
</dependencies>

Hutool 的國(guó)密文檔明確寫(xiě)了 SM2/SM3/SM4 依賴 Bouncy Castle;Hutool 加密模塊文檔也說(shuō)明其封裝入口之一就是 SmUtil。

啟動(dòng)類

package com.example.demo;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.security.Security;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        Security.addProvider(new BouncyCastleProvider());
        SpringApplication.run(DemoApplication.class, args);
    }
}

密鑰工具類

這個(gè)類負(fù)責(zé):

  • 生成 SM2 密鑰對(duì)
  • 導(dǎo)出前端可用的原始公鑰 Hex
  • 導(dǎo)出后端解密用的原始私鑰 Hex

Sm2KeyUtil.java

package com.example.demo.crypto;

import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.SM2;
import org.bouncycastle.jce.interfaces.BCECPrivateKey;
import org.bouncycastle.jce.interfaces.BCECPublicKey;
import org.bouncycastle.math.ec.ECPoint;

public class Sm2KeyUtil {

    private Sm2KeyUtil() {
    }

    public static SM2 generateSm2() {
        return SmUtil.sm2();
    }

    /**
     * 前端 sm-crypto 可直接使用的公鑰:
     * 04 + X(64位hex) + Y(64位hex)
     */
    public static String getPublicKeyHexForFrontend(SM2 sm2) {
        BCECPublicKey publicKey = (BCECPublicKey) sm2.getPublicKey();
        ECPoint point = publicKey.getQ();

        String x = leftPad64(point.getAffineXCoord().toBigInteger().toString(16));
        String y = leftPad64(point.getAffineYCoord().toBigInteger().toString(16));

        return "04" + x + y;
    }

    /**
     * 后端原始私鑰 hex,64位
     */
    public static String getPrivateKeyHexRaw(SM2 sm2) {
        BCECPrivateKey privateKey = (BCECPrivateKey) sm2.getPrivateKey();
        return leftPad64(privateKey.getD().toString(16));
    }

    /**
     * 按原始私鑰重建 SM2 對(duì)象
     */
    public static SM2 buildSm2ByPrivateKeyHex(String privateKeyHex) {
        return SmUtil.sm2(privateKeyHex, null);
    }

    private static String leftPad64(String hex) {
        if (hex == null) {
            return null;
        }
        if (hex.length() >= 64) {
            return hex;
        }
        return "0".repeat(64 - hex.length()) + hex;
    }
}

Hutool 官方文檔明確區(qū)分了 SM2 密鑰的幾種格式:私鑰可為 D 值、PKCS#8、PKCS#1,公鑰可為 Q 值、X.509、PKCS#1,并說(shuō)明新版本構(gòu)造方法對(duì)這些格式做了兼容。文檔還給出了用私鑰 D 值和公鑰 Q 值構(gòu)建/驗(yàn)簽的示例。

密鑰持有類

演示用啟動(dòng)時(shí)生成。生產(chǎn)環(huán)境要固定保存,不要每次重啟都換。

Sm2KeyHolder.java

package com.example.demo.crypto;

import cn.hutool.crypto.asymmetric.SM2;
import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Component;

@Component
public class Sm2KeyHolder {

    private String publicKeyHexForFrontend;
    private String privateKeyHexRaw;
    private SM2 sm2;

    @PostConstruct
    public void init() {
        this.sm2 = Sm2KeyUtil.generateSm2();
        this.publicKeyHexForFrontend = Sm2KeyUtil.getPublicKeyHexForFrontend(sm2);
        this.privateKeyHexRaw = Sm2KeyUtil.getPrivateKeyHexRaw(sm2);

        System.out.println("=== SM2密鑰初始化 ===");
        System.out.println("前端公鑰Hex: " + publicKeyHexForFrontend);
        System.out.println("后端私鑰Hex: " + privateKeyHexRaw);
    }

    public String getPublicKeyHexForFrontend() {
        return publicKeyHexForFrontend;
    }

    public String getPrivateKeyHexRaw() {
        return privateKeyHexRaw;
    }

    public SM2 getSm2() {
        return sm2;
    }
}

請(qǐng)求 DTO

EncryptedLoginRequest.java

package com.example.demo.dto;

public class EncryptedLoginRequest {

    /**
     * SM2加密后的SM4 key(hex)
     */
    private String key;

    /**
     * SM4加密后的業(yè)務(wù)數(shù)據(jù)(hex)
     */
    private String data;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}

LoginPlainRequest.java

package com.example.demo.dto;

public class LoginPlainRequest {

    private String username;
    private String password;
    private Long timestamp;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Long getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(Long timestamp) {
        this.timestamp = timestamp;
    }
}

解密服務(wù)

HybridCryptoService.java

package com.example.demo.service;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.symmetric.SM4;
import com.example.demo.crypto.Sm2KeyHolder;
import com.example.demo.crypto.Sm2KeyUtil;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;

@Service
public class HybridCryptoService {

    private final Sm2KeyHolder keyHolder;

    public HybridCryptoService(Sm2KeyHolder keyHolder) {
        this.keyHolder = keyHolder;
    }

    /**
     * 用后端私鑰解密前端傳來(lái)的 SM4 key
     */
    public String decryptSm4Key(String encryptedSm4KeyHex) {
        SM2 sm2 = Sm2KeyUtil.buildSm2ByPrivateKeyHex(keyHolder.getPrivateKeyHexRaw());
        byte[] keyBytes = sm2.decryptFromBcd(encryptedSm4KeyHex, KeyType.PrivateKey);
        return StrUtil.utf8Str(keyBytes);
    }

    /**
     * 用 SM4 key 解密業(yè)務(wù)數(shù)據(jù)
     */
    public String decryptBusinessData(String sm4Key, String encryptedDataHex) {
        SM4 sm4 = SmUtil.sm4(sm4Key.getBytes(StandardCharsets.UTF_8));
        return sm4.decryptStr(encryptedDataHex, StandardCharsets.UTF_8);
    }
}

Hutool 官方文檔給出了 SmUtil.sm4(key)、encryptHex(...)decryptStr(...) 的 SM4 用法,也給出了 sm2.decryptFromBcd(..., KeyType.PrivateKey) 的 SM2 私鑰解密示例。

控制器

LoginController.java

package com.example.demo.controller;

import cn.hutool.json.JSONUtil;
import com.example.demo.crypto.Sm2KeyHolder;
import com.example.demo.dto.EncryptedLoginRequest;
import com.example.demo.dto.LoginPlainRequest;
import com.example.demo.service.HybridCryptoService;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api")
public class LoginController {

    private final Sm2KeyHolder keyHolder;
    private final HybridCryptoService hybridCryptoService;

    public LoginController(Sm2KeyHolder keyHolder, HybridCryptoService hybridCryptoService) {
        this.keyHolder = keyHolder;
        this.hybridCryptoService = hybridCryptoService;
    }

    /**
     * 提供前端可直接使用的 SM2 原始公鑰
     */
    @GetMapping("/public-key")
    public Map<String, Object> getPublicKey() {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 0);
        result.put("publicKey", keyHolder.getPublicKeyHexForFrontend());
        return result;
    }

    /**
     * 混合加密登錄接口
     */
    @PostMapping("/login")
    public Map<String, Object> login(@RequestBody EncryptedLoginRequest request) {
        Map<String, Object> result = new HashMap<>();

        try {
            // 1. 解密 SM4 key
            String sm4Key = hybridCryptoService.decryptSm4Key(request.getKey());

            // 2. 解密業(yè)務(wù) JSON
            String plainJson = hybridCryptoService.decryptBusinessData(sm4Key, request.getData());

            // 3. 轉(zhuǎn)換為明文請(qǐng)求對(duì)象
            LoginPlainRequest loginRequest = JSONUtil.toBean(plainJson, LoginPlainRequest.class);

            // 4. 演示校驗(yàn)
            if ("admin".equals(loginRequest.getUsername())
                    && "123456".equals(loginRequest.getPassword())) {
                result.put("code", 0);
                result.put("message", "登錄成功");
            } else {
                result.put("code", 401);
                result.put("message", "用戶名或密碼錯(cuò)誤");
            }

            // 生產(chǎn)環(huán)境不要打印明文
            // System.out.println("解密后JSON: " + plainJson);

        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", "解密失敗: " + e.getMessage());
        }

        return result;
    }
}

三、前端代碼

這里是通用 JS,Vue / React / 原生都能直接使用。

安裝依賴

使用 sm-crypto,也可以用更新一點(diǎn)的 sm-crypto-v2。npm 上顯示 sm-crypto-v2 近期仍有更新,并明確支持 SM2/SM3/SM4。下面示例先按 sm-crypto 風(fēng)格來(lái)寫(xiě)。

npm install sm-crypto

混合加密工具

hybrid-login.js

import { sm2, sm4 } from "sm-crypto";

/**
 * 生成 16 字節(jié) SM4 key
 * 這里用 16 個(gè) ASCII 字符,后端按 UTF-8 字節(jié)拿到就是 16 字節(jié)
 */
function randomSm4Key(length = 16) {
  const chars = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
  let result = "";
  for (let i = 0; i < length; i++) {
    result += chars.charAt(Math.floor(Math.random() * chars.length));
  }
  return result;
}

/**
 * 獲取后端提供的 SM2 公鑰(原始hex,04開(kāi)頭)
 */
export async function getPublicKey() {
  const resp = await fetch("/api/public-key");
  const json = await resp.json();
  return json.publicKey;
}

/**
 * 混合加密:
 * 1. 隨機(jī)生成 SM4 key
 * 2. 用 SM4 加密整個(gè)業(yè)務(wù) JSON
 * 3. 用 SM2 公鑰加密 SM4 key
 */
export async function encryptLoginPayload(username, password) {
  const publicKey = await getPublicKey();

  // 1. 隨機(jī) SM4 key
  const sm4Key = randomSm4Key(16);

  // 2. 原始業(yè)務(wù) JSON
  const payload = JSON.stringify({
    username,
    password,
    timestamp: Date.now(),
  });

  // 3. SM4 加密業(yè)務(wù) JSON(輸出 hex)
  const encryptedData = sm4.encrypt(payload, sm4Key);

  // 4. SM2 加密 SM4 key(cipherMode=1 表示 C1C3C2)
  const cipherMode = 1;
  const encryptedKey = sm2.doEncrypt(sm4Key, publicKey, cipherMode);

  return {
    key: encryptedKey,
    data: encryptedData,
  };
}

/**
 * 提交登錄
 */
export async function login(username, password) {
  const body = await encryptLoginPayload(username, password);

  const resp = await fetch("/api/login", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });

  return await resp.json();
}

sm-crypto/同類包支持 SM2、SM4;Hutool 文檔則說(shuō)明 SM4 可以使用自定義 key,并通過(guò) encryptHex/decryptStr 處理字符串?dāng)?shù)據(jù)。

頁(yè)面調(diào)用示例

import { login } from "./hybrid-login";

async function submitLogin() {
  const username = document.getElementById("username").value;
  const password = document.getElementById("password").value;

  const result = await login(username, password);
  console.log(result);
}

四、完整交互過(guò)程

前端獲取公鑰

請(qǐng)求:

GET /api/public-key

響應(yīng):

{
  "code": 0,
  "publicKey": "04xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

這個(gè)公鑰是給前端 sm2.doEncrypt(...) 直接用的原始 SM2 公鑰。

前端組裝明文 JSON

{
  "username": "admin",
  "password": "123456",
  "timestamp": 1710000000000
}

前端生成隨機(jī) SM4 key

例如:

A8cD3eF7hJ2kL9mN

前端加密

  • data = sm4.encrypt(payload, sm4Key)
  • key = sm2.doEncrypt(sm4Key, publicKey, 1)

最終請(qǐng)求體:

{
  "key": "SM2加密后的SM4密鑰(hex)",
  "data": "SM4加密后的業(yè)務(wù)JSON(hex)"
}

后端解密

  • 用 SM2 私鑰解出 sm4Key
  • 用 SM4 key 解出 plainJson
  • 解析出 username/password/timestamp

五、為什么這樣更合理

“為什么不直接用 SM2”。這里混合加密的優(yōu)勢(shì)就是:

  • SM2 負(fù)責(zé)保護(hù)一個(gè)很小的隨機(jī)密鑰
  • SM4 負(fù)責(zé)高效加密真正的業(yè)務(wù)數(shù)據(jù)

Hutool 文檔本身也把 SM2 歸為非對(duì)稱加密,把 SM4 歸為對(duì)稱加密;這兩類算法在工程上本來(lái)就常常配合使用。

六、最容易踩的坑

1. 前端公鑰格式錯(cuò)

不能把 getPublicKeyBase64() 直接給前端。前端要的是 04 + X + Y 的原始公鑰,不是 X.509/ASN.1 編碼的公鑰。Hutool 文檔明確區(qū)分了公鑰的 Q 值X.509 兩種不同格式。

2. SM2 模式不一致

前端這里固定:

const cipherMode = 1;

聯(lián)調(diào)時(shí)就按 C1C3C2 統(tǒng)一,不要混。

3. SM4 key 長(zhǎng)度不對(duì)

Hutool 文檔中自定義 SM4 key 的示例是 128 位,也就是 16 字節(jié)。這里前端隨機(jī)生成 16 個(gè) ASCII 字符,后端按 UTF-8 讀取后恰好是 16 字節(jié)。

4. 后端每次重啟重新生成密鑰

演示可以這樣。生產(chǎn)不行。
生產(chǎn)環(huán)境要把私鑰固定存起來(lái),不然前端今天拿到的公鑰和明天后端的私鑰就不是一對(duì)了。

5. 仍然必須用 HTTPS

這套字段級(jí)加密不能替代 TLS。Hutool 只解決加解密實(shí)現(xiàn),不負(fù)責(zé)傳輸層安全。

七、生產(chǎn)版建議

可以先用上面代碼跑通,之后再補(bǔ)這幾項(xiàng):

  • 固定私鑰:放配置中心 / KMS / HSM
  • 時(shí)間戳校驗(yàn):比如 5 分鐘內(nèi)有效
  • nonce 防重放
  • 簽名校驗(yàn):在混合加密外再加簽,防篡改
  • 不要打印明文 JSON / 密碼
  • 全站 HTTPS

八、最小可驗(yàn)證步驟

先啟動(dòng)后端。

第一步,調(diào)用:

GET /api/public-key

確認(rèn)返回的 publicKey04 開(kāi)頭的長(zhǎng) hex 字符串。

第二步,前端執(zhí)行:

const body = await encryptLoginPayload("admin", "123456");
console.log(body);

應(yīng)該能看到:

{
  "key": "一串SM2 hex密文",
  "data": "一串SM4 hex密文"
}

第三步,調(diào)用:

login("admin", "123456")

應(yīng)該返回:

{
  "code": 0,
  "message": "登錄成功"
}

 以上就是SpringBoot算法實(shí)現(xiàn)數(shù)據(jù)加密傳輸?shù)脑敿?xì)內(nèi)容,更多關(guān)于SpringBoot數(shù)據(jù)加密傳輸?shù)馁Y料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

蓬安县| 安义县| 班玛县| 巴林右旗| 新郑市| 乌拉特前旗| 阳城县| 青神县| 四川省| 双柏县| 门源| 五河县| 越西县| 沙河市| 苏尼特左旗| 湘阴县| 突泉县| 安康市| 仙游县| 三江| 潜江市| 井冈山市| 彰化县| 昭通市| 清新县| 河北省| 商水县| 泽库县| 赫章县| 三穗县| 连城县| 利辛县| 大同县| 偏关县| 梧州市| 沿河| 平武县| 桐柏县| 海门市| 锡林浩特市| 静安区|