1.概述:AES算法是对称加密算法,对称加密算法是指在加密和解密过程中使用同一把秘钥进行操作,常用的对称加密算法有:AES算法,DES算法以及IDEA算法,AES算法分为ECB和CBC两种工作模式。
2.ECB工作模式
ECB工作模式使用秘钥长度为16bytes(即128bits)。
class AESCode{
private static Cipher cipher;
static {
try {
//根据算法名称创建Cipher对象
cipher=Cipher.getInstance("AES/ECB/PKCS5Padding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
}
public static byte[] encrypt(byte[] key,byte[] input) throws GeneralSecurityException{
//恢复密钥对象
SecretKey keySpec=new SecretKeySpec(key, "AES");
//设置为加密操作并将密钥对象传入
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
//执行加密操作,将加密结果返回
return cipher.doFinal(input);
}
public static byte[] decrypt(byte[] key,byte[] input) throws GeneralSecurityException {
SecretKey keySpec=new SecretKeySpec(key, "AES");
//设置为解密操作并将密钥传入
cipher.init(Cipher.DECRYPT_MODE, keySpec);
//执行解密操作,返回解密结果
return cipher.doFinal(input);
}
}
在上面的代码中,我们将使用AES算法进行加密和解密的操作封装到类中,首先,我们需要用到Cipher类,然后根据使用的算法名称/工作模式/填充模式创建出cipher对象,在操作中,不能直接将秘钥的字节数组传入,需要根据字节数组创建出SecretKey秘钥对象,然后调用cipher对象的init()方法设置加密/解密操作和秘钥,最后调用doFinal()方法执行操作。
public static void main(String[] args) throws GeneralSecurityException {
//秘钥的字节数组
byte[] key="1234567891012oiu".getBytes();
//需要加密的明文
byte[] input="大王叫我来巡山".getBytes();
//加密后的字节数组
byte[] encrypted=AESCode.encrypt(key, input);
System.out.println("解密结果:"+Base64.getEncoder().encodeToString(encrypted));
//解密后的字节数组
byte[] decrypted=AESCode.decrypt(key, encrypted);
System.out.println("解密结果:"+new String(decrypted));
}
运行结果:
将上面的代码不管执行多少次,只要不修改秘钥和明文的内容,得到的加密结果都是一致的,这样就使得安全性降低,在下面的CBC工作模式中,就会在每次加密时根据随机产生的IV参数使得相同的秘钥和明文,每次加密得到的结果都是不一样的。
3.CBC工作模式
CBC工作模式使用秘钥长度为32bytes(256bits),并且还需要一个随机的IV参数使得每次加密的结果不同。
class AESCode{
private static Cipher cipher;
static {
try {
cipher=Cipher.getInstance("AES/CBC/PKCS5Padding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
}
public static byte[] encrypt(byte[] key,byte[] input) throws GeneralSecurityException{
SecretKey keySpec=new SecretKeySpec(key, "AES");
SecureRandom rand=SecureRandom.getInstanceStrong();
//随机产生的16个字节的字节数组
byte[] ivParam=rand.generateSeed(16);
//根据字节数组创建IV参数对象
IvParameterSpec iv=new IvParameterSpec(ivParam);
cipher.init(Cipher.ENCRYPT_MODE, keySpec,iv);
byte[] data=cipher.doFinal(input);
return join(ivParam,data);
}
public static byte[] decrypt(byte[] key,byte[] input) throws GeneralSecurityException {
//根据秘钥字节数组恢复密钥对象
SecretKey keySpec=new SecretKeySpec(key, "AES");
byte[] ivParam=Arrays.copyOfRange(input, 0, 16);
byte[] data=Arrays.copyOfRange(input, 16, input.length);
//根据iv参数字节数组恢复iv参数对象
IvParameterSpec iv=new IvParameterSpec(ivParam);
cipher.init(Cipher.DECRYPT_MODE, keySpec,iv);
return cipher.doFinal(data);
}
public static byte[] join(byte[] iv,byte[] data) {
byte[] rt=new byte[iv.length+data.length];
System.arraycopy(iv, 0, rt, 0, iv.length);
System.arraycopy(data, 0, rt, iv.length, data.length);
return rt;
}
}
上面的代码为CBC工作模式下进行的操作,与ECB工作模式相差不多,只是在加密和解密中添加了随机的IvParameterSpec对象参数,因为在加密时使用到了iv参数,所以在解密时需要使用到相同的iv参数,否则无法得到正确的解密结果。
public static void main(String[] args) throws GeneralSecurityException {
//32个字节的秘钥
byte[] key="1234567891012oiuqwertyuio1234567".getBytes();
byte[] input="大王叫我来巡山".getBytes();
byte[] encrypted=AESCode.encrypt(key, input);
System.out.println("加密结果:"+Base64.getEncoder().encodeToString(encrypted));
byte[] decrypted=AESCode.decrypt(key, encrypted);
System.out.println("解密结果:"+new String(decrypted));
}
运行结果:
上面的两次运行结果可以看出在CBC工作模式下,相同的明文和秘钥由于随机的iv参数不同,每次的加密结果不同,相比于ECB工作模式,安全性得到了提高。
4.ECB和CBC的区别
ECB工作模式和CBC工作模式的加密和解密的操作流程大致相同,只不过CBC工作模式会在加密的时候传入IvParameterSpec的随机参数使得每次的解密结果不同。
工作模式 | 不同点 |
---|---|
ECB工作模式 | 秘钥长度为16bytes 没有Iv参数 |
CBC工作模式 | 秘钥长度为32bytes 有Iv参数 |