Springboot 实现数据加解密

Springboot 实现数据加解密

常用的算法介绍

  1. AES(Advanced Encryption Standard)**:是一种对称加密算法,用于替代过时的DES(Data Encryption Standard)。AES支持128位、192位和256位密钥长度,被广泛用于保护敏感信息。
  2. RSA(Rivest-Shamir-Adleman):是一种非对称加密算法,用于实现数据的加密和数字签名。RSA基于数学问题,涉及公钥和私钥,公钥用于加密,私钥用于解密。
  3. Diffie-Hellman密钥交换:是一种用于安全地交换密钥的协议,通过不安全的通信渠道,双方可以生成一个共享的密钥,然后用于对称加密。
  4. ECC(Elliptic Curve Cryptography):是一种基于椭圆曲线数学问题的非对称加密算法,与RSA相比,可以实现相同的安全性,但使用更短的密钥长度。
  5. Blowfish:是一种对称加密算法,适用于加密较大的数据块,由于其速度较快,被广泛用于加密通信和文件。
  6. Twofish:也是一种对称加密算法,是Blowfish的后续版本,被认为在安全性方面更为强大。
  7. DES(Data Encryption Standard):是一种对称加密算法,但由于其56位密钥长度较短,已被认为不够安全,逐渐被AES所取代。
  8. 3DES(Triple Data Encryption Standard):是对DES的改进,通过多次应用DES算法来增加安全性,但由于其计算开销较大,逐渐被更高效的加密算法取代。

实现数据的加解密

代码量过大,这里只展示了主要的逻辑,源代码已经放在github上

地址 https://github.com/yanghaiji/SecureTransmit

自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Decrypt {

}


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Encrypt {

}

数加密
@Slf4j
@RestControllerAdvice
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    private boolean encrypt;

    private final SecretProperties secretProperties;

    private final ThreadLocal<String> publicKey = new ThreadLocal<>();
    private final ThreadLocal<String> privateKey = new ThreadLocal<>();


    public EncryptResponseBodyAdvice(SecretProperties secretProperties) {
        this.secretProperties = secretProperties;
        LocalKeysInitFactory.initLocalKeys(secretProperties, this.publicKey, this.privateKey);
    }

    private static final ThreadLocal<Boolean> ENCRYPT_LOCAL = new ThreadLocal<>();

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        encrypt = Objects.requireNonNull(returnType.getMethod()).isAnnotationPresent(Encrypt.class) && secretProperties.getEnable();
        return encrypt;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        Boolean status = ENCRYPT_LOCAL.get();
        if (null != status && !status) {
            ENCRYPT_LOCAL.remove();
            return body;
        }
        if (encrypt) {
            try {
                // bug fix key is null
                LocalKeysInitFactory.initLocalKeys(secretProperties, this.publicKey, this.privateKey);
                String content = new SerializationStrategy().serialize(body);
                if (!StringUtils.hasText(publicKey.get())) {
                    throw new NullPointerException("Please configure secure.transmit.encrypt.publicKey parameter!");
                }
                String result = SecureTransmitDigest.getInstance(secretProperties).encrypt(publicKey.get(), content);
                if (secretProperties.getIsShowLog()) {
                    log.info("Pre-encrypted data:{},After encryption:{}", content, result);
                }
                return result;
            } catch (Exception e) {
                log.error("Encrypted data exception {}", e.getMessage(), e);
                throw new SecurityException("Encrypted data exception");
            } finally {
                cleanUp();
            }
        }
        return body;
    }


    /**
     * 清除当前的 缓存key
     */
    private void cleanUp() {
        privateKey.remove();
        publicKey.remove();
    }
}
数据解密
@Slf4j
@ControllerAdvice
public class DecryptRequestBodyAdvice implements RequestBodyAdvice {

    /**
     * 是否开启加密 {@link SecretProperties#getEnable()}
     */
    private boolean encrypt;

    /**
     * 安全信息配置 {@link SecretProperties}
     */
    private final SecretProperties secretProperties;

    /**
     * 密钥和机密算法的配置
     */
    private final ThreadLocal<String> publicKey = new ThreadLocal<>();
    private final ThreadLocal<String> privateKey = new ThreadLocal<>();

    public DecryptRequestBodyAdvice(SecretProperties secretProperties) {
        this.secretProperties = secretProperties;
        LocalKeysInitFactory.initLocalKeys(secretProperties, this.publicKey, this.privateKey);
    }

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        if (Objects.requireNonNull(methodParameter.getMethod()).isAnnotationPresent(Decrypt.class) && secretProperties.getEnable()) {
            encrypt = true;
        }
        return encrypt;
    }

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

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
                                           Class<? extends HttpMessageConverter<?>> converterType) {
        if (encrypt) {
            try {
                LocalKeysInitFactory.initLocalKeys(secretProperties, this.publicKey, this.privateKey);
                return new DecryptHttpInputMessage(inputMessage, privateKey.get(), secretProperties, secretProperties.getIsShowLog());
            } catch (Exception e) {
                log.error("Decryption failed {}", e.getMessage(), e);
                throw new SecretException("DecryptRequestBodyAdvice.class error ");
            } finally {
                cleanUp();
            }
        }
        return inputMessage;
    }

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


    /**
     * 清除当前的 缓存key
     */
    private void cleanUp() {
        privateKey.remove();
        publicKey.remove();
    }

}

加解密算法

