diff --git a/src/main/java/com/chinaweal/aiccs/common/util/SM4Utils.java b/src/main/java/com/chinaweal/aiccs/common/util/SM4Utils.java new file mode 100644 index 0000000..0b9bc5f --- /dev/null +++ b/src/main/java/com/chinaweal/aiccs/common/util/SM4Utils.java @@ -0,0 +1,107 @@ +package com.chinaweal.aiccs.common.util; + +import org.bouncycastle.crypto.engines.SM4Engine; +import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.encoders.Hex; + +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; + +/** + * + * @author lroyia + * @since 2026/1/4 15:33 + **/ +public class SM4Utils { + + + private static final int BLOCK_SIZE = 16; + + /** + * SM4 加密 + * + * @param plaintext 明文字符串 + * @param sm4Key SM4密钥 + * @return 十六进制格式的密文字符串 + */ + public static String encrypt(String plaintext, String sm4Key) { + try { + byte[] keyBytes = Hex.decode(sm4Key); + byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8); + + // 生成随机 IV + byte[] iv = new byte[BLOCK_SIZE]; + SecureRandom random = new SecureRandom(); + random.nextBytes(iv); + + // 执行加密 + SM4Engine engine = new SM4Engine(); + CBCBlockCipher blockCipher = new CBCBlockCipher(engine); + PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(blockCipher); + + KeyParameter keyParameter = new KeyParameter(keyBytes); + ParametersWithIV params = new ParametersWithIV(keyParameter, iv); + cipher.init(true, params); + + byte[] encrypted = new byte[cipher.getOutputSize(plaintextBytes.length)]; + int len = cipher.processBytes(plaintextBytes, 0, plaintextBytes.length, encrypted, 0); + len += cipher.doFinal(encrypted, len); + + // 将 IV 和密文合并返回 + byte[] result = new byte[iv.length + len]; + System.arraycopy(iv, 0, result, 0, iv.length); + System.arraycopy(encrypted, 0, result, iv.length, len); + + return Hex.toHexString(result); + } catch (Exception e) { + throw new RuntimeException("SM4 加密失败", e); + } + } + + /** + * SM4 解密 + * + * @param ciphertext 十六进制格式的密文字符串 + * @return 解密后的明文字符串 + */ + public static String decrypt(String ciphertext, String sm4Key) { + try { + byte[] keyBytes = Hex.decode(sm4Key); + byte[] ciphertextBytes = Hex.decode(ciphertext); + + if (ciphertextBytes.length < BLOCK_SIZE) { + throw new IllegalArgumentException("密文长度不足"); + } + + // 提取 IV 和密文 + byte[] iv = new byte[BLOCK_SIZE]; + byte[] encrypted = new byte[ciphertextBytes.length - BLOCK_SIZE]; + System.arraycopy(ciphertextBytes, 0, iv, 0, BLOCK_SIZE); + System.arraycopy(ciphertextBytes, BLOCK_SIZE, encrypted, 0, encrypted.length); + + // 执行解密 + SM4Engine engine = new SM4Engine(); + CBCBlockCipher blockCipher = new CBCBlockCipher(engine); + PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(blockCipher); + + KeyParameter keyParameter = new KeyParameter(keyBytes); + ParametersWithIV params = new ParametersWithIV(keyParameter, iv); + cipher.init(false, params); + + byte[] decrypted = new byte[cipher.getOutputSize(encrypted.length)]; + int len = cipher.processBytes(encrypted, 0, encrypted.length, decrypted, 0); + len += cipher.doFinal(decrypted, len); + + // 移除填充并返回明文 + byte[] result = new byte[len]; + System.arraycopy(decrypted, 0, result, 0, len); + + return new String(result, StandardCharsets.UTF_8); + } catch (Exception e) { + throw new RuntimeException("SM4 解密失败", e); + } + } +} diff --git a/src/main/java/com/chinaweal/aiccs/org/controller/OAuth2Controller.java b/src/main/java/com/chinaweal/aiccs/org/controller/OAuth2Controller.java index 841bc9a..47d89fd 100644 --- a/src/main/java/com/chinaweal/aiccs/org/controller/OAuth2Controller.java +++ b/src/main/java/com/chinaweal/aiccs/org/controller/OAuth2Controller.java @@ -1,8 +1,10 @@ package com.chinaweal.aiccs.org.controller; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.chinaweal.aiccs.common.base.controller.BaseController; import com.chinaweal.aiccs.common.util.OAuthTokenUtils; +import com.chinaweal.aiccs.common.util.SM4Utils; import com.chinaweal.aiccs.common.util.StringUtils; import com.chinaweal.aiccs.org.entity.OauthAccessToken; import com.chinaweal.aiccs.org.entity.OauthAuthorizationCode; @@ -13,9 +15,13 @@ import com.chinaweal.aiccs.org.service.IOauthAuthorizationCodeService; import com.chinaweal.aiccs.org.service.IOauthClientService; import com.chinaweal.aicorg.model.AICUser; import com.chinaweal.aicorg.services.OrgUM; +import com.chinaweal.youfool.framework.springboot.rest.RestResult; +import com.chinaweal.youfool.framework.springboot.rest.ResultCode; import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -35,6 +41,8 @@ import java.net.URLEncoder; @RequestMapping("oauth2") public class OAuth2Controller extends BaseController { + @Value("${oauth2.sm4.key}") + private String sm4Key; @Autowired private IOauthClientService oauthClientService; @Autowired @@ -504,4 +512,22 @@ public class OAuth2Controller extends BaseController { fullUserInfo.setPicture(null); return fullUserInfo; } + + /** + * 获取一个加密登录的登录用数据串 + * + * @param request 请求,用户获取当前登录信息 + * @return 加密串 + */ + @ApiOperation("获取一个加密登录的登录用数据串") + @GetMapping("sm4/encrypted/data") + public RestResult getSm4EncryptedData(HttpServletRequest request) { + AICUser loginUser = getLoginUser(request); + if (loginUser == null) { + return RestResult.error(ResultCode.USER_NOT_LOGGED_IN); + } + JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(loginUser)); + jsonObject.put("expired", System.currentTimeMillis() + 1000 * 60 * 5);// 增加有效期 + return RestResult.ok(SM4Utils.encrypt(jsonObject.toJSONString(), sm4Key)); + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3019a5b..a82dd3e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -151,3 +151,6 @@ punishment: account: 'xyfljgxt' password: '5d08zWhr0g18ncbvKp' deckey: 'GGXYLHJC' +oauth2: + sm4: + key: ${SM4_LOGIN_KEY:59eefc6ab7b5c9f06de12a3eefb06db5} \ No newline at end of file