AES算法是一种对称加密算法,加密和解密使用的是同一把密钥。常用的对称算法如下:
AES算法密钥长度是固定的,密钥长度直接决定加密强度,而工作模式和填充模式可以看成是对称加密算法的参数和格式选择。目前比较常见的工作模式是ECB和CBC。
ECB模式
ECB模式是最简单的加密模式,通过固定长度的秘钥,固定的明文会生成固定的密文。ECB模式的密钥长度是128位/16字节。
//AES + ECB
public class Demo01 {
public static void main(String[] args) throws GeneralSecurityException {
// 原文:
String message = "天生我材必有用飞流直下三千尺";
System.out.println("Message(原始信息): " + message);
// 128位密钥 = 16 bytes Key:
byte[] key = "1234567890abcdef".getBytes();
// 加密:
byte[] data = message.getBytes();
byte[] encrypted = encrypt(key, data);
System.out.println("Encrypted(加密内容): " + Base64.getEncoder().encodeToString(encrypted));
// 解密:
byte[] decrypted = decrypt(key, encrypted);
System.out.println("Decrypted(解密内容): " + new String(decrypted));
}
// 加密:
public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
// 创建密码对象,需要传入算法/工作模式/填充模式
Cipher cipher=Cipher.getInstance("AES/ECB/PKCS5Padding");//ECB/PKCSSPadding
// 根据key的字节内容,"恢复"秘钥对象
SecretKey keySpec=new SecretKeySpec(key, "AES");
// 初始化秘钥:设置加密模式ENCRYPT_
cipher.init(Cipher.ENCRYPT_MODE,keySpec);
// 根据原始内容(字节),进行加密
return cipher.doFinal(input);
}
// 解密:
public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
// 创建密码对象,需要传入算法/工作模式/填充模式
Cipher cipher=Cipher.getInstance("AES/ECB/PKCS5Padding");
// 根据key的字节内容,"恢复"秘钥对象
SecretKey keySpec=new SecretKeySpec(key,"AES");
// 初始化秘钥:设置解密模式DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, keySpec);
// 根据原始内容(字节),进行解密
return cipher.doFinal(input);
}
}
无论加密,解密首先要创建Cipher密码对象,并且需要传入算法/工作模式/填充模式 Cipher.getInstance("AES/ECB/PKCS5Padding")。
加密时创建好密码对象,要通过new SecretKeySpec(key, "AES")根据key的字节内容,"恢复"秘钥对象。然后调用init(Cipher.ENCRYPT_MODE,keySpec)初始化密钥,设置加密模式为ENCRYPT_MODE,最后调用doFinal()方法传入原始内容(字节),进行加密。
解密在创建好密码对象后,同样要调用new SecretKeySpec(key,"AES"),根据key的字节内容,"恢复"秘钥“对象。然后初始化密钥,设置解密模式,再调用doFina解密。
CBC模式
CBC模式加入了一个随机数作为IV参数,同样的明文每次生成的密文都不同。密钥长度为256位/32字节。IV参数是随机生成的16字节,所以每次加密后的密文不同。解密时需要使用同样的IV参数才能得到正确的解密结果。
//AES + CBC
public class Demo02 {
public static void main(String[] args) throws Exception {
// 原文:
String message = "Hello";
System.out.println("Message(原始信息): " + message);
// 256位密钥 = 32 bytes Key:
byte[] key = "1234567890abcdef1234567890abcdef".getBytes();
// 加密:
byte[] data = message.getBytes();
byte[] encrypted = encrypt(key,data);
System.out.println("Encrypted(加密内容): " +
Base64.getEncoder().encodeToString(encrypted));
// 解密:
byte[] decrypted = decrypt(key,encrypted);
System.out.println("Decrypted(解密内容): " + new String(decrypted));
}
// 加密:
public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
// 设置算法/工作模式CBC/填充
Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding");
// 恢复秘钥对象
SecretKey keyScpe=new SecretKeySpec(key, "AES");
// CBC模式需要生成一个16 bytes的initialization vector:
SecureRandom sr=SecureRandom.getInstanceStrong();
byte[] iv=sr.generateSeed(16);//生成16个字节的随机数
System.out.println("iv字节数组内容"+Arrays.toString(iv));
System.out.println("iv字节数组长度"+iv.length);
//随机数封装成IvParameterSpec参数对象
IvParameterSpec ivps=new IvParameterSpec(iv);
// 初始化秘钥:操作模式、秘钥、IV参数
cipher.init(Cipher.ENCRYPT_MODE, keyScpe,ivps);
// 加密
byte[] data=cipher.doFinal(input);
// IV不需要保密,把IV和密文一起返回:
return join(iv,data);
}
// 解密:
public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
// 把input分割成IV和密文:
byte[] iv=new byte[16];
byte[] data=new byte[input.length-16];
System.arraycopy(input,0,iv,0,16);//IV
System.arraycopy(input,16,data,0,data.length);//密文
// 解密:
Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKey keySpec=new SecretKeySpec(key, "AES");//恢复密钥
IvParameterSpec ivps=new IvParameterSpec(iv);//恢复iv
// 初始化秘钥:操作模式、秘钥、IV参数
cipher.init(Cipher.DECRYPT_MODE, keySpec,ivps);
// 解密操作
return cipher.doFinal(data);
}
// 合并数组
public static byte[] join(byte[] bs1, byte[] bs2) {
byte[] r=new byte[bs1.length+bs2.length];
System.arraycopy(bs1, 0, r, 0, bs1.length);
System.arraycopy(bs2, 0, r, bs1.length, bs2.length);
return r;
}
}
CBC模式比ECB模式多了,IvParameterSpec类,将随机数封装成IvParameterSpec参数对象,IV参数也要初始化。其他步骤与ECB模式一致。
区别:
1.CBC模式多了IV参数,安全程度也更高一些。
2.ECB密钥长度为128位(16字节),CBC密钥长度为256位(32字节)。