@Slf4j
public class RsaEncryption implements SecureTransmitTemplate {

    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * 使用给定的公钥加密给定的字符串。
     *
     * @param key       给定的公钥。
     * @param plaintext 字符串。
     * @return 给定字符串的密文。
     */
    @Override
    public String encrypt(String key, String plaintext) {
        if (key == null || plaintext == null) {
            return null;
        }
        byte[] data = plaintext.getBytes();
        try {
            PublicKey publicKey = DataKeyGenerator.RSA.getPublicKey(key);
            byte[] enData = encrypt(publicKey, data);
            return Base64.encodeBase64String(enData);
        } catch (Exception ex) {
            log.error("encryptString error {}", ex.getMessage());
        }
        return null;
    }

    /**
     * 使用指定的公钥加密数据。
     *
     * @param key  给定的公钥。
     * @param data 要加密的数据。
     * @return 加密后的数据。
     */

    private byte[] encrypt(Key key, byte[] data) throws Exception {
        Cipher ci = Cipher.getInstance(EncryptConstant.ALGORITHM);
        ci.init(Cipher.ENCRYPT_MODE, key);
        return getEnDeData(data, ci, MAX_ENCRYPT_BLOCK);
    }

    /**
     * 使用给定的公钥解密给定的字符串。
     *
     * @param key  给定的公钥
     * @param data 密文
     * @return 原文字符串。
     */
    @Override
    public String decrypt(String key, String data) {
        if (key == null || isBlank(data)) {
            return null;
        }
        try {
            PrivateKey privateKey = DataKeyGenerator.RSA.getPrivateKey(key);
            byte[] enData = Base64.decodeBase64(data);
            return new String(decrypt(privateKey, enData));
        } catch (Exception ex) {
            log.error(" Decryption failed. Cause: {} , error {}", data, ex.getCause().getMessage());
        }
        return null;
    }

    /**
     * 使用指定的公钥解密数据。
     *
     * @param key  指定的公钥
     * @param data 要解密的数据
     * @return 原数据
     */
    private static byte[] decrypt(Key key, byte[] data) throws Exception {
        Cipher ci = Cipher.getInstance(EncryptConstant.ALGORITHM);
        ci.init(Cipher.DECRYPT_MODE, key);
        return getEnDeData(data, ci, MAX_DECRYPT_BLOCK);
    }

    /**
     * 数据加解密
     */
    private static byte[] getEnDeData(byte[] data, Cipher ci, int maxBlock) throws Exception {
        int inputLen = data.length;
        byte[] edData;
        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段解密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > maxBlock) {
                    cache = ci.doFinal(data, offSet, maxBlock);
                } else {
                    cache = ci.doFinal(data, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * maxBlock;
            }
            edData = out.toByteArray();
        }
        return edData;
    }

    /**
     * 判断非空字符串
     *
     * @param cs 待判断的CharSequence序列
     * @return 是否非空
     */
    private static boolean isBlank(final CharSequence cs) {
        int strLen;
        if (cs == null || (strLen = cs.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if (!Character.isWhitespace(cs.charAt(i))) {
                return false;
            }
        }
        return true;
    }

}

完整的代码连接 ,源代码地址https://github.com/yanghaiji/SecureTransmit

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
SpringBoot中可以使用加密算法,如AES、DES等,对接口数据进行加解密处理。下面给出一个简单的例子。 1.添加依赖 在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>2.5.5</version> </dependency> ``` 2.配置文件 在application.properties文件中添加以下配置: ``` # 加密密钥 encryption.key=1234567890abcdef ``` 3.自定义加解密工具类 定义一个加解密工具类,如下: ``` @Component public class EncryptionUtils { @Value("${encryption.key}") private String key; /** * 加密 * * @param data 待加密数据 * @return 加密后数据 */ public String encrypt(String data) { try { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES"); IvParameterSpec ivParameterSpec = new IvParameterSpec(key.getBytes()); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec); byte[] encryptedData = cipher.doFinal(data.getBytes("UTF-8")); return Base64.getEncoder().encodeToString(encryptedData); } catch (Exception e) { throw new RuntimeException("加密失败", e); } } /** * 解密 * * @param data 待解密数据 * @return 解密后数据 */ public String decrypt(String data) { try { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES"); IvParameterSpec ivParameterSpec = new IvParameterSpec(key.getBytes()); cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); byte[] decryptedData = cipher.doFinal(Base64.getDecoder().decode(data)); return new String(decryptedData, "UTF-8"); } catch (Exception e) { throw new RuntimeException("解密失败", e); } } } ``` 4.使用加解密工具类 在Controller中使用加解密工具类,如下: ``` @RestController @RequestMapping("/api") public class ApiController { @Autowired private EncryptionUtils encryptionUtils; @PostMapping("/encrypt") public String encrypt(@RequestBody String data) { return encryptionUtils.encrypt(data); } @PostMapping("/decrypt") public String decrypt(@RequestBody String data) { return encryptionUtils.decrypt(data); } } ``` 5.测试接口 使用Postman测试接口,如下: - 请求:POST http://localhost:8080/api/encrypt Body:{"name": "张三", "age": 18} - 响应:f1gYflQ2Kj7TzJJbTnA7vQ== - 请求:POST http://localhost:8080/api/decrypt Body:f1gYflQ2Kj7TzJJbTnA7vQ== - 响应:{"name": "张三", "age": 18} 以上就是SpringBoot实现接口数据加解密的简单示例,可以根据实际需求进行修改和完善。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小杨同学~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值