在一些项目中,客户要求一方面把一些敏感信息进行加密存储到数据库中,另一方面又需要通过加密的信息进行查询,这时就需要在sql对加密的字段进行解密后再进行查询。
AesFieldUtils.java
package com.hw.common.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
public class AesFieldUtils {
/**
* 加密算法
*/
private final String KEY_ALGORITHM = "AES";
/**
* 算法/模式/补码方式
*/
private final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
/**
* 编码格式
*/
private final String CODE = "utf-8";
/**
* base64验证规则
*/
private static final String BASE64_RULE = "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)=?$";
/**
* 正则验证对象
*/
private static final Pattern PATTERN = Pattern.compile(BASE64_RULE);
/**
* 加解密 密钥key
*/
private String key
/**
* @param content 加密字符串
* @return 加密结果
*/
public String encrypt(String content) {
return encrypt(content, key);
}
/**
* 加密
*
* @param content 加密参数
* @param key 加密key
* @return 结果字符串
*/
public String encrypt(String content, String key) {
//判断如果已经是base64加密字符串则返回原字符串
if (isBase64(content)) {
return content;
}
// 为了安全起见,暂时不加密,需要时再放开
//return content;
byte[] encrypted = encrypt2bytes(content, key);
if (null == encrypted || encrypted.length < 1) {
log.error("加密字符串[{}]转字节为null", content);
return null;
}
return Base64Utils.encodeToString(encrypted);
}
/**
* @param content 加密字符串
* @param key 加密key
* @return 返回加密字节
*/
public byte[] encrypt2bytes(String content, String key) {
try {
byte[] raw = key.getBytes(CODE);
SecretKeySpec secretKeySpec = new SecretKeySpec(raw, KEY_ALGORITHM);
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
return cipher.doFinal(content.getBytes(CODE));
} catch (Exception e) {
log.error("failed to encrypt: {} of {}", content, e);
return null;
}
}
/**
* @param content 加密字符串
* @return 返回加密结果
*/
public String decrypt(String content) {
try {
return decrypt(content, key);
} catch (Exception e) {
log.error("failed to decrypt: {}, e: {}", content, e);
return null;
}
}
/**
* 解密
*
* @param content 解密字符串
* @param key 解密key
* @return 解密结果
*/
public String decrypt(String content, String key) throws Exception {
//不是base64格式字符串则不进行解密
if (!isBase64(content)) {
return content;
}
// 为了安全起见,暂时不加密解密,需要时再放开
//return content;
return decrypt(Base64Utils.decodeFromString(content), key);
}
/**
* @param content 解密字节
* @param key 解密key
* @return 返回解密内容
*/
public String decrypt(byte[] content, String key) throws Exception {
if (key == null) {
log.error("AES key should not be null");
return null;
}
byte[] raw = key.getBytes(CODE);
SecretKeySpec keySpec = new SecretKeySpec(raw, KEY_ALGORITHM);
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, keySpec);
try {
byte[] original = cipher.doFinal(content);
return new String(original, CODE);
} catch (Exception e) {
log.error("failed to decrypt content: {}/ key: {}, e: {}", content, key, e);
return null;
}
}
/**
* 判断是否为 base64加密
*
* @param str 参数
* @return 结果
*/
public static boolean isBase64(String str) {
Matcher matcher = PATTERN.matcher(str);
return matcher.matches();
}
public static void main(String[] args) {
AesFieldUtils aesFieldUtils = new AesFieldUtils();
String reuslt = aesFieldUtils.encrypt("13809449025","qwertyuiopasdfgh");
System.out.println(reuslt);
}
}
加密的密文:dcAJ143U+AkxbvOwRctnVg==
mysql 使用 AES_DECRYPT(crypt_str,key_str) 进行解密,第一个参数crypt_str加密的字符串,第二个参数key_str 密钥,需和代码加密时的密钥保持一致。
SELECT
AES_DECRYPT(FROM_BASE64('dcAJ143U+AkxbvOwRctnVg=='), 'qwertyuiopasdfgh') AS '解密结果';