AES加密解密工具类封装(AESUtil)
import org.springframework.util.Base64Utils; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.logging.Level; import java.util.logging.Logger; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; /** * @version V1.0 * @desc AES 加密工具类 */ public class AESUtil { private static final String KEY_ALGORITHM = "AES"; private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默认的加密算法 /** * AES 加密操作 * * @param content 待加密内容 * @param password 加密密码 * @return 返回Base64转码后的加密数据 */ public static String encrypt(String content, String password) { try { Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);// 创建密码器 byte[] byteContent = content.getBytes("utf-8"); cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(password));// 初始化为加密模式的密码器 byte[] result = cipher.doFinal(byteContent);// 加密 //Base64是一种基于64个可打印字符来表示二进制数据的表示方法。 return Base64Utils.encodeToString(result);//通过Base64转码返回 } catch (Exception ex) { Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex); } return null; } /** * AES 解密操作 * * @param content * @param password * @return */ public static String decrypt(String content, String password) { try { //实例化 Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); //使用密钥初始化,设置为解密模式 cipher.init(Cipher.DECRYPT_MODE, getSecretKey(password)); //执行操作 //Base64是一种基于64个可打印字符来表示二进制数据的表示方法。 byte[] result = cipher.doFinal(Base64Utils.decodeFromString(content)); return new String(result, "utf-8"); } catch (Exception ex) { Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex); } return null; } /** * 生成加密秘钥 * * @return */ private static SecretKeySpec getSecretKey(String password) { //返回生成指定算法密钥生成器的 KeyGenerator 对象 KeyGenerator kg = null; try { kg = KeyGenerator.getInstance(KEY_ALGORITHM); //AES 要求密钥长度为 128 kg.init(128, new SecureRandom(password.getBytes())); //生成一个密钥 SecretKey secretKey = kg.generateKey(); return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);// 转换为AES专用密钥 } catch (NoSuchAlgorithmException ex) { Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex); } return null; } public static void main(String[] args) { String origin = "my test string"; String encrypt = AESUtil.encrypt(origin, "my password"); String decrypt = AESUtil.decrypt(encrypt, "my password"); System.out.println(origin); System.out.println(encrypt); System.out.println(decrypt); } }
关于过程中,为什么可以用base64对产生的数组进行编码,以及解码。使用的时候可能产生异常:解密的字节数组必须是16的倍数
发现当把字节数组转为字符串后,在把字符串.getBytes()获得数组,发现两个字节数组前后不一样了
强调一下:new String(byte[]),和"str".getBytes(),两个方法使用的编码一样,然后换成其他编码也出现这样情况
原因:
1. 为什么数组转字符串,字符串然后转数组会出现,前后两个字节数组的值会不同,因为并不是每个字节数和编码集上的字符都有对应关系,如果一个字节数在编码集上没有对应,编码new String(byte[]) 后,往往解出来的会是一些乱码无意义的符号:例如:��,
但是解码的时候,�这个字符也是一个字符在编码表中也有固定的字节数用来表示,所有解码出来的值必定是编码表中对应的值,除非你的字节数组中的字节数正好在编码表中有对应的值,否则编码,解码后的字节数组会不一样
误区: 误以为所有的字节数组都可以new String(),然后在通过String.getBytes()还原
2.再说这个异常报:解密的字节数组必须是16的倍数,这得从AES的原理说起,AES是把数据按16字节分组加密的,所有如果数组长度不是16的倍数会报错
AES原理:AES是对数据按128位,也就是16个字节进行分组进行加密的,每次对一组数据加密需要运行多轮。而输入密钥的长度可以为128、192和256位,也就是16个字节、24个字节和32个字节,如果用户输入的密钥长度不是这几种长度,也会补成这几种长度。无论输入密钥是多少字节,加密还是以16字节的数据一组来进行的,密钥长度的不同仅仅影响加密运行的轮数。
可以用base64对参生的数组进行编码,然后在解码,这样不会像new String(byte[]),getBytes()那样造成数组前后不一致,一开始,我看到大部分人都是用base64,我也只是以为多一层编码看起来安全一些而已,没想到base64对数组的处理是不会造成误差的