【java】前端RSA加密后端解密

news/2024/9/28 11:12:10 标签: java, 前端

目录

          • 1. 说明
          • 2. 前端示例
          • 3. 后端示例
            • 3.1 pom依赖
            • 3.2 后端结构图
            • 3.3 DecryptHttpInputMessage
            • 3.4 ApiCryptoProperties
            • 3.5 TestController
            • 3.6 ApiCryptoUtil
            • 3.7 ApiDecryptParamResolver
            • 3.8 ApiDecryptRequestBodyAdvice
            • 3.9 ApiDecryptRsa
            • 3.10 ApiCryptoProperties
            • 3.11 KeyPair
            • 3.12 Pair
            • 3.13 ClassUtil
            • 3.14 Func
            • 3.15 RsaUtil
            • 3.16 SpringBootLearningApplication启动类
            • 3.17 配置文件
          • 4. 调用截图

1. 说明
  • 1.RSA是非对称加密。
  • 2.前端采用公钥加密,后端采用私钥解密。
  • 3.此示例是前端加密,后端解密,后端返回的数据未加密。如果后端相应数据也要加密,可以另写注解,采用对称加密。
  • 4.公钥私钥的base64格式可以由RsaUtil工具类生成,参考其中main方法。
  • 5.在需要加密的接口上添加自定义注解@ApiDecryptRsa即可解密。
  • 5.ApiDecryptParamResolver是解析requestParam参数的,这里没写全,需要额外写注解。
2. 前端示例
  • 1.rsa依赖包
npm install jsencrypt
  • 2.页面代码
<template>
  <el-button type="primary" icon="el-icon-plus" @click="handleTest">测试</el-button>
</template>

<script>
import { save } from "@/api/test";
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min';
export default {
  name: '测试RSA加密',
  methods: {
    handleTest(){
      const crypt = new JSEncrypt()
      // 公钥,由后端接口返回,这里写死作为测试
      const publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJlmkqsmA28rt/bsBJ2hDEdvbIlEvnJ2fg0gm1++rXxXNEBvGyCnwnDxvztjsHtbootAdeB1D73DVfCKzCj5Le/Wv21llvriG1bAeM3zSEywPKQzW/5zhC3BODdx8vuFe3r1KxunqdXHm/67tnC/VMS85XGtCBkcfAXCJZyNE5rQIDAQAB';
      crypt.setPublicKey(publicKey)
      const data = {
        "content": "测试",
      }
      const encrypted = crypt.encrypt(JSON.stringify(data))
      console.log(encrypted)
      // 调用接口
      save(encrypted).then(res=>{
        console.log(res.data)
      })
    },
  }
}
</script>
  • 3.api请求
import request from "@/axios";

export const save = row => {
  return request({
    url: '/xapi/test/save',
    headers: {
      'Content-Type': 'text/plain'
    },
    method: 'post',
    data: row,
  });
};
3. 后端示例
3.1 pom依赖
  • 1.pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>my-springboot</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.6</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.40</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.13.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
    </dependencies>
</project>
3.2 后端结构图
  • 2.后端结构图
    在这里插入图片描述
3.3 DecryptHttpInputMessage
  • 3.DecryptHttpInputMessage
package com.learning.bean;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;

import java.io.InputStream;

/**
 * <p>解密信息输入流</p>
 *
 */
@Getter
@RequiredArgsConstructor
public class DecryptHttpInputMessage implements HttpInputMessage {
	private final InputStream body;
	private final HttpHeaders headers;
}

3.4 ApiCryptoProperties
  • 4.ApiCryptoProperties
package com.learning.config;

import com.learning.crypto.advice.ApiDecryptParamResolver;
import com.learning.crypto.props.ApiCryptoProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
 * api 签名自动配置
 *
 */
@AutoConfiguration
@RequiredArgsConstructor
@EnableConfigurationProperties(ApiCryptoProperties.class)
@ConditionalOnProperty(value = ApiCryptoProperties.PREFIX + ".enabled", havingValue = "true", matchIfMissing = true)
public class ApiCryptoConfiguration implements WebMvcConfigurer {
	private final ApiCryptoProperties apiCryptoProperties;

	@Override
	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
		argumentResolvers.add(new ApiDecryptParamResolver(apiCryptoProperties));
	}

}

3.5 TestController
  • 5.TestController
package com.learning.controller;

