From f8ccb97d9c024cb39023902e771d53d7b753b2a0 Mon Sep 17 00:00:00 2001 From: landaiqing <3517283258@qq.com> Date: Sun, 28 Apr 2024 15:32:49 +0800 Subject: [PATCH] feat: add aes/rsa encrypt --- .../controller/EncryptController.java | 51 ++++ .../application/filter/EncryptApiFilter.java | 100 +++++++ .../wrapper/EncryptRequestWrapper.java | 257 ++++++++++++++++++ .../application/wrapper/ResponseWrapper.java | 114 ++++++++ .../pom.xml | 13 + .../auth/common/entity/ApiEncryptRes.java | 19 ++ .../auth/common/utils/AESUtils.java | 110 ++++++++ .../auth/common/utils/ApiSecurityUtils.java | 44 +++ .../schisandra/auth/common/utils/MD5Util.java | 61 +++++ .../auth/common/utils/RSAUtils.java | 171 ++++++++++++ 10 files changed, 940 insertions(+) create mode 100644 schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/controller/EncryptController.java create mode 100644 schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/filter/EncryptApiFilter.java create mode 100644 schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/wrapper/EncryptRequestWrapper.java create mode 100644 schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/wrapper/ResponseWrapper.java create mode 100644 schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/entity/ApiEncryptRes.java create mode 100644 schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/AESUtils.java create mode 100644 schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/ApiSecurityUtils.java create mode 100644 schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/MD5Util.java create mode 100644 schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/RSAUtils.java diff --git a/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/controller/EncryptController.java b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/controller/EncryptController.java new file mode 100644 index 0000000..45d724f --- /dev/null +++ b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/controller/EncryptController.java @@ -0,0 +1,51 @@ +package com.schisandra.auth.application.controller; + +import com.schisandra.auth.common.entity.Result; +import com.schisandra.auth.common.redis.RedisUtil; +import com.schisandra.auth.common.utils.MD5Util; +import com.schisandra.auth.common.utils.RSAUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.Map; + +/** + * @ClassName EncryptController + * @Description TODO + * @Author L + * @Date 2024/1/29 9:18 + */ +@RestController +@RequestMapping("/encrypt") +@Slf4j +public class EncryptController { + @Resource + private RedisUtil redisUtil; + private String authPublicKeyPrefix = "auth.publicKey"; + private String authPrivateKeyPrefix = "auth.privateKey"; + + @GetMapping("/getPublicKey") + public Result getPublicKey() throws Exception { + //获取当前登陆账号对应的token,这行代码就不贴了。 + String token="12345"; + String publicKey=""; + if (StringUtils.isNotBlank(token)) { + Map stringStringMap = RSAUtils.genKeyPair(); + publicKey = stringStringMap.get("publicKey"); + String privateKey = stringStringMap.get("privateKey"); + String md5Token = MD5Util.md5(token); + //这个地方的存放时间根据你的token存放时间走 + redisUtil.set(md5Token + "publicKey", publicKey); + redisUtil.set(md5Token + "privateKey", privateKey); + return Result.ok(publicKey); + } + return Result.ok(publicKey); + } + +} + diff --git a/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/filter/EncryptApiFilter.java b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/filter/EncryptApiFilter.java new file mode 100644 index 0000000..8bcbb28 --- /dev/null +++ b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/filter/EncryptApiFilter.java @@ -0,0 +1,100 @@ +package com.schisandra.auth.application.filter; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.servlet.ServletUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSON; +import com.schisandra.auth.application.wrapper.EncryptRequestWrapper; +import com.schisandra.auth.application.wrapper.ResponseWrapper; +import com.schisandra.auth.common.entity.ApiEncryptRes; +import com.schisandra.auth.common.utils.ApiSecurityUtils; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.web.servlet.filter.OrderedFilter; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +@Slf4j +@Component +public class EncryptApiFilter extends OncePerRequestFilter implements OrderedFilter { + public static final int DEFAULT_ORDER = Integer.MAX_VALUE; + + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String servletPath = request.getServletPath(); + + ResponseWrapper wrapper = new ResponseWrapper(response); + String requestContent = null; + + if (ServletUtil.isMultipart(request)) { + } else { + EncryptRequestWrapper encryptRequestWrapper = EncryptRequestWrapper.newOrGetMultiReadHttpServletRequest(request); + requestContent = encryptRequestWrapper.getBody(); + request = encryptRequestWrapper; + + } + log.debug("{}>>>{}", servletPath, requestContent); + + try { + filterChain.doFilter(request, wrapper); + } finally { + try { + process(request, wrapper, response); + } catch (Exception e) { + log.error("AesFilter error", e); + } + } + } + + @SneakyThrows + protected void process(HttpServletRequest request, ResponseWrapper responseWrapper, HttpServletResponse response) { + + String servletPath = request.getRequestURI(); + String isencrypt = request.getHeader("Isencrypt"); + ServletOutputStream out = response.getOutputStream(); + if (ResponseWrapper.isJsonContentType(responseWrapper.getContentType())) { + String responseContent = responseWrapper.getTextContent(); + if (StrUtil.isNotEmpty(responseContent)&&StringUtils.isNotBlank(isencrypt)) { + String JSPublicKey = ((EncryptRequestWrapper)request).getJSPublicKey(); + if (StringUtils.isNotBlank(JSPublicKey)) { + ApiEncryptRes apiEncryptRes = ApiSecurityUtils.encrypt(JSON.toJSONString(responseContent), JSPublicKey); + responseContent = JSONUtil.toJsonStr(apiEncryptRes); + } + log.info("{}<<<{}", servletPath, responseContent); + byte[] bytes = responseContent.getBytes(StandardCharsets.UTF_8); + response.setContentLength(bytes.length); + out.write(bytes); + out.flush(); + out.close(); + } else { + responseWrapper.getByteArrayOutputStream().writeTo(out); + out.flush(); + out.close(); + } + } else { + responseWrapper.getByteArrayOutputStream().writeTo(out); + out.flush(); + out.close(); + } + } + + @Override + public void afterPropertiesSet() throws ServletException { + super.afterPropertiesSet(); + } + + @Override + public int getOrder() { + return DEFAULT_ORDER; + } +} diff --git a/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/wrapper/EncryptRequestWrapper.java b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/wrapper/EncryptRequestWrapper.java new file mode 100644 index 0000000..13fc264 --- /dev/null +++ b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/wrapper/EncryptRequestWrapper.java @@ -0,0 +1,257 @@ +package com.schisandra.auth.application.wrapper; + +import cn.hutool.core.convert.Convert; +import com.alibaba.fastjson.JSONObject; +import com.schisandra.auth.common.redis.RedisUtil; +import com.schisandra.auth.common.utils.ApiSecurityUtils; +import com.schisandra.auth.common.utils.MD5Util; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.*; + + +import cn.hutool.core.io.FastByteArrayOutputStream; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.http.ContentType; + + +import lombok.SneakyThrows; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; +import org.springframework.web.multipart.support.StandardServletMultipartResolver; +import org.springframework.web.util.WebUtils; + + +import java.util.TreeMap; + +/** + * @ClassName requestWrapper + * @Description TODO + * @Author landaiqing + * @Date 2024/1/31 17:58 + */ + +/** + * 确保request可以多次读取 + */ + +@Slf4j +public class EncryptRequestWrapper extends HttpServletRequestWrapper { + + + + //2024-02-02由于在过滤器中无法直接通过Autowired获取Bean,因此需要通过spring上下文来获取IOC管理的实体类 +// @Autowired +// private RedisUtil redisUtil; + + protected FastByteArrayOutputStream cachedContent; + protected String JSPublicKey; + protected EncryptRequestWrapper(HttpServletRequest request ) { + + super(request); + RedisUtil redisUtils =getBean(RedisUtil.class, request); + this.copyBody(redisUtils); + + + } + + /** + * + * 此处主要是用于读取 RedisUtil + * @param clazz + * @param request + * @param + * @return + */ + public T getBean(Class clazz, HttpServletRequest request){ + WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext()); + return applicationContext.getBean(clazz); + } + + public static EncryptRequestWrapper newOrGetMultiReadHttpServletRequest(HttpServletRequest request) { + EncryptRequestWrapper multiReadHttpServletRequest = getMultiReadHttpServletRequest(request); + if (null != multiReadHttpServletRequest) { + return multiReadHttpServletRequest; + } else { + String contentType = request.getContentType(); + if (contentType != null && contentType.contains(ContentType.MULTIPART.getValue())) { + // 将转化后的 request 放入过滤链中 + request = new StandardServletMultipartResolver().resolveMultipart(request); + } + return new EncryptRequestWrapper(request); + } + } + + public static EncryptRequestWrapper getMultiReadHttpServletRequest(HttpServletRequest request) { + EncryptRequestWrapper nativeRequest = WebUtils.getNativeRequest(request, EncryptRequestWrapper.class); + if (null != nativeRequest) { + return nativeRequest; + } else if (request instanceof EncryptRequestWrapper) { + return (EncryptRequestWrapper) request; + } else { + return null; + } + } + //重新读取请求,因为request只可以被读取一次,需要重新设置为可多次读取 + @SneakyThrows + protected void copyBody(RedisUtil redisUtils) { + int length = this.getContentLength(); + if (length > 0) { + cachedContent = IoUtil.read(getRequest().getInputStream()); + if (StringUtils.isNotBlank(this.getHeader("Isencrypt"))&&StringUtils.isNotBlank(this.getHeader("X-Access-Token"))) { + String body = new String(cachedContent.toByteArray()); + log.info("------------------- body = " + body + "------------------------"); + if (StringUtils.isNotBlank(body)) { + JSONObject jsonBody = JSONObject.parseObject(body); + if(null != jsonBody){ + String dataEncrypt = jsonBody.getString("data"); + String aeskey = jsonBody.getString("aeskey"); + JSPublicKey = jsonBody.getString("frontPublicKey"); + String data; + JSONObject json = null; + log.info("------------------- dataEncrypt = " + dataEncrypt + "------------------------"); + log.info("------------------- aesKey = " + aeskey + "------------------------"); + String token = this.getHeader("X-Access-Token"); + String md5Token = MD5Util.md5(token); + String privateKey = Convert.toStr(redisUtils.get(md5Token + "privateKey")); + data = ApiSecurityUtils.decrypt(aeskey, dataEncrypt,privateKey); + // 如果数据不为空就编译 + if (StringUtils.isNotBlank(data)) { + //如果参数为空前端传回undefined + if ("undefined".equalsIgnoreCase(data)) { + json = new JSONObject(); + } else { + json = JSONObject.parseObject(data); + } + } + if (json != null) { + body = json.toJSONString(); + } + } + log.info("------------------- body = " + body + "------------------------"); + cachedContent.reset(); + cachedContent.write( body.getBytes(), 0, body.getBytes().length); + + } + } + } else { + cachedContent = new FastByteArrayOutputStream(); + } + } + + @Override + public int getContentLength() { + if (null != cachedContent) { + return cachedContent.size(); + } + return super.getContentLength(); + } + + public byte[] getByteArrayBody() { + return cachedContent.toByteArray(); + } + + public String getBody() { + String body = null; + if (cachedContent != null && cachedContent.size() > 0) { + body = new String(cachedContent.toByteArray()); + } + return body; + } + + public String getSortBody() { + return JSONObject.toJSONString(this.getBody(TreeMap.class)); + } + + public T getBody(Class type) { + return cn.hutool.json.JSONUtil.toBean(this.getBody(), type); + } + + //这个方法为获取前端给后端用于加密aeskey的rsa公钥 + public String getJSPublicKey(){ + return JSPublicKey; + } + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + if (this.getContentLength() < 1) { + return super.getInputStream(); + } + return new ResettableServletInputStream(new ByteArrayInputStream(cachedContent.toByteArray())); + } + + private class ResettableServletInputStream extends ServletInputStream { + + private final InputStream sourceStream; + + private boolean finished = false; + + + /** + * Create a DelegatingServletInputStream for the given source stream. + * + * @param sourceStream the source stream (never {@code null}) + */ + public ResettableServletInputStream(InputStream sourceStream) { + Assert.notNull(sourceStream, "Source InputStream must not be null"); + this.sourceStream = sourceStream; + } + + /** + * Return the underlying source stream (never {@code null}). + */ + public final InputStream getSourceStream() { + return this.sourceStream; + } + + + @Override + public int read() throws IOException { + int data = this.sourceStream.read(); + if (data == -1) { + this.finished = true; + } + return data; + } + + @Override + public int available() throws IOException { + return this.sourceStream.available(); + } + + @Override + public void close() throws IOException { + super.close(); + this.sourceStream.close(); + } + + @Override + public boolean isFinished() { + return finished; + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setReadListener(ReadListener readListener) { + + } + } + + + +} + diff --git a/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/wrapper/ResponseWrapper.java b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/wrapper/ResponseWrapper.java new file mode 100644 index 0000000..10f6eed --- /dev/null +++ b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-application/schisandra-cloud-storage-auth-application-controller/src/main/java/com/schisandra/auth/application/wrapper/ResponseWrapper.java @@ -0,0 +1,114 @@ +package com.schisandra.auth.application.wrapper; + +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; + +import javax.servlet.ServletOutputStream; +import javax.servlet.WriteListener; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +/** * 返回值输出代理类 + * @ClassName ResponseWrapper + * @Description TODO + * @Author L + * @Date 2024/1/31 18:58 + */ + +@Slf4j +public class ResponseWrapper extends HttpServletResponseWrapper { + ByteArrayOutputStream _stream = new ByteArrayOutputStream(); + PrintWriter _pw = new PrintWriter(_stream); + + public ResponseWrapper(HttpServletResponse resp) throws IOException { + super(resp); + } + + /** + * 覆盖getWriter()方法,将字符流缓冲到本地 + */ + @Override + public PrintWriter getWriter() throws IOException { + return _pw; + } + + /** + * 覆盖getOutputStream()方法,将字节流缓冲到本地 + */ + @Override + public ServletOutputStream getOutputStream() throws IOException { + return new ServletOutputStream() { + @Override + public boolean isReady() { + return false; + } + + @Override + public void setWriteListener(WriteListener writeListener) { + + } + + @Override + public void write(int b) throws IOException { + _stream.write(b); + } + }; + } + + /** + * 把缓冲区内容写入输出流后关闭 + * + * @author xxj + */ + public void flush() { + try { + _pw.flush(); + _pw.close(); + _stream.flush(); + _stream.close(); + } catch (IOException e) { + log.error("", e); + } + } + + /** + * 获取字节流 + * + * @return + */ + public ByteArrayOutputStream getByteArrayOutputStream() { + return _stream; + } + + /** + * 将换出区内容转为文本输出 + * + * @return + */ + public String getTextContent() { + flush(); + return _stream.toString(); + } + + public static boolean isTextContentType(String contentType) { + boolean flag = false; + if (StrUtil.isBlank(contentType)) { + return false; + } else { + flag = StrUtil.startWithIgnoreCase(contentType, "text"); + if (!flag) { + flag = isJsonContentType(contentType); + } + } + return flag; + } + + public static boolean isJsonContentType(String contentType) { + return !StrUtil.isBlank(contentType) + && (StrUtil.containsIgnoreCase(contentType, "application/problem+json") + || StrUtil.containsIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE)); + } +} diff --git a/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/pom.xml b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/pom.xml index 86846ad..13a1cf4 100644 --- a/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/pom.xml +++ b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/pom.xml @@ -100,5 +100,18 @@ sms4j-Email-core 3.2.1 + + + org.apache.directory.studio + org.apache.commons.codec + 1.8 + + + + cn.hutool + hutool-extra + 5.8.27 + + diff --git a/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/entity/ApiEncryptRes.java b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/entity/ApiEncryptRes.java new file mode 100644 index 0000000..9ba8785 --- /dev/null +++ b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/entity/ApiEncryptRes.java @@ -0,0 +1,19 @@ +package com.schisandra.auth.common.entity; + +import lombok.Data; + +/** + * @Classname ApiEncryptRes + * @BelongsProject: schisandra-cloud-storage + * @BelongsPackage: com.schisandra.auth.common.entity + * @Author: landaiqing + * @CreateTime: 2024-04-28 12:01 + * @Description: TODO + * @Version: 1.0 + */ +@Data +public class ApiEncryptRes { + private String aesKeyByRsa; + private String data; + private String frontPublicKey; +} diff --git a/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/AESUtils.java b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/AESUtils.java new file mode 100644 index 0000000..e5bd78c --- /dev/null +++ b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/AESUtils.java @@ -0,0 +1,110 @@ +package com.schisandra.auth.common.utils; + +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.util.Random; + +/** + * @ClassName AESUtils 一定要选择16位密钥长度,也就是KEY_LENGTH=16*8,36的话就需要修改环境的jar包。 + * @Description TODO + * @Author L + * @Date 2024/1/26 17:27 + */ +public class AESUtils { + /** + * 加密算法AES + */ + private static final String KEY_ALGORITHM = "AES"; + + /** + * key的长度,Wrong key size: must be equal to 128, 192 or 256 + * 传入时需要16、24、36 + */ + private static final int KEY_LENGTH = 16 * 8; + + /** + * 算法名称/加密模式/数据填充方式 + * 默认:AES/ECB/PKCS5Padding + */ + private static final String ALGORITHMS = "AES/ECB/PKCS5Padding"; + + /** + * 后端AES的key,由静态代码块赋值 + */ + public static String key; + + + static { + key = getKey(); + } + + /** + * 获取key + */ + public static String getKey() { + int length = KEY_LENGTH / 8; + StringBuilder uid = new StringBuilder(length); + //产生32位的强随机数 + Random rd = new SecureRandom(); + for (int i = 0; i < length; i++) { + //产生0-2的3位随机数 + switch (rd.nextInt(3)) { + case 0: + //0-9的随机数 + uid.append(rd.nextInt(10)); + break; + case 1: + //ASCII在65-90之间为大写,获取大写随机 + uid.append((char) (rd.nextInt(26) + 65)); + break; + case 2: + //ASCII在97-122之间为小写,获取小写随机 + uid.append((char) (rd.nextInt(26) + 97)); + break; + default: + break; + } + } + return uid.toString(); + } + + /** + * AES 加密 + * + * @param content 加密的字符串 + * @param encryptKey key值 + */ + public static String encrypt(String content, String encryptKey) throws Exception { + //设置Cipher对象 + Cipher cipher = Cipher.getInstance(ALGORITHMS); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), KEY_ALGORITHM)); + + //调用doFinal + // 转base64 + return Base64.encodeBase64String(cipher.doFinal(content.getBytes(StandardCharsets.UTF_8))); + + } + + /** + * AES 解密 + * + * @param encryptStr 解密的字符串 + * @param decryptKey 解密的key值 + */ + public static String decrypt(String encryptStr, String decryptKey) throws Exception { + //base64格式的key字符串转byte + byte[] decodeBase64 = Base64.decodeBase64(encryptStr); + + + //设置Cipher对象 + Cipher cipher = Cipher.getInstance(ALGORITHMS); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), KEY_ALGORITHM)); + //调用doFinal解密 + return new String(cipher.doFinal(decodeBase64)); + + } +} diff --git a/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/ApiSecurityUtils.java b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/ApiSecurityUtils.java new file mode 100644 index 0000000..230bc69 --- /dev/null +++ b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/ApiSecurityUtils.java @@ -0,0 +1,44 @@ +package com.schisandra.auth.common.utils; + +import com.schisandra.auth.common.entity.ApiEncryptRes; +import com.schisandra.auth.common.utils.AESUtils; +import com.schisandra.auth.common.utils.RSAUtils; +import org.apache.commons.codec.binary.Base64; +/** + * @ClassName ApiSecurityUtils + * @Description TODO + * @Author L + * @Date 2024/1/26 17:39 + */ +public class ApiSecurityUtils { + /** + * + * @param aesKeyByRsa 经过rsa加密的aeskey + * @param decryptStr 经过aes加密的数据 + * @return 解密后的数据 + */ + public static String decrypt(String aesKeyByRsa,String decryptStr,String privateKey) throws Exception { + byte[] bytes = RSAUtils.decryptByPrivateKey(Base64.decodeBase64(aesKeyByRsa), privateKey); + String aesKey = new String(bytes); + return AESUtils.decrypt(decryptStr, aesKey); + } + + /** + * + * @param encryptStr 要加密的数据 + * @param frontPublicKey 前端公钥 + * @return 加密后的数据 + */ + public static ApiEncryptRes encrypt(String encryptStr, String frontPublicKey) throws Exception { + String aesKey = AESUtils.getKey(); + String data = AESUtils.encrypt(encryptStr, aesKey); + ApiEncryptRes apiEncryptRes = new ApiEncryptRes(); + String aesKeyByRsa = Base64.encodeBase64String(RSAUtils.encryptByPublicKey(aesKey.getBytes(), frontPublicKey)); + apiEncryptRes.setAesKeyByRsa(aesKeyByRsa); + apiEncryptRes.setData(data); + return apiEncryptRes; + } + +} + + diff --git a/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/MD5Util.java b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/MD5Util.java new file mode 100644 index 0000000..9a21050 --- /dev/null +++ b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/MD5Util.java @@ -0,0 +1,61 @@ +package com.schisandra.auth.common.utils; + +import java.security.MessageDigest; + +public class MD5Util { + //十六进制下数字到字符的映射数组 + private final static String[] hexDigits = {"0", "1", "2", "3", "4", + "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; + + + /** + * 把inputString加密 + */ + public static String md5(String inputString) { + return encodeByMD5(inputString); + } + /** + * 对字符串进行MD5加密 + */ + private static String encodeByMD5(String originString) { + if (originString != null) { + try { + //创建具有指定算法名称的信息摘要 + MessageDigest md = MessageDigest.getInstance("MD5"); + //使用指定的字节数组对摘要进行最后更新,然后完成摘要计算 + byte[] results = md.digest(originString.getBytes("utf-8")); + //将得到的字节数组变成字符串返回 + String resultString = byteArrayToHexString(results); + return resultString.toUpperCase(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + return null; + } + /** + * 转换字节数组为十六进制字符串 + * + * @param + * @return 十六进制字符串 + */ + private static String byteArrayToHexString(byte[] b) { + StringBuffer resultSb = new StringBuffer(); + for (int i = 0; i < b.length; i++) { + resultSb.append(byteToHexString(b[i])); + } + return resultSb.toString(); + } + + private static String byteToHexString(byte b) { + int n = b; + if (n < 0) { + n += 256; + } + int d1 = n / 16; + int d2 = n % 16; + return hexDigits[d1] + hexDigits[d2]; + } + +} + diff --git a/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/RSAUtils.java b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/RSAUtils.java new file mode 100644 index 0000000..acdf56c --- /dev/null +++ b/schisandra-cloud-storage-auth/schisandra-cloud-storage-auth-common/src/main/java/com/schisandra/auth/common/utils/RSAUtils.java @@ -0,0 +1,171 @@ +package com.schisandra.auth.common.utils; + +import org.apache.commons.codec.binary.Base64; +import javax.crypto.Cipher; +import java.io.ByteArrayOutputStream; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @ClassName RSAUtils + * @Description TODO + * @Author L + * @Date 2024/1/26 17:28 + */ +public class RSAUtils { + + /** + * 加密算法RSA + */ + private static final String KEY_ALGORITHM = "RSA"; + + /** + * 算法名称/加密模式/数据填充方式 + * 默认:RSA/ECB/PKCS1Padding + */ + private static final String ALGORITHMS = "RSA/ECB/PKCS1Padding"; + + /** + * RSA最大加密明文大小 + */ + private static final int MAX_ENCRYPT_BLOCK = 245; + + /** + * RSA最大解密密文大小 + */ + private static final int MAX_DECRYPT_BLOCK = 256; + + /** + * RSA 位数 如果采用2048 上面最大加密和最大解密则须填写: 245 256 + */ + private static final int INITIALIZE_LENGTH = 2048; + + /** + * 后端RSA的密钥对(公钥和私钥)Map,由静态代码块赋值 + */ + private static final Map map = new LinkedHashMap<>(2); + + /** + * 生成密钥对(公钥和私钥) + */ + + public static Map genKeyPair() throws Exception { + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); + keyPairGen.initialize(INITIALIZE_LENGTH); + KeyPair keyPair = keyPairGen.generateKeyPair(); + // 获取公钥 + RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); + // 获取私钥 + RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); + // 得到公钥字符串 + String publicKeyString = Base64.encodeBase64String(publicKey.getEncoded()); + // 得到私钥字符串 + String privateKeyString = Base64.encodeBase64String((privateKey.getEncoded())); + map.put("publicKey",publicKeyString); + map.put("privateKey",privateKeyString); + return map; + } + public static String getPrivateKey(){ + return map.get("privateKey"); + } + public static String getPublicKey(){ + return map.get("publicKey"); + } + /** + * RSA私钥解密 + * @param data BASE64编码过的密文 + * @param privateKey 私钥(BASE64编码) + * @return utf-8编码的明文 + */ + public static byte[] decryptByPrivateKey(byte[] data, String privateKey) throws Exception { + //base64格式的key字符串转Key对象 + Key privateK = KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey))); + Cipher cipher = Cipher.getInstance(ALGORITHMS); + cipher.init(Cipher.DECRYPT_MODE, privateK); + + //分段进行解密操作 + return encryptAndDecryptOfSubsection(data, cipher, MAX_DECRYPT_BLOCK); + } + + /** + * RSA公钥加密 + * @param data BASE64编码过的密文 + * @param publicKey 公钥(BASE64编码) + * @return utf-8编码的明文 + */ + public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception { + //base64格式的key字符串转Key对象 + Key publicK = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey))); + Cipher cipher = Cipher.getInstance(ALGORITHMS); + cipher.init(Cipher.ENCRYPT_MODE, publicK); + //分段进行加密操作 + return encryptAndDecryptOfSubsection(data, cipher, MAX_ENCRYPT_BLOCK); + } + + /** + * RSA公钥解密 + * @param data BASE64编码过的密文 + * @param publicKey RSA公钥 + * @return utf-8编码的明文 + */ + public static byte[] pubKeyDec(byte[] data, String publicKey) throws Exception { + //base64格式的key字符串转Key对象 + Key privateK = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey))); + Cipher cipher = Cipher.getInstance(ALGORITHMS); + cipher.init(Cipher.DECRYPT_MODE, privateK); + + //分段进行解密操作 + return encryptAndDecryptOfSubsection(data, cipher, MAX_DECRYPT_BLOCK); + } + + + /** + * RSA私钥加密 + * @param data 待加密的明文 + * @param privateKey RSA私钥 + * @return 经BASE64编码后的密文 + */ + public static byte[] privKeyEnc(byte[] data, String privateKey) throws Exception { + + //base64格式的key字符串转Key对象 + Key publicK = KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey))); + Cipher cipher = Cipher.getInstance(ALGORITHMS); + cipher.init(Cipher.ENCRYPT_MODE, publicK); + + //分段进行加密操作 + return encryptAndDecryptOfSubsection(data, cipher, MAX_ENCRYPT_BLOCK); + } + + /** + * 分段进行加密、解密操作 + */ + private static byte[] encryptAndDecryptOfSubsection(byte[] data, Cipher cipher, int encryptBlock) throws Exception { + int inputLen = data.length; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int offSet = 0; + byte[] cache; + int i = 0; + // 对数据分段加密 + while (inputLen - offSet > 0) { + if (inputLen - offSet > encryptBlock) { + cache = cipher.doFinal(data, offSet, encryptBlock); + } else { + cache = cipher.doFinal(data, offSet, inputLen - offSet); + } + out.write(cache, 0, cache.length); + i++; + offSet = i * encryptBlock; + } + out.close(); + return out.toByteArray(); + } + +}