Spring Boot整合RSA与前端进行加密数据传输

1 篇文章 0 订阅
1 篇文章 0 订阅

简介

通过整合Spring Boot与RSA加密算法,你可以实现安全的数据传输和保护敏感信息。确保密钥的安全管理,以及在加密和解密过程中处理异常,是确保系统安全性的关键步骤。

一.Spring Boot 整合 RSA

可以自己生成一个公钥和私钥,也可以在网站中在线生成:在线RSA密钥对生成工具 - UU在线工具

application.yml

        

# 数据加密
data-encryption:
  public-key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD15L5jt1JxlUWcA6Om4HT7DA7P4wvrWaJ3weypmZky13AGxG3onOW5ijQWvW/GIWx1enN2EFhg9haQC3MVA1007PtR7zE0gZJhbsj8G25sAtiaX8nEfKaTa5WjAeZME0Jr4THfnek4ypqOSgObCvs/cvdiJlnw3fK8is41Nr4muQIDAQAB
  private-key: MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAPXkvmO3UnGVRZwDo6bgdPsMDs/jC+tZonfB7KmZmTLXcAbEbeic5bmKNBa9b8YhbHV6c3YQWGD2FpALcxUDXTTs+1HvMTSBkmFuyPwbbmwC2JpfycR8ppNrlaMB5kwTQmvhMd+d6TjKmo5KA5sK+z9y92ImWfDd8ryKzjU2via5AgMBAAECgYEAuw/+qxtuk8wsfYjjOfOt6sJ8jjg6/Btflt2nrkqUmnH83pkWyAx8U09JWzdYThFlUmYKann60iDEf2bGcOjzQO9t/zmR27NShklT5Vs/cEJ769XQDc34uUyabYNpvpPPAxgrMAWXUZ2lGiJsmu2qpdR/9mBiDaIxtv6F2WdapFECQQD/hF7qlksJbGXlFzSUlExKdpCuHU4cTFUnoG+EFPowHGJOtB6OON4Pz+Qh+qwJwULfV0vuoRYExLGJx/4hmf/NAkEA9lu3eSgrwdcYY/LNMdkv0WCvkPdf0c+ZpqHpKu/4BwgovnVFSnQfnTzkWveY29kiNpC8FjX/UZoSFrGGJ9tenQJABySEIrqgzlqhXo4PgjfDCjHWSYe4L0JS/xCVDXQoqj+g3+JZGFCupHKDRg/jz0H/xI6EKN+H5go5jEhy6i2L4QJADgR/9wvbGYd0otNA67Iw71fyBWyd7iFC2+FglAndcgD72IJxs9/RoK/DSo3GC9RaBwLLQPRHl8wNdW8adBAeXQJAI+DMwPowmxzkSM+zA9JNUMy8vMzBBIpsJJ6WXgMPn/sEd+s5d2CSe7/Q1+Bjv6xvqKKjBjmke3dJs8o3VnN5Tg==

注意:公钥和私钥要放为一行,不然会报错。

RequestBodyAdvice

我们在请求之前处理数据,我们与前端的对接是

请求体是否需要解密,根据请求头里的 Encryption-Type 字段判断,为 1 时表示需要解密,为 0 或者没有这个字段 表示不需要解密

import com.general.exception.UnifiedException;
import com.general.tools.RSAUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

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

/**
 * 请求前数据处理
 */
@ControllerAdvice("com.general.controller")
@Slf4j
public class RsaDecodeRequestBodyAdvice implements RequestBodyAdvice {


    @Value("${data-encryption.private-key}")
    private String privateKay;

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        HttpHeaders headers = httpInputMessage.getHeaders();
        String encryptionType = headers.getFirst("Encryption-Type");
        boolean decode = "1".equals(encryptionType);

