前言
随着互联网的飞速发展,信息安全变得尤为重要,在这里我给大家介绍几种常用的对称加密算法以及AES算法不同模式的使用案例。
一、对称加密算法简介
对称加密算法就是传统的用一个密钥进行加密和解密。例如:我们常用的WinZip和WinRAR对压缩包的加密和解密,就是使用的对称加密算法。
特点:算法公开,计算量小,加密速度快,加密效率搞。
不足点:交易双方都使用相同的钥匙,安全性得不到保证。
- 对称加密时加密/解密都必须使用相同的密钥
- 明文:指没有任何经过加密的信息
- 加密算法:对明文进行替换,变形等操作的步骤规则
- 密钥:明文转换成密文或将密文转换成明文的算法中输入的参数
- 密文:明文经过加密算法处理后的输出
- 解密算法:加密算法的逆向操作
在软件开发中,常见的对称加密算法有:
算法 | 密钥长度 | 工作模式 | 填充模式 |
---|---|---|---|
DES | 54/64 | ECB/CBC/PCBC/CTR/… | NoPadding/PKCS5Padding |
AES | 128/192/256 | ECB/CBC/PCBC/CTR/… | NoPadding/PKCS5Padding/PKCS7Padding |
IDEA | 128 | ECB | PKCS5Padding/PKCS7Padding |
密钥长度直接决定加密的强度,而工作模式和填充模式可以看成是对称加密算法的参数和格式选择。
最后,值得注意的是,DES算法因密钥太短,很容易被破解,所以现在已经不安全不怎么使用了。
二、AES加密
AES加密是目前应用最广泛的加密算法。比较常用的工作模式是ECB和CBC
1.ECB模式
最基本的加密模式,也就是通常理解的加密,相同的明文将永远加密成相同的密文,无初始向量,容易受到密码本重放攻击,一般情况下很少用。
使用ECB模式代码如下:
// AES + ECB
public class Work01 {
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");
// 根据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);
}
}
我自己定义了两个静态方法封装加密和解密的过程,要使用AES加密算法的ECB模式,得先通过Cipher.getInstance(“AES/ECB/PKCS5Padding”)方法传入算法,工作模式和填充模式参数,得到Cipher对象,
再通过SecretKey构造方法传入key和算法,得到keySpec对象,再用Cipher.init()方法初始化密钥,其中是加密还是解密就体现在Cipher.init()方法中第一个参数传入的是cipher.ENCRYPT_MODE还是cipher.DECRYPT_MODE,最终返回cipher.doFinal(input)方法,参数是要加密内容的字节数组。
注意:密钥必须是16个字节,在实际运用中不会写死密钥。
运行结果如图:
(由于我打印的加密内容做了Bease64编码转成字符串,所以打印出来的不是字节数组。)
2.CBC模式
CBC模式相较于ECB模式更安全,它需要一个随机数作为IV参数,这样对于同一份明文,每次输出都会有不同的密文。
使用CBC模式的代码如下:
//AES + CBC
public class Work02 {
public static void main(String[] args) throws Exception {
// 原文:
String message = "天王盖地虎";
System.out.println("Message(原始信息): " + message);
// 256位密钥 = 32 bytes Key:
byte[] key = "123456789qwertyuiopasdfghjklzxcv".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));
}
// 加密:
@SuppressWarnings("static-access")
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("生成的iv字节数组" + 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);
System.arraycopy(input, 16, data, 0, data.length);
System.out.println("解密后的iv字节数组" + Arrays.toString(iv));
// 解密:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivps = new IvParameterSpec(iv);
SecretKey keySpec = new SecretKeySpec(key, "AES");
// 初始化秘钥:操作模式、秘钥、IV参数
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivps);
// 解密操作
return cipher.doFinal(data);
}
// 合并数组
public static byte[] join(byte[] bs1, byte[] bs2) {
byte[] ret = new byte[bs1.length + bs2.length];
System.arraycopy(bs1, 0, ret, 0, bs1.length);
System.arraycopy(bs2, 0, ret, bs1.length, bs2.length);
return ret;
}
}
观察代码可以发现,与ECB模式不同的地方在于,加密时多生成了一个固定长度为16个字节的SecureRandom随机数iv,iv作为参数生成了IvParameterSpec对象ivps,在初始化时cipher.init()方法第三个参数放入了ivps。为了解码方便,我还写了一个join()方法,将iv的字节数组和加密后的字节数组合并,由于iv的字节数组长度是固定的,这样在解码时,可以很轻松的提取到加密时使用的iv和加密的字节数组,最终达成解密的效果。
运行结果如图:
总结
以上就是对称加密算法的基本内容了,我简单介绍了几种常用对称加密算法,以及AES算法不同模式的使用案例。