目录
一、什么是AES算法
二、 AES算法的两种工作模式
1、ECB
2、CBC
总结:
在之前的文章中,我了解了哈希算法加密,但是它只能用于加密后进行验证。而在互通信息时不仅需要加密也需要解密,这就需要使用对称加密算法,即使用同一个密钥进行加密和解密的算法,而AES算法就是现在最常用对称加密算法。
算法 |
密钥长度 |
工作模式 |
填充模式 |
DES |
56/64 |
ECB/CBC/PCBC/CTR/... |
NoPadding/PKCS5Padding/... |
AES |
128/192/256 |
ECB/CBC/PCBC/CTR/... |
NoPadding/PKCS5Padding/PKCS7Padding/... |
IDEA |
128 |
ECB |
PKCS5Padding/PKCS7Padding/... |
密钥长度直接决定加密强度,而工作模式和填充模式可以看成是对称加密算法的参数和格式选择。Java标准库提供的算法实现并不包括所有的工作模式和所有填充模式。最后,值得注意的是,DES算法由于密钥过短,可以在短时间内被暴力破解,所以现在已经不安全了。
ECB模式是最简单的AES加密模式,它需要一个固定长度的密钥,固定的明文会生成固定的密文。在了解具体实现前,我们需要了解一下算法流程:
// 加密:
public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
// 创建密码对象,需要传入算法/工作模式/填充模式
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
// 根据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);
}
// 原文:
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));
小结:ECB工作模式是一种一对一的简单加密模式。
init()方法进行密钥初始化操作,选择加密或解密。
doFinal()方法对信息进行加密或解密操作。
因为ECB工作模式的一对一方式安全性较低,所以,可选择需要一个随机数作为IV参数的CBC模式,这样对于同一份明文,每次生成的密文都不同安全性大大提升。
首先来了解一下加密操作:
// 加密:
public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
// 设置算法/工作模式CBC/填充
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// 恢复秘钥对象
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
// CBC模式需要生成一个16 bytes的initialization vector:
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] iv = sr.generateSeed(16);
System.out.println("iv内容:" + Arrays.toString(iv));
System.out.println("iv长度:" + iv.length);
//随机数封装成参数对象
IvParameterSpec ivps = new IvParameterSpec(iv);
// 初始化秘钥:操作模式、秘钥、IV参数
cipher.init(Cipher.ENCRYPT_MODE, keySpec,ivps);
// 加密
byte[] data = cipher.doFinal(input);
// IV不需要保密,把IV和密文一起返回:
return join(iv,data);
}
在流程与方法名称上CBC与ECB并没有什么明显区别,但在init()方法的参数中和可以看到ivps参数,即iv参数对象,与密钥和原文一起加密。所以当我们解密时也需要相同的iv参数进行解密,所以需要我们将iv参数一并给需要解密的一方,所有调用join()方法及逆行拼接。
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;
}
解密操作:
// 解密:
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");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivps = new IvParameterSpec(iv);
// 初始化秘钥:操作模式、秘钥、IV参数
cipher.init(Cipher.DECRYPT_MODE,keySpec,ivps);
// 解密操作
return cipher.doFinal(data);
}
因为加密后将IV参数和密文放在了一起,所以我们首先要进行字节数组的拆分,将两者分离开。
之后操作与加密操作并无区别,只需注意操作模式的选择即可。
注意:在我们进行数据合并时,应该将固定长度的iv参数放在数组前面,因为,不同的原始信息产生的密文是不同长度的。
AES算法是一种常用的对称加密算法。
ECB工作模式是一对一的简单加密方式,优点就是简单,缺点是安全性较低。
CBC工作模式则是需要一个iv参数的针对相同原始信息,可以生成不同密文相对更安全的模式。