最近有个需求,需要对数据库某些字段加密,调研发现采用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("加密秘钥失败");
        }

    }
}