import com.learning.crypto.annotation.ApiDecryptRsa;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {
    @PostMapping(value = "/save", produces = "text/plain")
    @ApiDecryptRsa
    public String save(@RequestBody String data){
        return data;
    }
}

3.6 ApiCryptoUtil
  • 6.ApiCryptoUtil
package com.learning.crypto.advice;

import com.learning.crypto.props.ApiCryptoProperties;

import java.util.Objects;
import com.learning.util.RsaUtil;
/**
 * <p>辅助工具类</p>
 *
 */
public class ApiCryptoUtil {

	/**
	 * 选择加密方式并进行解密
	 *
	 * @param bodyData byte array
	 * @return 解密结果
	 */
	public static byte[] decryptData(ApiCryptoProperties properties, byte[] bodyData) {
		String privateKey = Objects.requireNonNull(properties.getRsaPrivateKey());
		return RsaUtil.decryptFromBase64(privateKey, bodyData);
	}

}
3.7 ApiDecryptParamResolver
  • 7.ApiDecryptParamResolver
package com.learning.crypto.advice;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.learning.crypto.annotation.ApiDecryptRsa;
import com.learning.crypto.props.ApiCryptoProperties;
import com.learning.util.Func;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import java.lang.reflect.Parameter;
import java.nio.charset.StandardCharsets;

/**
 * param 参数 解析
 *
 */
@RequiredArgsConstructor
public class ApiDecryptParamResolver implements HandlerMethodArgumentResolver {
	private final ApiCryptoProperties properties;

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return AnnotatedElementUtils.hasAnnotation(parameter.getParameter(), ApiDecryptRsa.class);
	}

	@Nullable
	@Override
	public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer mavContainer,
								  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
		Parameter parameter = methodParameter.getParameter();
		String text = webRequest.getParameter(properties.getParamName());
		if (Func.isEmpty(text)) {
			return null;
		}
		byte[] textBytes = text.getBytes(StandardCharsets.UTF_8);
		byte[] decryptData = ApiCryptoUtil.decryptData(properties, textBytes);
		ObjectMapper mapper = new ObjectMapper();
		try{
			return mapper.readValue(decryptData, parameter.getType());
		}catch (Exception e){
			e.printStackTrace();
			return null;
		}
	}
}

3.8 ApiDecryptRequestBodyAdvice
  • 8.ApiDecryptRequestBodyAdvice
package com.learning.crypto.advice;

import com.learning.crypto.annotation.ApiDecryptRsa;
import com.learning.crypto.props.ApiCryptoProperties;
import com.learning.util.ClassUtil;
import com.learning.bean.DecryptHttpInputMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.NonNull;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;

/**
 * 请求数据的加密信息解密处理<br>
 * 本类只对控制器参数中含有<strong>{@link org.springframework.web.bind.annotation.RequestBody}</strong>
 * 以及package为<strong><code>ccom.xiaoi.xics.core.crypto.api.signature.annotation.decrypt</code></strong>下的注解有效
 */
@Slf4j
@Order(1)
@AutoConfiguration
@ControllerAdvice
@RequiredArgsConstructor
@ConditionalOnProperty(value = ApiCryptoProperties.PREFIX + ".enabled", havingValue = "true", matchIfMissing = true)
public class ApiDecryptRequestBodyAdvice implements RequestBodyAdvice {
	private final ApiCryptoProperties properties;

	@Override
	public boolean supports(MethodParameter methodParameter, @NonNull Type targetType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
		return ClassUtil.isAnnotated(methodParameter.getMethod(), ApiDecryptRsa.class);
	}

	@Override
	public Object handleEmptyBody(Object body, @NonNull HttpInputMessage inputMessage, @NonNull MethodParameter parameter, @NonNull Type targetType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
		return body;
	}

	@NonNull
	@Override
	public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, @NonNull MethodParameter parameter, @NonNull Type targetType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
		// 判断 body 是否为空
		InputStream messageBody = inputMessage.getBody();
		if (messageBody.available() <= 0) {
			return inputMessage;
		}
		byte[] decryptedBody = null;
		byte[] bodyByteArray = StreamUtils.copyToByteArray(messageBody);
		decryptedBody = ApiCryptoUtil.decryptData(properties, bodyByteArray);
		if (decryptedBody == null) {
			throw new RuntimeException("Decryption error, " +
				"please check if the selected source data is encrypted correctly." +
				" (解密错误,请检查选择的源数据的加密方式是否正确。)");
		}
		InputStream inputStream = new ByteArrayInputStream(decryptedBody);
		return new DecryptHttpInputMessage(inputStream, inputMessage.getHeaders());
	}

	@NonNull
	@Override
	public Object afterBodyRead(@NonNull Object body, @NonNull HttpInputMessage inputMessage, @NonNull MethodParameter parameter, @NonNull Type targetType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
		return body;
	}

}

