先贴代码:重要的参数都有注释,自己摸索的,有不对的地方大家指正
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
*
* @ClassName: DESUtil
* @Description: 加解密工具类
* @author: panpan
* @date: 2019年1月29日 下午5:01:48
*
*/
public class AESUtil {
private static final String CHARSET_NAME = "UTF-8";
public static String encode(String plainText, String key, String vi) {
try {
// 根据字符串key生成 Cipher 初始化使用的 secretKey
SecretKey secretKey = new SecretKeySpec(key.getBytes(CHARSET_NAME),"AES");
// 获取Cipher实例,指定加密方法、加密模式、补位模式
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// 加密器初始化,参数分别是: 加密器编码模式(加密或解密)、秘钥、加密向量
cipher.init(Cipher.ENCRYPT_MODE, secretKey,
new IvParameterSpec(vi.getBytes()));
// cipher 可以 add 但是最终需要使用 doFinal结束,返回byte[],这里通过base64编码成字符串
return java.util.Base64.getEncoder().encodeToString(cipher.doFinal(plainText.getBytes(CHARSET_NAME)));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String decode(String encryptText, String key, String vi) {
try {
SecretKey secretKey = new SecretKeySpec(key.getBytes(CHARSET_NAME),"AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey,
new IvParameterSpec(vi.getBytes()));
return new String(cipher.doFinal(java.util.Base64.getDecoder().decode(encryptText)), CHARSET_NAME);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
String vi = "f4CAHEW85Daszbwi"; // 16字节加密向量
String message = "Uibn1aICzX"; // 任意大小需要加密的文字
String key = "JvOybH9j7Qs0wSxi"; // 128bit、16字节秘钥
String encryptedCode = encode(message,key,vi);
System.out.println(encryptedCode);
String decryptedCode = decode(encryptedCode,key,vi);
System.out.println(decryptedCode);
}
}
首先第一位的是 JAVA API:
http://www.matools.com/api/java8
需要注意的是,获取Cipher实例时,可以选择很多加密模式,这是写这个工具类的核心。
1、加密方法有: AES、DES、DESede、RSA
2、加密模式有: CBC、ECB,这两种是JDK中提供的实例有的,如果你需要写其他的加密模式,如:CFB、OFB
可以参考这篇文章的原理:
当然,想要demo的话,这里有一个CFB的例子:
3、最后一个参数: Padding or not: 是否补位,这个初次接触加密的童鞋可能有点懵,说实话今早我刚刚看到这个的时候也有点懵逼,后来参考了 这篇文章讲述的原理,有点明白了,其实AES、DES在加密时,是以 8 bit 作为一个单元的,如果不满 8 bit,如果你选择了 PKCS5Padding,则会给你补位,CFB中那位老哥写的demo,使用的是 NoPadding,但是自己在代码了撸了补位的操作,具体为什么他不使用工具里自带的补位,没有细细研究。
4、还有一点,括号中的数字,说的是你的 加密key需要的位数,也是 AES加密快的大小
引用一个图
比如我使用 AES/CBC/PKCS5Padding,官方文档中说要 128 bit,即 key需要 128/8 = 16 字符的字符串,
如果你的位数不满 16 位,就会报以下错误:
java.lang.RuntimeException: java.security.InvalidKeyException: Invalid AES key length: 17 bytes
接下来的一个核心,是Cipher加密器的初始化:
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(vi.getBytes()));
1、加密器模式: ENCRYPT_MODE是加密 DECRYPT_MODE是解密
2、 secretKey 是秘钥,当然你不能直接把直接放字符串秘钥,得使用JAVA提供的秘钥生成器,并制定加密方式,因为不同加密方式,使用的key构成是不一样的,并且秘钥的大小也不同,重点需要特别注意。
3、 加密向量,注意 上面代码里采用的 AES/CBC/PKCS5Padding 需要 16字节128bit的加密向量,否则则会报错:
java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long
加密向量干啥用的嘞? 大家可以回想一下大学学得 线性代数,加密的每个明文模块、秘钥模块、密文模块其实都是一个矩阵,这里的加密向量其实和线代里的向量应该是一致的。
再借用一张图
大概原理差不多是: 明文矩阵 X 秘钥矩阵 X 基础向量 = 明文矩阵