SpringBoot实现接口的RSA和AES加解密

RSA加密和AES加密使用了hutool工具包

依赖

<dependency>
   <groupId>cn.hutool</groupId>
   <artifactId>hutool-all</artifactId>
   <version>5.7.13</version>
</dependency>

加解密工具类

public class EncryptionUtil {

    // rsa秘钥
    private final static String PRIVATE_KEY = "...";

    // rsa公钥
    private final static String PUBLIC_KEY = "...";

    // 创建rsa加密对象 服务端公钥是不需要的,公钥提供给前端就行
    private final static RSA RSA = new RSA(PRIVATE_KEY, PUBLIC_KEY);

    /**
     * 获取加密返回
     */
    public static EncryptionResponse getEncodeResponse(String encryptionData) {
        // 生成随机aes秘钥
        byte[] key = getRandomKey();
        AES aes = SecureUtil.aes(key);
        // 加密aes key
        String aesKey = RSA.encryptBase64(key, KeyType.PrivateKey);
        // aes加密内容
        String encryptString = aes.encryptBase64(encryptionData);
        return new EncryptionResponse(aesKey, encryptString);
    }

    /**
     * 获取解密返回流
     */
    public static InputStream getDecodeInputStream(EncryptionRequest request) {
        String encryptionKey = request.getEncryptionKey();
        // rsa解密 获取aes秘钥
        byte[] decryptKey = RSA.decrypt(encryptionKey, KeyType.PrivateKey);
        // aes进行解密内容 获取输入流(方便整合springMVC)
        AES aes = SecureUtil.aes(decryptKey);
        byte[] decryptData = aes.decrypt(request.getEncryptionData());
        return IoUtil.toStream(decryptData);
    }

    /**
     * 随机生成aes的秘钥
     */
    private static byte[] getRandomKey() {
        return RandomUtil.randomString(16).getBytes();
    }
}

进行加密解密的一些对象准备

#配置
request:
  encode: true # 配置接口返回是否进行加密
  decode: true # 配置接口入参是否进行解密
// 两个实体对象,用来表示请求和返回结果
@Data
@AllArgsConstructor
public class EncryptionResponse {

    // rsa加密后的aes秘钥
    private String encryptionKey;

    // aes加密后的数据内容
    private String encryptionData;
}

@Data
public class EncryptionRequest {

    // 同response
    private String encryptionKey;

    private String encryptionData;
}

// 自定义注解,方法上有这个注解的进行加密和解密的操作
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SecurityParameter {

    /**
     * 入参是否解密,默认解密
     */
    boolean inDecode() default true;

    /**
     * 出参是否加密,默认加密
     */
    boolean outEncode() default true;
}

SpringMvc的responseBody增强

// 进行加密的advice,@ConditionalOnProperty配置request.encode等于true的时候才进行加密
@Slf4j
@ControllerAdvice
@ConditionalOnProperty(value = "request.encode", havingValue = "true")
public class EncodeResponseAdvice implements ResponseBodyAdvice {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        // 方法上有SecurityParameter注解的进行加密 根据需求可以在判断SecurityParameter注解中的outEncode是否为true
        return methodParameter.hasMethodAnnotation(SecurityParameter.class);
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass,
                                  ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        try {
            // 要支持接口返回string的话,response需要序列化成string对象,因为接口返回string和对象使用消息转换器不是一样的
            // 把返回结果序列化成json
            String dataJson = objectMapper.writeValueAsString(o);
            // 进行加密返回
            return EncryptionUtil.getEncodeResponse(dataJson);
        } catch (JsonProcessingException e) {
            log.error("json processing error", e);
        }
        return o;
    }
}

SpringMvc的requestBody增强

// 参数进行解密的方法 request.decode配置控制是否参数进行解密(这个是RequestBody注解的增强,参数必须要加这个注解)
@Slf4j
@ControllerAdvice
@RequiredArgsConstructor
@ConditionalOnProperty(value = "request.decode", havingValue = "true")
public class DecodeRequestAdvice implements RequestBodyAdvice {

    private final ObjectMapper objectMapper;

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        // 判断方法是否需要进行解密
        return methodParameter.hasMethodAnnotation(SecurityParameter.class) && methodParameter.getMethodAnnotation(SecurityParameter.class).inDecode();
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        // 读取body对象,获取EncryptionRequest对象
        InputStream body = httpInputMessage.getBody();
        EncryptionRequest encryptionRequest = objectMapper.readValue(body, EncryptionRequest.class);
        if (encryptionRequest.getEncryptionKey() == null) {
            throw new IllegalArgumentException("encryption error,request is null");
        }
        // 转换成解密后的HttpInputMessage对象
        HttpInputMessage decryHttpMessage = new HttpInputMessage() {
            @Override
            public HttpHeaders getHeaders() {
                return httpInputMessage.getHeaders();
            }

            // 解密后的输入流
            @Override
            public InputStream getBody() {
                return EncryptionUtil.getDecodeInputStream(encryptionRequest);
            }
        };
        return decryHttpMessage;
    }

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

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

执行结果

// 测试
@RestController
public class DemoController {

    // string的接口返回值要在ResponseBodyAdvice中进行特殊处理,将对象转换成string对象
    // 正常业务中基本是会固定返回对象的,所以我这里就不做处理了
    @SecurityParameter
    @PostMapping("test")
    public DemoResponse demo() {
        return new DemoResponse("success");
    }

}

接口执行结果
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
首先,我们可以使用RSA算法来AES的密钥,然后使用AES算法来密数据。 1. 生成RSA公私钥对 ```java KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); generator.initialize(2048); KeyPair keyPair = generator.generateKeyPair(); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); ``` 2. 使用RSA公钥AES密钥 ```java // 生成AES密钥 KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(128); SecretKey secretKey = keyGenerator.generateKey(); // 使用RSA公钥AES密钥 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] encryptedKey = cipher.doFinal(secretKey.getEncoded()); ``` 3. 使用AES密钥密数据 ```java // 使用AES密钥密数据 byte[] rawData = "hello world".getBytes("UTF-8"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] encryptedData = cipher.doFinal(rawData); ``` 4. 使用RSA私钥解密AES密钥 ```java // 使用RSA私钥解密AES密钥 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decryptedKey = cipher.doFinal(encryptedKey); SecretKey originalKey = new SecretKeySpec(decryptedKey, 0, decryptedKey.length, "AES"); ``` 5. 使用AES密钥解密数据 ```java // 使用AES密钥解密数据 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, originalKey); byte[] decryptedData = cipher.doFinal(encryptedData); ``` 注意事项: - AES密钥需要保密,不能直接传输或存储。 - RSA密的数据长度不能超过RSA公钥的长度。因此,如果需要密的数据较长,可以使用AES算法对数据进行分块密。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dichotomy_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值