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