From 13305bfd30392d26b39d906d8ada2525823039a6 Mon Sep 17 00:00:00 2001 From: anthonywj Date: Sun, 4 Feb 2024 09:43:36 +0800 Subject: [PATCH] =?UTF-8?q?SM2=E5=9B=BD=E5=AF=86=EF=BC=8C=E4=B8=AD?= =?UTF-8?q?=E5=8C=BB=E9=99=A2=E7=AD=89=E4=BF=9D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 7 ++ .../glxp/api/aspect/BodyRequestWrapper.java | 57 ++++++++++++ .../com/glxp/api/aspect/RequestHandler.java | 53 +++++++++++ .../com/glxp/api/aspect/RequestWrapper.java | 89 +++++++++++++++++++ .../api/controller/auth/LoginController.java | 13 +-- src/main/java/com/glxp/api/util/Sm2Util.java | 75 ++++++++++++++++ 6 files changed, 289 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/glxp/api/aspect/BodyRequestWrapper.java create mode 100644 src/main/java/com/glxp/api/aspect/RequestHandler.java create mode 100644 src/main/java/com/glxp/api/aspect/RequestWrapper.java create mode 100644 src/main/java/com/glxp/api/util/Sm2Util.java diff --git a/pom.xml b/pom.xml index c9b87a8db..8e9e97eea 100644 --- a/pom.xml +++ b/pom.xml @@ -400,6 +400,13 @@ redisson 3.6.0 + + org.bouncycastle + bcprov-jdk15on + 1.69 + + + diff --git a/src/main/java/com/glxp/api/aspect/BodyRequestWrapper.java b/src/main/java/com/glxp/api/aspect/BodyRequestWrapper.java new file mode 100644 index 000000000..def3c978f --- /dev/null +++ b/src/main/java/com/glxp/api/aspect/BodyRequestWrapper.java @@ -0,0 +1,57 @@ +package com.glxp.api.aspect; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * 用来重新封装request + */ +public class BodyRequestWrapper extends HttpServletRequestWrapper { + + /** + * 存放JSON数据主体 + */ + private String body; + + public BodyRequestWrapper(HttpServletRequest request, String context) { + super(request); + body = context; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes("UTF-8")); + return new ServletInputStream() { + @Override + public int read() throws IOException { + return byteArrayInputStream.read(); + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener listener) { + + } + }; + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(this.getInputStream())); + } +} diff --git a/src/main/java/com/glxp/api/aspect/RequestHandler.java b/src/main/java/com/glxp/api/aspect/RequestHandler.java new file mode 100644 index 000000000..51d2731f9 --- /dev/null +++ b/src/main/java/com/glxp/api/aspect/RequestHandler.java @@ -0,0 +1,53 @@ +package com.glxp.api.aspect; + +import com.alibaba.fastjson.JSON; +import com.glxp.api.util.Sm2Util; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * 请求加解密过滤器 + */ +@Slf4j +@Component +public class RequestHandler implements Filter { + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + /** + * 进行请求加密 + */ + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + // form-data不校验 + if ("application/x-www-form-urlencoded".equals(request.getContentType())) { + chain.doFilter(request, response); + return; + } + + // 拿到加密串 + String data = new RequestWrapper((HttpServletRequest) request).getBody(); + if (StringUtils.isEmpty(data)) { + chain.doFilter(request, response); + return; + } + + // 解析 + String body = Sm2Util.decrypt("00e36cfc8d61175584333e6160c645700f2a4659f5908c1bed5824423eab1a1626", JSON.parseObject(data).getString("data")); + log.info(body); + request = new BodyRequestWrapper((HttpServletRequest) request, body); + chain.doFilter(request, response); + } + + @Override + public void destroy() { + + } +} diff --git a/src/main/java/com/glxp/api/aspect/RequestWrapper.java b/src/main/java/com/glxp/api/aspect/RequestWrapper.java new file mode 100644 index 000000000..f4d1af958 --- /dev/null +++ b/src/main/java/com/glxp/api/aspect/RequestWrapper.java @@ -0,0 +1,89 @@ +package com.glxp.api.aspect; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.*; + + +/** + * 用来读取body + */ +public class RequestWrapper extends HttpServletRequestWrapper { + private final String body; + + public RequestWrapper(HttpServletRequest request) { + super(request); + StringBuilder stringBuilder = new StringBuilder(); + BufferedReader bufferedReader = null; + InputStream inputStream = null; + try { + inputStream = request.getInputStream(); + if (inputStream != null) { + bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + char[] charBuffer = new char[128]; + int bytesRead = -1; + while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { + stringBuilder.append(charBuffer, 0, bytesRead); + } + } else { + stringBuilder.append(""); + } + } catch (IOException ex) { + + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (bufferedReader != null) { + try { + bufferedReader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + body = stringBuilder.toString(); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes()); + ServletInputStream servletInputStream = new ServletInputStream() { + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + } + + @Override + public int read() throws IOException { + return byteArrayInputStream.read(); + } + }; + return servletInputStream; + + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(this.getInputStream())); + } + + public String getBody() { + return this.body; + } +} diff --git a/src/main/java/com/glxp/api/controller/auth/LoginController.java b/src/main/java/com/glxp/api/controller/auth/LoginController.java index 109331620..976bf96b0 100644 --- a/src/main/java/com/glxp/api/controller/auth/LoginController.java +++ b/src/main/java/com/glxp/api/controller/auth/LoginController.java @@ -29,10 +29,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; @@ -115,7 +112,7 @@ public class LoginController extends BaseController { Map claims = new HashMap<>(); claims.put("admin_id", authAdmin.getId()); - String token = JwtUtils.createToken(claims, 86400L); // 一天后过期 + String token = JwtUtils.createToken(claims, 1800L); // 一天后过期 Map map = new HashMap<>(); map.put("id", authAdmin.getId()); @@ -401,5 +398,11 @@ public class LoginController extends BaseController { return ResultVOUtils.success(webTitleResponse); } + @PostMapping("/sm2") + public BaseResponse sm2(@RequestParam String data) { + + return ResultVOUtils.success(Sm2Util.decrypt("00e36cfc8d61175584333e6160c645700f2a4659f5908c1bed5824423eab1a1626", data)); + } + } diff --git a/src/main/java/com/glxp/api/util/Sm2Util.java b/src/main/java/com/glxp/api/util/Sm2Util.java new file mode 100644 index 000000000..4ebdfde59 --- /dev/null +++ b/src/main/java/com/glxp/api/util/Sm2Util.java @@ -0,0 +1,75 @@ +package com.glxp.api.util; + +import cn.hutool.core.util.HexUtil; +import cn.hutool.crypto.BCUtil; +import cn.hutool.crypto.SmUtil; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.SM2; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.crypto.engines.SM2Engine; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; + +import java.util.HashMap; +import java.util.Map; + + +/** + * @author 猴哥 + */ +@Slf4j +public class Sm2Util { + /** + * 生成秘钥对 + * + * @return 公钥和私钥 + */ + public static Map generator() { + SM2 sm2 = SmUtil.sm2(); + String publicKey = HexUtil.encodeHexStr(((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false)).toUpperCase(); + String privateKey = HexUtil.encodeHexStr(BCUtil.encodeECPrivateKey(sm2.getPrivateKey())).toUpperCase(); + return new HashMap(2) {{ + put("publicKey", publicKey); + put("privateKey", privateKey); + }}; + } + + /** + * 加密 + * + * @param publicKey 公钥 + * @param data 明文 + * @return 密文 + */ + public static String encrypt(String publicKey, String data) { + return SmUtil.sm2(null, publicKey) + // 不写默认就是C1C3C2 + .setMode(SM2Engine.Mode.C1C3C2) + .encryptHex(data.getBytes(), KeyType.PublicKey) + // 加密后,密文前面会有04,需要去掉 + .substring(2); + } + + /** + * 解密 + * + * @param privateKey 私钥 + * @param data 密文 + * @return 明文 + */ + public static String decrypt(String privateKey, String data) { + // 确定前端不会加04,所以后端直接加(上面处理方式可能造成报错(Invalid point coordinates):原因前端加密后密文自带04开头) + data = "04" + data; + return SmUtil.sm2(privateKey, null) + // 不写默认就是C1C3C2 + .setMode(SM2Engine.Mode.C1C3C2) + .decryptStr(data, KeyType.PrivateKey); + } + + public static void main(String[] agrs) { + SM2 sm2 = SmUtil.sm2(); + String privateKey = HexUtil.encodeHexStr(BCUtil.encodeECPrivateKey(sm2.getPrivateKey())); + String publicKey = HexUtil.encodeHexStr(((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false)); + log.info(privateKey); + log.info(publicKey); + } +}