feat: add aes/rsa encrypt
This commit is contained in:
@@ -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<String, String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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 <T>
|
||||
* @return
|
||||
*/
|
||||
public <T> T getBean(Class<T> 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> T getBody(Class<T> 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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@@ -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));
|
||||
}
|
||||
}
|
@@ -100,5 +100,18 @@
|
||||
<artifactId>sms4j-Email-core</artifactId>
|
||||
<version>3.2.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.directory.studio</groupId>
|
||||
<artifactId>org.apache.commons.codec</artifactId>
|
||||
<version>1.8</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-extra</artifactId>
|
||||
<version>5.8.27</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@@ -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;
|
||||
}
|
@@ -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));
|
||||
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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<String, String> map = new LinkedHashMap<>(2);
|
||||
|
||||
/**
|
||||
* 生成密钥对(公钥和私钥)
|
||||
*/
|
||||
|
||||
public static Map<String,String> 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();
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user