        if (decode) {
            log.info("对方法method :【" + methodParameter.getMethod().getName() + "】请求体数据进行解密");
            return new MyHttpInputMessage(httpInputMessage);
        } else {
            return httpInputMessage;
        }
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return body;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return body;
    }


    class MyHttpInputMessage implements HttpInputMessage {

        private HttpHeaders httpHeaders;

        private InputStream inputStream;

        public MyHttpInputMessage(HttpInputMessage httpInputMessage) throws IOException {
            this.httpHeaders = httpInputMessage.getHeaders();
            String easpString = easpString(IOUtils.toString(httpInputMessage.getBody(), "UTF-8"));
            this.inputStream = IOUtils.toInputStream(RSAUtils.decryptDataOnJava(easpString, privateKay));
        }

        @Override
        public InputStream getBody() throws IOException {
            return this.inputStream;
        }

        @Override
        public HttpHeaders getHeaders() {
            return this.httpHeaders;
        }

        /**
         * @param requestData
         * @return
         */
        public String easpString(String requestData) {
            if (requestData != null && !requestData.isEmpty()) {
                String prefix = "{\"requestData\":";
                if (!requestData.startsWith(prefix)) {
                    throw new UnifiedException("数据解密失败!");
                }
                int openIndex = prefix.length();
                int closeIndex = requestData.length() - 1;
                if (closeIndex <= openIndex) {
                    throw new UnifiedException("数据参数格式错误!");
                }
                return requestData.substring(openIndex, closeIndex);
            }
            return null;
        }
    }
}

ResponseBodyAdvice

我们对返回给前端的数据进行加密,我们与前端的对接是检查请求头是否有 PublicKey 字段,如果有则用其加密返回数据,如果没有就直接明文返回(PublicKey为前端公钥)


import com.fasterxml.jackson.databind.ObjectMapper;
import com.general.tools.RSAUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;


/**
 * 返回数据加密
 */
@Component
@ControllerAdvice("com.general.controller")
@Slf4j
public class ResponseBodyAdvice implements ResponseBodyAdvice {

    @Value("${data-encryption.public-key}")
    private String publicKey;

    /**
     * 是否支持这个类
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        // 默认是不使用,改为true
        return true;
    }

    /**
     * 返回前处理
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        log.info("------响应数据加密-------");
        String publicKeyHeader = serverHttpRequest.getHeaders().getFirst("PublicKey");

        if (publicKeyHeader != null) {
            log.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行加密");
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                String result = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(body);
                return RSAUtils.encryptByPublicKey(result.getBytes(), publicKeyHeader).toString();
            } catch (Exception e) {
                e.printStackTrace();
                log.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行加密出现异常:" + e.getMessage());
                return encryptErrorData(e.getMessage());
            }
        }
        return body;
    }

    // 加密错误信息
    private String encryptErrorData(String errorMessage) {
        // 这里可以根据需要加密错误信息
        return errorMessage;
    }
}

RSAUtils

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class RSAUtils {

	public static final String KEY_ALGORITHM = "RSA";

	private static final int MAX_ENCRYPT_BLOCK = 245;

	private static final int MAX_DECRYPT_BLOCK = 1280000;


	/**
	 * 私钥解密
	 */
	public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
		byte[] keyBytes = Base64.decodeBase64(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, privateK);
		int inputLen = encryptedData.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段解密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
				cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_DECRYPT_BLOCK;
		}
		byte[] decryptedData = out.toByteArray();
		out.close();
		return decryptedData;
	}


	/**
	 * java端私钥解密
	 */
	public static String decryptDataOnJava(String data, String PRIVATEKEY) {
		String temp = "";
		try {
			byte[] rs = Base64.decodeBase64(data);
			temp = new String(RSAUtils.decryptByPrivateKey(rs, PRIVATEKEY),"UTF-8");

		} catch (Exception e) {
			e.printStackTrace();
		}
		return temp;
	}


	/**
	 * 公钥加密
	 */
	public static String encryptByPublicKey(byte[] data, String publicKeyStr) throws Exception {
		byte[] keyBytes = Base64.decodeBase64(publicKeyStr);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(keyBytes));

		Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);

		int inputLen = data.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;

		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
				cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(data, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_ENCRYPT_BLOCK;
		}

		byte[] encryptedData = out.toByteArray();
		out.close();
		return Base64.encodeBase64String(encryptedData);
	}
}

二.加密测试

三.总结

RSA加密,基于大数因子分解难题,使用公钥加密、私钥解密,确保数据安全传输。密钥一对生成,非对称设计提升安全性,广泛应用于网络通信加密。参考网上的案例进行修改。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值