目录
一、AES算法
AES(Advanced Encryption Standard,高级加密标准)算法是目前应用最广泛的对称加密算法(对称加密算法就是加密和解密用同一个密钥),常见的两种工作模式是ECB和CBC。
二、ECB模式
ECB(Electronic Code Book Mode,电子密码本 )模式是最简单的AES加密模式,它需要一个固定长度的密钥,固定的明文会生成固定的密文。
使用ECB模式编写代码:
Ⅰ根据算法名称/工作模式/填充模式获取Cipher实例;
Ⅱ根据算法名称初始化一个SecretKey实例,密钥必须是指定长度(16位);
Ⅲ使用SerectKey初始化Cipher实例,并设置加密或解密模式;
Ⅳ传入明文或密文,获得密文或明文。
import java.security.GeneralSecurityException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
// AES + ECB
public class Test {
public static void main(String[] args) throws GeneralSecurityException {
// 原文
String message = "天生我材必有用千金散尽还复来";
System.out.println("Message(原始信息): " + 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));
//Encrypted(加密内容): XgGZgGfr0drZUONqCNeCUF6XZTxuvE3lgSRm/I/TiiA=
// 解密
byte[] decrypted = decrypt(key, encrypted);
System.out.println("Decrypted(解密内容): " + new String(decrypted));
//Decrypted(解密内容): 天生我材必有用千金散尽还复来
}
// 加密
public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
// 创建密码对象,需要传入算法/工作模式/填充模式
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
// 根据key的字节内容,"恢复"秘钥对象
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 {
// 创建密码对象,需要传入算法/工作模式/填充模式
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);
}
}
三、CBC模式
ECB模式是最简单的AES加密模式,这种一对一的加密方式会导致安全性降低。所以,更好的方式是通过CBC(Cipher Block Chaining Mode,密码分组链接)模式,它需要一个随机数作为IV参数,这样对于同一份明文,每次生成的密文都不同。
在CBC模式下,需要一个随机生成的16字节IV参数,必须使用SecureRandom生成。因为多了一个IvParameterSpec实例,因此,初始化方法需要调用Cipher的一个重载方法并传入IvParameterSpec。观察输出,可以发现每次生成的IV不同,密文也不同。
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
//AES + CBC
public class Demo02 {
public static void main(String[] args) throws Exception {
// 原文
String message = "风吹草低见牛羊";
System.out.println("Message(原始信息): " + message);
// 256位密钥 = 32 bytes Key:
byte[] key = "13759493089afgkh13759493089afgkh".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 KeySpec = new SecretKeySpec(key, "AES");
// CBC模式需要生成一个16 bytes的initialization vector:
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] iv = sr.generateSeed(16);
System.out.println(Arrays.toString(iv));
IvParameterSpec ivps = new IvParameterSpec(iv);
// 初始化秘钥:操作模式、秘钥、IV参数
cipher.init(Cipher.ENCRYPT_MODE, KeySpec,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); //密文
System.out.println(Arrays.toString(iv));
// 解密
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKey KeySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivps = new IvParameterSpec(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;
}
}
四、ECB和CBC模式的区别
ECB:是一种基础的加密方式,明文被分割成长度相等的块,然后单独一个个加密,一个个输出组成密文,ECB模式只进行了加密。
CBC:是一种更安全的加密方式,明文分组后,在加密之前会有一个随机向量IV与之结合,每次加密之前都会产生一个新的随机数序列,这样保证了加密算法的安全性。