From a73cce8cc122d1070b7fdd54e3bb09e34acb9ed4 Mon Sep 17 00:00:00 2001 From: zhouhaibin Date: Thu, 19 Dec 2024 15:05:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8E=E7=AB=AF=E5=8A=A0=E5=AF=86=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E4=B8=BAsm2=E5=92=8Csm4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application.yml | 10 +- ruoyi-common/ruoyi-common-encrypt/pom.xml | 6 +- .../filter/DecryptRequestBodyWrapper.java | 14 ++- .../filter/EncryptResponseBodyWrapper.java | 10 +- .../common/encrypt/utils/EncryptUtils.java | 92 ++++++++++++++++--- .../dromara/common/encrypt/utils/SM4Util.java | 69 ++++++++++++++ .../domain/ModelPrompts.java | 3 + 7 files changed, 179 insertions(+), 25 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/SM4Util.java diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 58d2b6a..c820c68 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -160,7 +160,7 @@ mybatis-encryptor: # 是否开启加密 enable: false # 默认加密算法 - algorithm: BASE64 + algorithm: SM4 # 编码方式 BASE64/HEX。默认BASE64 encode: BASE64 # 安全秘钥 对称算法的秘钥 如:AES,SM4 @@ -177,12 +177,12 @@ api-decrypt: headerFlag: encrypt-key # 响应加密公钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换 # 对应前端解密私钥 MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE= - publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJnNwrj4hi/y3CCJu868ghCG5dUj8wZK++RNlTLcXoMmdZWEQ/u02RgD5LyLAXGjLOjbMtC+/J9qofpSGTKSx/MCAwEAAQ== -# publicKey: MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE33S12uP/J5rMPVYuHqbuCA7OWElC60egKvWya55YHc+56PGz5WH+/IOeLXpQ+NZ5mAqRgA/a1f0DdhHRmz87Dw== +# publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJnNwrj4hi/y3CCJu868ghCG5dUj8wZK++RNlTLcXoMmdZWEQ/u02RgD5LyLAXGjLOjbMtC+/J9qofpSGTKSx/MCAwEAAQ== + publicKey: 04f41b6910c3ad0edbd184cc0fdefc1b6cd2a8e6b3cad6ad01f412d86fdd9895079cea172828dd34376347fbb527eced9d71d0c1c68971c56e671cdb43d9920942 # 请求解密私钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换 # 对应前端加密公钥 MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ== - privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y= -# privateKey: MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgLzTaqeTg5aZSOuVLi+PVy7GauNpAlXdoARCtJOxMK5agCgYIKoEcz1UBgi2hRANCAASxU+uTFpJlHdv+Bhkl5EuYJTfhRkt4FxRULyN0Lf+xYecST0F1WWPmazvX3WR6mXTCIAgt5jQG8OjxyhCkKDEx +# privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y= + privateKey: 00bb2ea7a18607f85b0dc4939fcebba1cb0efd1f674f8e9a4e85e1db0d53a5988e springdoc: api-docs: diff --git a/ruoyi-common/ruoyi-common-encrypt/pom.xml b/ruoyi-common/ruoyi-common-encrypt/pom.xml index ed4910e..c7d55c9 100644 --- a/ruoyi-common/ruoyi-common-encrypt/pom.xml +++ b/ruoyi-common/ruoyi-common-encrypt/pom.xml @@ -26,7 +26,11 @@ org.bouncycastle bcprov-jdk15to18 - + + com.antherd + sm-crypto + 0.3.2.1 + cn.hutool hutool-crypto diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java index 98f4bc7..293f50b 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java @@ -26,16 +26,20 @@ public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper { public DecryptRequestBodyWrapper(HttpServletRequest request, String privateKey, String headerFlag) throws IOException { super(request); - // 获取 AES 密码 采用 RSA 加密 + // 获取 sm4 密码 采用 RSA 加密 String headerRsa = request.getHeader(headerFlag); - String decryptAes = EncryptUtils.decryptByRsa(headerRsa, privateKey); - // 解密 AES 密码 +// String decryptAes = EncryptUtils.decryptByRsa(headerRsa, privateKey); + String decryptAes = EncryptUtils.decryptBySm2(headerRsa, privateKey); + + // 解密 sm4 密码 String aesPassword = EncryptUtils.decryptByBase64(decryptAes); request.setCharacterEncoding(Constants.UTF8); byte[] readBytes = IoUtil.readBytes(request.getInputStream(), false); String requestBody = new String(readBytes, StandardCharsets.UTF_8); - // 解密 body 采用 AES 加密 - String decryptBody = EncryptUtils.decryptByAes(requestBody, aesPassword); + // 解密 body 采用 sm4 加密 +// String decryptBody = EncryptUtils.decryptByAes(requestBody, aesPassword); + String decryptBody = EncryptUtils.decryptBySm4(requestBody, aesPassword); + body = decryptBody.getBytes(StandardCharsets.UTF_8); } diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java index c0af232..e81e71e 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java @@ -6,6 +6,7 @@ import jakarta.servlet.WriteListener; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponseWrapper; import org.dromara.common.encrypt.utils.EncryptUtils; +import org.dromara.common.encrypt.utils.SM4Util; import java.io.*; import java.nio.charset.StandardCharsets; @@ -69,11 +70,14 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper { */ public String getEncryptContent(HttpServletResponse servletResponse, String publicKey, String headerFlag) throws IOException { // 生成秘钥 - String aesPassword = RandomUtil.randomString(32); +// String aesPassword = RandomUtil.randomString(16); + String aesPassword = SM4Util.generateKey(); + // 秘钥使用 Base64 编码 String encryptAes = EncryptUtils.encryptByBase64(aesPassword); // Rsa 公钥加密 Base64 编码 - String encryptPassword = EncryptUtils.encryptByRsa(encryptAes, publicKey); +// String encryptPassword = EncryptUtils.encryptByRsa(encryptAes, publicKey); + String encryptPassword = EncryptUtils.encryptBySm2Hex(encryptAes, publicKey); // 设置响应头 servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag); @@ -85,7 +89,7 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper { // 获取原始内容 String originalBody = this.getContent(); // 对内容进行加密 - return EncryptUtils.encryptByAes(originalBody, aesPassword); + return EncryptUtils.encryptBySm4(originalBody, aesPassword); } @Override diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java index 8e34843..0e6d355 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java @@ -2,13 +2,19 @@ package org.dromara.common.encrypt.utils; import cn.hutool.core.codec.Base64; import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.HexUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.BCUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.SmUtil; import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.RSA; import cn.hutool.crypto.asymmetric.SM2; +import com.antherd.smcrypto.sm4.Sm4; +import org.bouncycastle.crypto.engines.SM2Engine; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; +import javax.script.ScriptException; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -112,7 +118,7 @@ public class EncryptUtils { * @param password 秘钥字符串 * @return 加密后字符串, 采用Base64编码 */ - public static String encryptBySm4(String data, String password) { + public static String encryptBySm4Bak(String data, String password) { if (StrUtil.isBlank(password)) { throw new IllegalArgumentException("SM4需要传入秘钥信息"); } @@ -150,7 +156,7 @@ public class EncryptUtils { * @param password 秘钥字符串 * @return 解密后字符串 */ - public static String decryptBySm4(String data, String password) { + public static String decryptBySm4Bak(String data, String password) { if (StrUtil.isBlank(password)) { throw new IllegalArgumentException("SM4需要传入秘钥信息"); } @@ -161,7 +167,47 @@ public class EncryptUtils { } return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8); } - + public static String decryptBySm4(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("SM4需要传入秘钥信息"); + } + // sm4算法的秘钥要求是16位长度 + int sm4PasswordLength = 32; + if (sm4PasswordLength != password.length()) { + throw new IllegalArgumentException("SM4秘钥长度要求为16位"); + } + String decrypt = ""; + try { + String replace = data.replace("\"", ""); + decrypt=SM4Util.decrypt(replace, password); + } catch (Exception e) { + throw new RuntimeException(e); + } + return decrypt; + } + /** sm4加密 + * + * @param data 待加密数据 + * @param password 秘钥字符串 + * @return 加密后字符串, 采用Base64编码 + */ + public static String encryptBySm4(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("SM4需要传入秘钥信息"); + } + // sm4算法的秘钥要求是16位长度 + int sm4PasswordLength = 32; + if (sm4PasswordLength != password.length()) { + throw new IllegalArgumentException("SM4秘钥长度要求为16位"); + } + String encrypt = null; + try { + encrypt = SM4Util.encrypt(data, password); + } catch (Exception e) { + throw new RuntimeException(e); + } + return encrypt; + } /** * 产生sm2加解密需要的公钥和私钥 * @@ -169,12 +215,32 @@ public class EncryptUtils { */ public static Map generateSm2Key() { Map keyMap = new HashMap<>(2); - SM2 sm2 = SmUtil.sm2(); - keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64()); - keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64()); +// SM2 sm2 = SmUtil.sm2(); +// keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64()); +// keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64()); + //创建sm2 对象 + SM2 sm = SmUtil.sm2(); + + // sm2的加解密时有两种方式即 C1C2C3、 C1C3C2, + sm.setMode(SM2Engine.Mode.C1C3C2); + + // 生成私钥 + String privateKey = HexUtil.encodeHexStr(sm.getPrivateKey().getEncoded()); + keyMap.put("私钥: {}", privateKey); + + // 生成公钥 + String publicKey = HexUtil.encodeHexStr(sm.getPublicKey().getEncoded()); + keyMap.put("公钥: {}", publicKey); + + // 生成私钥 D,以D值做为js端的解密私钥 + String privateKeyD = HexUtil.encodeHexStr(BCUtil.encodeECPrivateKey(sm.getPrivateKey())); + keyMap.put("私钥D: {}", privateKeyD); + + // 生成公钥 Q,以Q值做为js端的加密公钥 + String publicKeyQ = HexUtil.encodeHexStr(((BCECPublicKey) sm.getPublicKey()).getQ().getEncoded(false)); + keyMap.put("公钥Q: {}", publicKeyQ); return keyMap; } - /** * sm2公钥加密 * @@ -186,7 +252,7 @@ public class EncryptUtils { if (StrUtil.isBlank(publicKey)) { throw new IllegalArgumentException("SM2需要传入公钥进行加密"); } - SM2 sm2 = SmUtil.sm2(null, publicKey); + SM2 sm2 = SmUtil.sm2(null, HexUtil.decodeHex(publicKey)); return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey); } @@ -202,7 +268,10 @@ public class EncryptUtils { throw new IllegalArgumentException("SM2需要传入公钥进行加密"); } SM2 sm2 = SmUtil.sm2(null, publicKey); - return sm2.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey); +// return sm2.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey); + byte[] encrypt = sm2.encrypt(data, KeyType.PublicKey); + String encryStr = HexUtil.encodeHexStr(encrypt); + return encryStr; } /** @@ -216,8 +285,8 @@ public class EncryptUtils { if (StrUtil.isBlank(privateKey)) { throw new IllegalArgumentException("SM2需要传入私钥进行解密"); } - SM2 sm2 = SmUtil.sm2(privateKey, null); - return sm2.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8); + SM2 sm2 = SmUtil.sm2(HexUtil.decodeHex(privateKey), null); + return sm2.decryptStr("04"+data, KeyType.PrivateKey, StandardCharsets.UTF_8); } /** @@ -309,3 +378,4 @@ public class EncryptUtils { } } + diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/SM4Util.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/SM4Util.java new file mode 100644 index 0000000..e98c2b6 --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/SM4Util.java @@ -0,0 +1,69 @@ +package org.dromara.common.encrypt.utils; + +import org.bouncycastle.crypto.engines.SM4Engine; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.encoders.Hex; + +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; + +public class SM4Util { + + private static final int BLOCK_SIZE = 16; // 块大小 + + // 随机生成 SM4 密钥 + public static String generateKey() { + byte[] key = new byte[BLOCK_SIZE]; + new SecureRandom().nextBytes(key); + return Hex.toHexString(key); + } + + // 加密 + public static String encrypt(String plainText, String hexKey) throws Exception { + byte[] input = plainText.getBytes(StandardCharsets.UTF_8); + byte[] keyBytes = Hex.decode(hexKey); + // 计算填充长度 + int paddingLength = BLOCK_SIZE - (input.length % BLOCK_SIZE); + byte[] paddedInput = new byte[input.length + paddingLength]; + + // 复制原始数据 + System.arraycopy(input, 0, paddedInput, 0, input.length); + // PKCS7填充 + for (int i = input.length; i < paddedInput.length; i++) { + paddedInput[i] = (byte) paddingLength; + } + + SM4Engine sm4 = new SM4Engine(); + sm4.init(true, new KeyParameter(keyBytes)); + + byte[] encrypted = new byte[paddedInput.length]; + for (int i = 0; i < paddedInput.length; i += BLOCK_SIZE) { + sm4.processBlock(paddedInput, i, encrypted, i); + } +// SM4Engine sm4 = new SM4Engine(); +// sm4.init(true, new KeyParameter(keyBytes)); // 加密模式 +// +// byte[] encrypted = new byte[input.length]; +// for (int i = 0; i < input.length; i += BLOCK_SIZE) { +// sm4.processBlock(input, i, encrypted, i); +// } + + return Hex.toHexString(encrypted); + } + + // 解密 + public static String decrypt(String cipherText, String hexKey) throws Exception { + byte[] keyBytes = Hex.decode(hexKey); + byte[] encrypted = Hex.decode(cipherText); + + SM4Engine sm4 = new SM4Engine(); + sm4.init(false, new KeyParameter(keyBytes)); // 解密模式 + + byte[] decrypted = new byte[encrypted.length]; + for (int i = 0; i < encrypted.length; i += BLOCK_SIZE) { + sm4.processBlock(encrypted, i, decrypted, i); + } + + return new String(decrypted, StandardCharsets.UTF_8).trim(); + } +} diff --git a/zaojiaManagement/zaojia-productManagement/src/main/java/org/dromara/productManagement/domain/ModelPrompts.java b/zaojiaManagement/zaojia-productManagement/src/main/java/org/dromara/productManagement/domain/ModelPrompts.java index e1ce3d9..9d92725 100644 --- a/zaojiaManagement/zaojia-productManagement/src/main/java/org/dromara/productManagement/domain/ModelPrompts.java +++ b/zaojiaManagement/zaojia-productManagement/src/main/java/org/dromara/productManagement/domain/ModelPrompts.java @@ -1,5 +1,7 @@ package org.dromara.productManagement.domain; +import org.dromara.common.encrypt.annotation.EncryptField; +import org.dromara.common.encrypt.enumd.AlgorithmType; import org.dromara.common.tenant.core.TenantEntity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; @@ -35,6 +37,7 @@ public class ModelPrompts extends TenantEntity { /** * 模型任务名称 */ + @EncryptField(algorithm = AlgorithmType.SM4, password = "c7746c89a780c27eda14a2f9b3097c8f") private String taskName; /**