最近有个需求,需要对数据库某些字段加密,调研发现采用AES加密的方式较多,而且反向解密速度快,符合需求,于是采用;下面是遇到的问题及相关代码
首先第一个问题,AES的秘钥是16位,mysql的密码长度aes_encrypt没有要求:
是因为mysql默认取秘钥的ascii编码前16位,相当于:
byte[] keyBytes = Arrays.copyOf(password.getBytes("ASCII"), 16);
第二个问题,因涉及历史数据处理,mysql需要使用aes_encrypt加密:
但是生成的二进制数据,要把二进制数据存到原先的varchar字段中,网上搜索多数采用的HEX(aes_encrypt(name, "password")),而HEX编码会过分增大原数据的长度(据说是增倍),有的字段长度会变的过长;
于是再查询发现,mysql5.6以后增加了to_base64的base64编码方式。两者区别
- hex也称为base16,意思是使用16个可见字符来表示一个二进制数组,编码后数据大小将翻倍,因为1个字符需要用2个可见字符来表示。
- base32,意思是使用32个可见字符来表示一个二进制数组,编码后数据大小变成原来的8/5,也即5个字符用8个可见字符表示,但是最后如果不足8个字符,将用=来补充。
- base64,意思是使用64个可见字符来表示一个二进制数组,编码后数据大小变成原来的4/3,也即3个字符用4个可见字符来表示。
可见base64的字段长度比HEX少很多,db设计不是很严谨的甚至不必增加字段长度;
相关sql:
加密
update T_USER t
set t.name=to_base64(aes_encrypt(name, "password"));
解密
select cast(aes_decrypt(from_base64(name) ,"password") as char) from T_USER;
java类:
import org.apache.commons.lang3.StringUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Arrays;
import java.util.Base64;
/**
* Created by Administrator on 2018/11/8.
*/
public class EncryptUtil {
private static final String PASS_WORD="edmund2018";
private static SecretKeySpec AES_PASSWORD;
private static final String KEY_ALGORITHM = "AES";
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默认的加密算法
/**
* AES 加密操作
*
* @param content 待加密内容
* @return 返回Base64转码后的加密数据
*/
public static String encrypt(String content) {
if(StringUtils.isEmpty(content)) {
return content;
}
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);// 创建密码器
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(PASS_WORD));// 初始化为加密模式的密码器
byte[] encrypted = cipher.doFinal(content.getBytes("utf-8"));
return new BASE64Encoder().encode(encrypted);
} catch (Exception ex) {
Logger.defalutLogger.error("加密失败");
}
return content;
}
/**
* AES 解密操作
*
* @param content
* @return
*/
public static String decrypt(String content) {
if(StringUtils.isEmpty(content)) {
return content;
}
try {
//实例化
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
//使用密钥初始化,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(PASS_WORD));
byte[] base64Data = new BASE64Decoder().decodeBuffer(content);//先用base64解密
byte[] original = cipher.doFinal(base64Data);
String originalString = new String(original,"utf-8");
return originalString;
} catch (Exception ex) {
Logger.defalutLogger.error("解密失败"+ex.getStackTrace());
}
return content;
}
/**
* 生成加密秘钥
*
* @return
*/
private static SecretKeySpec getSecretKey(final String password) throws Exception {
if(AES_PASSWORD !=null){
return AES_PASSWORD;
}
try { //返回生成指定算法密钥生成器的 KeyGenerator 对象
byte[] keyBytes = Arrays.copyOf(password.getBytes("ASCII"), 16);
AES_PASSWORD =new SecretKeySpec(keyBytes, KEY_ALGORITHM);// 转换为AES专用密钥
return AES_PASSWORD;
} catch (Exception ex) {
Logger.defalutLogger.error("加密秘钥失败"+ex.getStackTrace());
throw new Exception("加密秘钥失败");
}
}
}