使用hutool工具类实现sm2和rsa公钥加密 私钥解密demo

1.相关依赖

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-web-services'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    implementation 'cn.hutool:hutool-all:5.8.15'
    implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
}

2.接口

package com.ruoyi.rsa_sm2_demo.controller;

import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.SM2;
import io.micrometer.common.util.StringUtils;
import jakarta.annotation.PostConstruct;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.web.bind.annotation.*;

import java.security.PrivateKey;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/crypto")
public class CryptoController {

    private RSA rsa;
    private SM2 sm2;

    @PostConstruct
    public void init() {
        // 在PostConstruct中初始化,而不是在字段声明时直接初始化
        rsa = new RSA();
        
        // 初始化SM2,使用SmUtil工具类生成密钥对,并设置为C1C3C2模式
        sm2 = SmUtil.sm2();
        sm2.setMode(SM2Engine.Mode.C1C3C2);
        sm2.usePlainEncoding();
        
        //打印密钥信息用于调试
        System.out.println("RSA公钥: " + rsa.getPublicKeyBase64());
        System.out.println("RSA私钥: " + rsa.getPrivateKeyBase64());

        System.out.println("SM2公钥: " + sm2.getPublicKeyBase64());
        System.out.println("SM2私钥: " + sm2.getPrivateKeyBase64());
    }

    @GetMapping("/keys")
    public Map<String, Object> getKeys() {
        Map<String, Object> keys = new HashMap<>();
        // RSA密钥
        keys.put("rsaPublicKey", rsa.getPublicKeyBase64());
        keys.put("rsaPrivateKey", rsa.getPrivateKeyBase64());
        // SM2密钥
        keys.put("sm2PublicKey", sm2.getPublicKeyBase64());
        keys.put("sm2PrivateKey", sm2.getPrivateKeyBase64());
        return keys;
    }

    @PostMapping("/rsa/decrypt")
    public String rsaDecrypt(@RequestBody String encryptedData) {
        try {
            // 检查输入是否为空
            if (StringUtils.isEmpty(encryptedData)) {
                throw new IllegalArgumentException("加密数据不能为空");
            }

            // 执行解密
            return rsa.decryptStr(encryptedData.trim(), KeyType.PrivateKey);
        } catch (Exception e) {
            throw new RuntimeException("RSA解密失败:" + e.getMessage());
        }
    }

    @PostMapping("/rsa/encrypt")
    public String rsaEncrypt(@RequestBody String data) {
        try {
            if (StringUtils.isEmpty(data)) {
                throw new IllegalArgumentException("待加密数据不能为空");
            }
            return rsa.encryptBase64(data, KeyType.PublicKey);
        } catch (Exception e) {
            throw new RuntimeException("RSA加密失败:" + e.getMessage());
        }
    }

    @PostMapping("/sm2/decrypt")
    public String sm2Decrypt(@RequestBody Sm2Param sm2Param) {
        String encryptedData = sm2Param.getData();
        if (StringUtils.isEmpty(encryptedData)) {
            throw new IllegalArgumentException("加密数据不能为空");
        }
        
        try {
            // 预处理输入数据,移除所有空白字符
            encryptedData = encryptedData.replaceAll("\\s+", "");
            
            // 预处理输入数据,替换URL安全的Base64字符
            encryptedData = encryptedData.replace('-', '+').replace('_', '/');
            
            // 尝试Base64解码,先尝试标准Base64,如果失败则尝试URL安全的Base64
            byte[] decodedBytes;
            try {
                try {
                    decodedBytes = Base64.getDecoder().decode(encryptedData);
                } catch (IllegalArgumentException e) {
                    // 如果标准Base64解码失败,尝试URL安全的Base64
                    decodedBytes = Base64.getUrlDecoder().decode(encryptedData);
                }
            } catch (IllegalArgumentException e) {
                throw new IllegalArgumentException("Base64解码失败:请确保输入的是有效的Base64编码字符串");
            }
            
            // 基本格式验证
            if (decodedBytes.length < 45) {
                throw new IllegalArgumentException("数据格式错误:不是有效的SM2加密数据");
            }
            // 假设 sm2 是你的 SM2 加密工具类实例
// 先获取私钥对象
            String privateKey = sm2.getPrivateKeyBase64();

            System.out.println(privateKey);

            // 执行解密
            String decryptedData = sm2.decryptStr(encryptedData, KeyType.PrivateKey);
            if (StringUtils.isEmpty(decryptedData)) {
                throw new IllegalArgumentException("解密结果为空");
            }
            
            return decryptedData;
            
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("SM2解密失败:" + e.getMessage());
        } catch (Exception e) {
            throw new IllegalArgumentException("SM2解密失败:密文格式错误或密钥不匹配");
        }
    }
    public static class Sm2Param{
        private String data;

        public String getData() {
            return data;
        }