3.9 ApiDecryptRsa
  • 9.ApiDecryptRsa
package com.learning.crypto.annotation;

import java.lang.annotation.*;

/**
 * rsa 解密
 */
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ApiDecryptRsa {
}

3.10 ApiCryptoProperties
  • 10.ApiCryptoProperties
package com.learning.crypto.props;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * api 签名配置类
 */
@Getter
@Setter
@ConfigurationProperties(ApiCryptoProperties.PREFIX)
public class ApiCryptoProperties {
	/**
	 * 前缀
	 */
	public static final String PREFIX = "api.crypto";

	/**
	 * 是否开启 api 签名
	 */
	private Boolean enabled = Boolean.TRUE;

	/**
	 * url的参数签名,传递的参数名。例如:/user?data=签名后的数据
	 */
	private String paramName = "data";

	/**
	 * rsa 私钥
	 */
	private String rsaPrivateKey;

}

3.11 KeyPair
  • 11.KeyPair
package com.learning.crypto.tuple;

import com.learning.util.RsaUtil;
import lombok.RequiredArgsConstructor;

import java.security.PrivateKey;
import java.security.PublicKey;

/**
 * rsa 的 key pair 封装
 *
 */
@RequiredArgsConstructor
public class KeyPair {
	private final java.security.KeyPair keyPair;

	public PublicKey getPublic() {
		return keyPair.getPublic();
	}

	public PrivateKey getPrivate() {
		return keyPair.getPrivate();
	}

	public byte[] getPublicBytes() {
		return this.getPublic().getEncoded();
	}

	public byte[] getPrivateBytes() {
		return this.getPrivate().getEncoded();
	}

	public String getPublicBase64() {
		return RsaUtil.getKeyString(this.getPublic());
	}

	public String getPrivateBase64() {
		return RsaUtil.getKeyString(this.getPrivate());
	}

	@Override
	public String toString() {
		return "PublicKey=" + this.getPublicBase64() + '\n' + "PrivateKey=" + this.getPrivateBase64();
	}
}

3.12 Pair
  • 12.Pair
package com.learning.crypto.tuple;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;

/**
 * tuple Pair
 **/
@Getter
@ToString
@EqualsAndHashCode
public class Pair<L, R> {
	private static final Pair<Object, Object> EMPTY = new Pair<>(null, null);

	private final L left;
	private final R right;

	/**
	 * Returns an empty pair.
	 */
	@SuppressWarnings("unchecked")
	public static <L, R> Pair<L, R> empty() {
		return (Pair<L, R>) EMPTY;
	}

	/**
	 * Constructs a pair with its left value being {@code left}, or returns an empty pair if
	 * {@code left} is null.
	 *
	 * @return the constructed pair or an empty pair if {@code left} is null.
	 */
	public static <L, R> Pair<L, R> createLeft(L left) {
		if (left == null) {
			return empty();
		} else {
			return new Pair<>(left, null);
		}
	}

	/**
	 * Constructs a pair with its right value being {@code right}, or returns an empty pair if
	 * {@code right} is null.
	 *
	 * @return the constructed pair or an empty pair if {@code right} is null.
	 */
	public static <L, R> Pair<L, R> createRight(R right) {
		if (right == null) {
			return empty();
		} else {
			return new Pair<>(null, right);
		}
	}

	public static <L, R> Pair<L, R> create(L left, R right) {
		if (right == null && left == null) {
			return empty();
		} else {
			return new Pair<>(left, right);
		}
	}

	private Pair(L left, R right) {
		this.left = left;
		this.right = right;
	}

}

3.13 ClassUtil
  • 13.ClassUtil
package com.learning.util;

import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.web.method.HandlerMethod;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * 类操作工具
 */
public class ClassUtil extends org.springframework.util.ClassUtils {

	private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();

	/**
	 * 获取方法参数信息
	 *
	 * @param constructor    构造器
	 * @param parameterIndex 参数序号
	 * @return {MethodParameter}
	 */
	public static MethodParameter getMethodParameter(Constructor<?> constructor, int parameterIndex) {
		MethodParameter methodParameter = new SynthesizingMethodParameter(constructor, parameterIndex);
		methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
		return methodParameter;
	}