        public void setData(String data) {
            this.data = data;
        }
    }
    @PostMapping("/sm2/encrypt")
    public String sm2Encrypt(@RequestBody Sm2Param param) {
        String data = param.getData();
        try {
            if (StringUtils.isEmpty(data)) {
                throw new IllegalArgumentException("待加密数据不能为空");
            }
            // 去除可能的空白字符
            data = data.trim();
            
            try {
                // 执行加密并返回Base64编码的结果
                String encryptedData = sm2.encryptBase64(data, KeyType.PublicKey);
                if (StringUtils.isEmpty(encryptedData)) {
                    throw new RuntimeException("SM2加密结果为空");
                }
                // 验证加密结果是否为有效的Base64编码
                try {
                    byte[] testDecode = Base64.getDecoder().decode(encryptedData);
                    if (testDecode.length == 0) {
                        throw new RuntimeException("加密结果无效:生成的Base64编码为空");
                    }
                } catch (IllegalArgumentException e) {
                    throw new RuntimeException("加密结果格式错误:生成的不是有效的Base64编码");
                }
                return encryptedData;
            } catch (RuntimeException e) {
                throw e;
            } catch (Exception e) {
                throw new RuntimeException("SM2加密失败:" + e.getMessage());
            }
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("SM2加密失败:" + e.getMessage());
        } catch (Exception e) {
            throw new RuntimeException("SM2加密失败:加密过程发生错误,请检查输入数据格式");
        }
    }
}

3.前端demo

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>加密解密演示</title>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<h2>RSA加解密演示</h2>
<div>
    <h3>RSA加密</h3>
    <textarea id="rsaPlaintext" rows="4" cols="50" placeholder="请输入要加密的内容"></textarea><br>
    <button onclick="rsaEncrypt()">加密</button><br>
    <textarea id="rsaCiphertext" rows="4" cols="50" readonly></textarea>
</div>

<div>
    <h3>RSA解密</h3>
    <textarea id="rsaEncryptedText" rows="4" cols="50" placeholder="请输入要解密的内容"></textarea><br>
    <button onclick="rsaDecrypt()">解密</button><br>
    <textarea id="rsaDecryptedText" rows="4" cols="50" readonly></textarea>
</div>

<h2>SM2加解密演示</h2>
<div>
    <h3>SM2加密</h3>
    <textarea id="sm2Plaintext" rows="4" cols="50" placeholder="请输入要加密的内容"></textarea><br>
    <button onclick="sm2Encrypt()">加密</button><br>
    <textarea id="sm2Ciphertext" rows="4" cols="50" readonly></textarea>
</div>

<div>
    <h3>SM2解密</h3>
    <textarea id="sm2EncryptedText" rows="4" cols="50" placeholder="请输入要解密的内容"></textarea><br>
    <button onclick="sm2Decrypt()">解密</button><br>
    <textarea id="sm2DecryptedText" rows="4" cols="50" readonly></textarea>
</div>

<script>
    // RSA加密
    async function rsaEncrypt() {
        try {
            const plaintext = document.getElementById('rsaPlaintext').value;
            const response = await axios.post('/api/crypto/rsa/encrypt', plaintext, {
                headers: {
                    'Content-Type': 'text/plain'
                },
                transformResponse: [(data) => {
                    // 确保返回的是原始数据,不要让axios自动解析
                    return data;
                }]
            });
            document.getElementById('rsaCiphertext').value = response.data;
        } catch (error) {
            alert('加密失败:' + (error.response?.data || error.message));
        }
    }

    // RSA解密
    async function rsaDecrypt() {
        try {
            const encryptedText = document.getElementById('rsaEncryptedText').value.trim();
            // 检查是否是有效的Base64
            if (!isValidBase64(encryptedText)) {
                throw new Error('输入的不是有效的Base64编码字符串');
            }

            const response = await axios.post('/api/crypto/rsa/decrypt', encryptedText, {
                headers: {
                    'Content-Type': 'text/plain'
                },
                transformResponse: [(data) => {
                    return data;
                }]
            });
            document.getElementById('rsaDecryptedText').value = response.data;
        } catch (error) {
            alert('解密失败:' + (error.response?.data || error.message));
        }
    }

    // 检查是否是有效的Base64字符串
    function isValidBase64(str) {
        try {
            return btoa(atob(str)) === str;
        } catch (err) {
            return false;
        }
    }

    // SM2加密
    async function sm2Encrypt() {
        try {
            const plaintext = document.getElementById('sm2Plaintext').value;
            console.log("plaintext====="+plaintext)
            // 使用 JSON 格式包装数据
            const response = await axios.post('/api/crypto/sm2/encrypt', {
                data: plaintext
            }
            ,{
                headers: {
                    'Content-Type': 'application/json'
                },
                transformResponse: [(data) => {
                    return data;
                }]
                });

            document.getElementById('sm2Ciphertext').value = response.data;
        } catch (error) {
            alert('加密失败:' + (error.response?.data || error.message));
        }
    }

    // SM2解密
    async function sm2Decrypt() {
        try {
            const response = await axios.post('/api/crypto/sm2/decrypt',
                {data:document.getElementById('sm2EncryptedText').value},
                {
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    transformResponse: [(data) => {
                        return data;
                    }]
                }

            );
            document.getElementById('sm2DecryptedText').value = response.data;
        } catch (error) {
            alert('解密失败:' + error);
        }
    }
</script>
</body>
</html>

运行结果如下图

 

 

posted @ 2025-05-19 12:04  Fyy发大财  阅读(471)  评论(0)    收藏  举报