	/**
	 * 获取方法参数信息
	 *
	 * @param method         方法
	 * @param parameterIndex 参数序号
	 * @return {MethodParameter}
	 */
	public static MethodParameter getMethodParameter(Method method, int parameterIndex) {
		MethodParameter methodParameter = new SynthesizingMethodParameter(method, parameterIndex);
		methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
		return methodParameter;
	}

	/**
	 * 获取Annotation
	 *
	 * @param method         Method
	 * @param annotationType 注解类
	 * @param <A>            泛型标记
	 * @return {Annotation}
	 */
	public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {
		Class<?> targetClass = method.getDeclaringClass();
		// The method may be on an interface, but we need attributes from the target class.
		// If the target class is null, the method will be unchanged.
		Method specificMethod = ClassUtil.getMostSpecificMethod(method, targetClass);
		// If we are dealing with method with generic parameters, find the original method.
		specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
		// 先找方法,再找方法上的类
		A annotation = AnnotatedElementUtils.findMergedAnnotation(specificMethod, annotationType);
		;
		if (null != annotation) {
			return annotation;
		}
		// 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类
		return AnnotatedElementUtils.findMergedAnnotation(specificMethod.getDeclaringClass(), annotationType);
	}

	/**
	 * 获取Annotation
	 *
	 * @param handlerMethod  HandlerMethod
	 * @param annotationType 注解类
	 * @param <A>            泛型标记
	 * @return {Annotation}
	 */
	public static <A extends Annotation> A getAnnotation(HandlerMethod handlerMethod, Class<A> annotationType) {
		// 先找方法,再找方法上的类
		A annotation = handlerMethod.getMethodAnnotation(annotationType);
		if (null != annotation) {
			return annotation;
		}
		// 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类
		Class<?> beanType = handlerMethod.getBeanType();
		return AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType);
	}


	/**
	 * 判断是否有注解 Annotation
	 *
	 * @param method         Method
	 * @param annotationType 注解类
	 * @param <A>            泛型标记
	 * @return {boolean}
	 */
	public static <A extends Annotation> boolean isAnnotated(Method method, Class<A> annotationType) {
		// 先找方法,再找方法上的类
		boolean isMethodAnnotated = AnnotatedElementUtils.isAnnotated(method, annotationType);
		if (isMethodAnnotated) {
			return true;
		}
		// 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类
		Class<?> targetClass = method.getDeclaringClass();
		return AnnotatedElementUtils.isAnnotated(targetClass, annotationType);
	}

}

3.14 Func
  • 14.Func
package com.learning.util;

import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;

/**
 * 工具类
 */
public class Func {


	/**
	 * 判断空对象 object、map、list、set、字符串、数组
	 *
	 * @param obj the object to check
	 * @return 数组是否为空
	 */
	public static boolean isEmpty(@Nullable Object obj) {
		return ObjectUtils.isEmpty(obj);
	}

	/**
	 * 对象不为空 object、map、list、set、字符串、数组
	 *
	 * @param obj the object to check
	 * @return 是否不为空
	 */
	public static boolean isNotEmpty(@Nullable Object obj) {
		return !ObjectUtils.isEmpty(obj);
	}

}

3.15 RsaUtil
  • 15.RsaUtil
package com.learning.util;

import com.learning.crypto.tuple.KeyPair;
import org.springframework.lang.Nullable;
import org.springframework.util.Base64Utils;

import javax.crypto.Cipher;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.*;
import java.util.Objects;

/**
 * RSA加、解密工具
 *
 * 1. 公钥负责加密,私钥负责解密;
 * 2. 私钥负责签名,公钥负责验证。
 */
public class RsaUtil {
	/**
	 * 数字签名,密钥算法
	 */
	public static final String RSA_ALGORITHM = "RSA";
	public static final String RSA_PADDING = "RSA/ECB/PKCS1Padding";

	/**
	 * 获取 KeyPair
	 *
	 * @return KeyPair
	 */
	public static KeyPair genKeyPair() {
		return genKeyPair(1024);
	}

	/**
	 * 获取 KeyPair
	 *
	 * @param keySize key size
	 * @return KeyPair
	 */
	public static KeyPair genKeyPair(int keySize) {
		try {
			KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(RSA_ALGORITHM);
			// 密钥位数
			keyPairGen.initialize(keySize);
			// 密钥对
			return new KeyPair(keyPairGen.generateKeyPair());
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 生成RSA私钥
	 *
	 * @param modulus N特征值
	 * @param exponent d特征值
	 * @return {@link PrivateKey}
	 */
	public static PrivateKey generatePrivateKey(String modulus, String exponent) {
		return generatePrivateKey(new BigInteger(modulus), new BigInteger(exponent));
	}

	/**
	 * 生成RSA私钥
	 *
	 * @param modulus N特征值
	 * @param exponent d特征值
	 * @return {@link PrivateKey}
	 */
	public static PrivateKey generatePrivateKey(BigInteger modulus, BigInteger exponent) {
		RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(modulus, exponent);
		try {
			KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
			return keyFactory.generatePrivate(keySpec);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 生成RSA公钥
	 *
	 * @param modulus N特征值
	 * @param exponent e特征值
	 * @return {@link PublicKey}
	 */
	public static PublicKey generatePublicKey(String modulus, String exponent) {
		return generatePublicKey(new BigInteger(modulus), new BigInteger(exponent));
	}

	/**
	 * 生成RSA公钥
	 *
	 * @param modulus N特征值
	 * @param exponent e特征值
	 * @return {@link PublicKey}
	 */
	public static PublicKey generatePublicKey(BigInteger modulus, BigInteger exponent) {
		RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent);
		try {
			KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
			return keyFactory.generatePublic(keySpec);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 得到公钥
	 *
	 * @param base64PubKey 密钥字符串(经过base64编码)
	 * @return PublicKey
	 */
	public static PublicKey getPublicKey(String base64PubKey) {
		Objects.requireNonNull(base64PubKey, "base64 public key is null.");
		byte[] keyBytes = Base64Utils.decodeFromString(base64PubKey);
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
		try {
			KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
			return keyFactory.generatePublic(keySpec);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 得到公钥字符串
	 *
	 * @param base64PubKey 密钥字符串(经过base64编码)
	 * @return PublicKey String
	 */
	public static String getPublicKeyToBase64(String base64PubKey) {
		PublicKey publicKey = getPublicKey(base64PubKey);
		return getKeyString(publicKey);
	}

	/**
	 * 得到私钥
	 *
	 * @param base64PriKey 密钥字符串(经过base64编码)
	 * @return PrivateKey
	 */
	public static PrivateKey getPrivateKey(String base64PriKey) {
		Objects.requireNonNull(base64PriKey, "base64 private key is null.");
		byte[] keyBytes = Base64Utils.decodeFromString(base64PriKey);
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
		try {
			KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
			return keyFactory.generatePrivate(keySpec);
		} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 得到密钥字符串(经过base64编码)
	 *
	 * @param key key
	 * @return base 64 编码后的 key
	 */
	public static String getKeyString(Key key) {
		return Base64Utils.encodeToString(key.getEncoded());
	}

	/**
	 * 得到私钥 base64
	 *
	 * @param base64PriKey 密钥字符串(经过base64编码)
	 * @return PrivateKey String
	 */
	public static String getPrivateKeyToBase64(String base64PriKey) {
		PrivateKey privateKey = getPrivateKey(base64PriKey);
		return getKeyString(privateKey);
	}

	/**
	 * 共要加密
	 *
	 * @param base64PublicKey base64 的公钥
	 * @param data            待加密的内容
	 * @return 加密后的内容
	 */
	public static byte[] encrypt(String base64PublicKey, byte[] data) {
		return encrypt(getPublicKey(base64PublicKey), data);
	}

	/**
	 * 共要加密
	 *
	 * @param publicKey 公钥
	 * @param data      待加密的内容
	 * @return 加密后的内容
	 */
	public static byte[] encrypt(PublicKey publicKey, byte[] data) {
		return rsa(publicKey, data, Cipher.ENCRYPT_MODE);
	}

	/**
	 * 私钥加密,用于 qpp 内,公钥解密
	 *
	 * @param base64PrivateKey base64 的私钥
	 * @param data             待加密的内容
	 * @return 加密后的内容
	 */
	public static byte[] encryptByPrivateKey(String base64PrivateKey, byte[] data) {
		return encryptByPrivateKey(getPrivateKey(base64PrivateKey), data);
	}

	/**
	 * 私钥加密,加密成 base64 字符串,用于 qpp 内,公钥解密
	 *
	 * @param base64PrivateKey base64 的私钥
	 * @param data             待加密的内容
	 * @return 加密后的内容
	 */
	public static String encryptByPrivateKeyToBase64(String base64PrivateKey, byte[] data) {
		return Base64Utils.encodeToString(encryptByPrivateKey(base64PrivateKey, data));
	}

	/**
	 * 私钥加密,用于 qpp 内,公钥解密
	 *
	 * @param privateKey 私钥
	 * @param data       待加密的内容
	 * @return 加密后的内容
	 */
	public static byte[] encryptByPrivateKey(PrivateKey privateKey, byte[] data) {
		return rsa(privateKey, data, Cipher.ENCRYPT_MODE);
	}

	/**
	 * 公钥加密
	 *
	 * @param base64PublicKey base64 公钥
	 * @param data            待加密的内容
	 * @return 加密后的内容
	 */
	@Nullable
	public static String encryptToBase64(String base64PublicKey, @Nullable String data) {
		if (Func.isEmpty(data)) {
			return null;
		}
		return Base64Utils.encodeToString(encrypt(base64PublicKey, data.getBytes(StandardCharsets.UTF_8)));
	}

	/**
	 * 解密
	 *
	 * @param base64PrivateKey base64 私钥
	 * @param data             数据
	 * @return 解密后的数据
	 */
	public static byte[] decrypt(String base64PrivateKey, byte[] data) {
		return decrypt(getPrivateKey(base64PrivateKey), data);
	}

	/**
	 * 解密
	 *
	 * @param base64publicKey base64 公钥
	 * @param data            数据
	 * @return 解密后的数据
	 */
	public static byte[] decryptByPublicKey(String base64publicKey, byte[] data) {
		return decryptByPublicKey(getPublicKey(base64publicKey), data);
	}

	/**
	 * 解密
	 *
	 * @param privateKey privateKey
	 * @param data       数据
	 * @return 解密后的数据
	 */
	public static byte[] decrypt(PrivateKey privateKey, byte[] data) {
		return rsa(privateKey, data, Cipher.DECRYPT_MODE);
	}

	/**
	 * 解密
	 *
	 * @param publicKey PublicKey
	 * @param data      数据
	 * @return 解密后的数据
	 */
	public static byte[] decryptByPublicKey(PublicKey publicKey, byte[] data) {
		return rsa(publicKey, data, Cipher.DECRYPT_MODE);
	}

	/**
	 * rsa 加、解密
	 *
	 * @param key  key
	 * @param data 数据
	 * @param mode 模式
	 * @return 解密后的数据
	 */
	private static byte[] rsa(Key key, byte[] data, int mode) {
		try {
			Cipher cipher = Cipher.getInstance(RSA_PADDING);
			cipher.init(mode, key);
			return cipher.doFinal(data);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * base64 数据解密
	 *
	 * @param base64PublicKey base64 公钥
	 * @param base64Data      base64数据
	 * @return 解密后的数据
	 */
	public static byte[] decryptByPublicKeyFromBase64(String base64PublicKey, byte[] base64Data) {
		return decryptByPublicKey(getPublicKey(base64PublicKey), base64Data);
	}

	/**
	 * base64 数据解密
	 *
	 * @param base64PrivateKey base64 私钥
	 * @param base64Data       base64数据
	 * @return 解密后的数据
	 */
	@Nullable
	public static String decryptFromBase64(String base64PrivateKey, @Nullable String base64Data) {
		if (Func.isEmpty(base64Data)) {
			return null;
		}
		return new String(decrypt(base64PrivateKey, Base64Utils.decodeFromString(base64Data)), StandardCharsets.UTF_8);
	}

	/**
	 * base64 数据解密
	 *
	 * @param base64PrivateKey base64 私钥
	 * @param base64Data       base64数据
	 * @return 解密后的数据
	 */
	public static byte[] decryptFromBase64(String base64PrivateKey, byte[] base64Data) {
		return decrypt(base64PrivateKey, Base64Utils.decode(base64Data));
	}

	/**
	 * base64 数据解密
	 *
	 * @param base64PublicKey base64 公钥
	 * @param base64Data      base64数据
	 * @return 解密后的数据
	 */
	@Nullable
	public static String decryptByPublicKeyFromBase64(String base64PublicKey, @Nullable String base64Data) {
		if (Func.isEmpty(base64Data)) {
			return null;
		}
		return new String(decryptByPublicKeyFromBase64(base64PublicKey, Base64Utils.decodeFromString(base64Data)), StandardCharsets.UTF_8);
	}

	public static void main(String[] args) {
//		KeyPair keyPair = genKeyPair();
//		System.out.println("私钥:"+keyPair.getPrivateBase64());
//		System.out.println("公钥:"+keyPair.getPublicBase64());
		String privateBase64 = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAImWaSqyYDbyu39uwEnaEMR29siUS+cnZ+DSCbX76tfFc0QG8bIKfCcPG/O2Owe1uii0B14HUPvcNV8IrMKPkt79a/bWWW+uIbVsB4zfNITLA8pDNb/nOELcE4N3Hy+4V7evUrG6ep1ceb/ru2cL9UxLzlca0IGRx8BcIlnI0TmtAgMBAAECgYBFSLLIx25f/Teh4jl+dws+g9GeC991FYjf06UEOUl3Qnza4sxPJayDVr5yqW9sYHzQBmg3V2PWkHtn0cx9ZSNF3iyDd0EQOb0ky9M7qWXifCD0uG1Ruc+cb1/vewRrj15VH2qOd9jmgeZhxWelvx0cENRpEMicIJ+zTt0kvX+UQQJBAMECqoBG5A5X3lkSdgsUbLtNbeIsPqH+FLA3+3UT29u+s5b9uPXhWBwVfRt3gtgJxaftiI/LHA7WZbFUYrKcxGUCQQC2fVxhNSFwTeiRCoimx+WID3fILOBNPNzGr75YzOzJdDpdnvHElo2CsUatoGDSk8S2hcUtwI2LSm/yxX9bzJepAkEAg/fYsIDIKe52fxyaTZUXizGz8jMiWAysBJkie7iqWSOZE6JDtwru/bTLp94dPq3f0aQd/YN4mcSKH6d9HHcH6QJAZrDOnkj2oyrEN3I1CZ0tNc52eid+pRgdqJTWyUOv74E/ItXBeP27bhLyEdxQ/851gLxwA9n6DKr7qiKnE3Ji2QJAMcs1ipl0n/aR0NR56/uI0R1cD2AsoimfZjc8hPWdjs/YfpZVQnrcpgQx5Ps4O631F7LQKz22MbwOoBt3UcZYSQ==";
		String publicBase64 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJlmkqsmA28rt/bsBJ2hDEdvbIlEvnJ2fg0gm1++rXxXNEBvGyCnwnDxvztjsHtbootAdeB1D73DVfCKzCj5Le/Wv21llvriG1bAeM3zSEywPKQzW/5zhC3BODdx8vuFe3r1KxunqdXHm/67tnC/VMS85XGtCBkcfAXCJZyNE5rQIDAQAB";
		System.out.println("加密数据:"+encryptToBase64(publicBase64, "hello world"));
		System.out.println("解密数据:"+decryptFromBase64(privateBase64, encryptToBase64(publicBase64, "hello world")));
		String data = "hu5u8UyEg6fcn+/4wKFRSUVGETrXGifVE6/RzRBQCVxAQt+XMA7C+xVR6Ws2ZXFmYVFoS0YL29u6oVkxvmESdRc9Zj/bf0M6ykqa57vyvvRRmrrqCSYUD6STo/QRdPFK4sxlsseTU2/XjZQYohnrMmouYspWykJ3fcN34uoieHc=";
//		System.out.println("解密数据:"+decryptFromBase64(privateBase64, data));
		System.out.println("解密数据:"+RsaUtil.decryptFromBase64(privateBase64, data));
	}

}
3.16 SpringBootLearningApplication启动类
  • 16.SpringBootLearningApplication
package com.learning;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

/**
 **/
@SpringBootApplication
@ComponentScan(basePackages = {"com.learning"})
public class SpringBootLearningApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootLearningApplication.class, args);
    }
}

3.17 配置文件
  • 17.application.yaml
api:
  crypto:
    # 私钥
    rsaPrivateKey: MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAImWaSqyYDbyu39uwEnaEMR29siUS+cnZ+DSCbX76tfFc0QG8bIKfCcPG/O2Owe1uii0B14HUPvcNV8IrMKPkt79a/bWWW+uIbVsB4zfNITLA8pDNb/nOELcE4N3Hy+4V7evUrG6ep1ceb/ru2cL9UxLzlca0IGRx8BcIlnI0TmtAgMBAAECgYBFSLLIx25f/Teh4jl+dws+g9GeC991FYjf06UEOUl3Qnza4sxPJayDVr5yqW9sYHzQBmg3V2PWkHtn0cx9ZSNF3iyDd0EQOb0ky9M7qWXifCD0uG1Ruc+cb1/vewRrj15VH2qOd9jmgeZhxWelvx0cENRpEMicIJ+zTt0kvX+UQQJBAMECqoBG5A5X3lkSdgsUbLtNbeIsPqH+FLA3+3UT29u+s5b9uPXhWBwVfRt3gtgJxaftiI/LHA7WZbFUYrKcxGUCQQC2fVxhNSFwTeiRCoimx+WID3fILOBNPNzGr75YzOzJdDpdnvHElo2CsUatoGDSk8S2hcUtwI2LSm/yxX9bzJepAkEAg/fYsIDIKe52fxyaTZUXizGz8jMiWAysBJkie7iqWSOZE6JDtwru/bTLp94dPq3f0aQd/YN4mcSKH6d9HHcH6QJAZrDOnkj2oyrEN3I1CZ0tNc52eid+pRgdqJTWyUOv74E/ItXBeP27bhLyEdxQ/851gLxwA9n6DKr7qiKnE3Ji2QJAMcs1ipl0n/aR0NR56/uI0R1cD2AsoimfZjc8hPWdjs/YfpZVQnrcpgQx5Ps4O631F7LQKz22MbwOoBt3UcZYSQ==
4. 调用截图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


http://www.niftyadmin.cn/n/5680977.html

相关文章

mysql数据库项目

mysql数据库项目 如果对运维课程感兴趣,可以在b站上、csdn或微信视频号 上搜索我的账号: 运维实战课程,可以关注我,学习更多免费的运维实战技术视频 项目1 安装系统自带mysql服务,修改登录mysql数据库的默认登录密码,并在登录mysql数据库后对库的操作:查看当前已有库…

自定义knife4j访问路径

文章目录 本文档只是为了留档方便以后工作运维&#xff0c;或者给同事分享文档内容比较简陋命令也不是特别全&#xff0c;不适合小白观看&#xff0c;如有不懂可以私信&#xff0c;上班期间都是在得 原由&#xff0c;嫌弃doc.html 太大众 直接重定向&#xff0c;直接上代码了 p…

基于微信小程序的特色乡村综合展示平台设计与实现(源码+文档+讲解开发)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

死磕P7: JVM类加载那些事儿,一起探知类的前世今生(二)

这是「死磕P7」系列第 006 篇文章&#xff0c;欢迎大家来跟我一起 死磕 100 天&#xff0c;争取在 2025 年来临之际&#xff0c;给自己一个交代。 接上篇&#xff0c;上一篇介绍了 JVM 类加载过程及类的生命周期&#xff0c;回顾一下&#xff1a; 死磕P7: JVM类加载那些事儿&a…

Linux高级IO之poll与epoll

文章目录 poll使用 epoll系统调用 epoll的工作原理红黑树队列 epoll的工作模式水平触发边缘触发 Reactor设计模式工作原理epoll Reactor设计模式的简单示例 poll和epoll都是多路转接的调用&#xff0c;但是epoll实在过于优秀了&#xff0c;一般也都是用epoll的&#xff0c;除此…

【架构设计笔记】抽象接口、抽象类和实现类

前言 最近学习数据结构与算法&#xff0c;打算写一个用来拓展JavaScript基础数据结构的npm包&#xff0c;例如栈/队列/链表之类的&#xff0c;从中有了不少收获&#xff0c;主要是关于工程架构角度&#xff0c;写个笔记&#xff0c;总结一下&#xff0c;将从架构设计的角度写写…

【高频SQL基础50题】6-10

目录 1.上级经理已离职的公司员工 2.修复表中的名字 3. 寻找用户推荐人 4.产品销售分析 I 5.平均售价 1.上级经理已离职的公司员工 子查询。 先根据薪水大小查询&#xff0c;再根据manager_id查询该员工是否存在&#xff0c;最后做排序。 # Write your MySQL query st…

物联网智能项目全面解析

目录 引言 一、物联网概述 1.1 什么是物联网 1.2 物联网的历史与发展 二、物联网智能项目分类 三、关键组件与技术 3.1 传感器和执行器 3.2 连接技术 3.3 数据处理与分析 3.4 用户界面 四、物联网智能项目案例分析 4.1 智能家居 4.2 智慧城市 4.3 工业物联网 4.4…