一、对称加密算法
从程序的角度看,所谓加密,就是这样一个函数,它接收密码和明文,然后输出密文:secret = encrypt(key, message);
而解密则相反,它接收密码和密文,然后输出明文:plain = decrypt(key, secret);
常用的对称加密算法:
算法 | 密钥长度 | 工作模式 | 填充模式 |
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 |
二、AES加密
AES是目前应用最广泛的加密算法,本文将介绍最常用的CBC和ECB工作模式。
ECB模式
Electronic codebook,即电子密码本。需要将待加密消息按照对应算法分组的要求分成若干块,并对每个块进行独立加密。
// 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 keySec=new SecretKeySpec(key, "AES");
// 初始化秘钥:设置加密模式ENCRYPT_
cipher.init(Cipher.ENCRYPT_MODE, keySec);
// 根据原始内容(字节),进行加密
return cipher.doFinal(input);
}
// 解密:
public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
// 创建密码对象,需要传入算法/工作模式/填充模式
Cipher cipher=Cipher.getInstance("AES/ECB/PKCS5Padding");
// 根据key的字节内容,"恢复"秘钥对象
SecretKey keySec=new SecretKeySpec(key, "AES");
// 初始化秘钥:设置解密模式DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, keySec);
// 根据原始内容(字节),进行解密
return cipher.doFinal(input);
}
}
运行结果:
Message(原始信息): 天生我材必有用飞流直下三千尺
Encrypted(加密内容): a8MJCOSonzcdWEZ6suh6kLbiHdf5K1SQxuwZHrUJNdM=
Decrypted(解密内容): 天生我材必有用飞流直下三千尺
优点:ECB模块可以并行处理数据。
缺点:同样原文生成同样的密文,并不能很好地保护数据。
同时加密,如果原文一样,加密出来的密文也是一样的,因为它们的加密密钥key都是一样的。
CBC工作模式
CBC模式的全称:Cipher Block Chaining(密文分组链接模式),之所以叫这个名字,是因为密文分组像链条一样互相连接在一起。
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 Work02 {
public static void main(String[] args) throws Exception {
// 原文:
String message = "hello,world!";
System.out.println("Message(原始信息): " + message);
// 256位密钥 = 32 bytes Key:
byte[] key = "1234567890qwerty1234567890abcdef".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 keySec=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
IvParameterSpec ivps=new IvParameterSpec(iv);
// 初始化秘钥:操作模式、秘钥、IV参数
cipher.init(Cipher.ENCRYPT_MODE, keySec,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"); // 密码对象
SecretKeySpec 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;
}
}
运行结果:
Message(原始信息): hello,world!
iv字节数组内容[-35, 91, -101, 102, 22, -32, -22, 1, -49, 90, -64, -42, -111, 27, 115, -60]
iv字节数组长度16
Encrypted(加密内容): 3VubZhbg6gHPWsDWkRtzxAh1CN7Ly9663FCeN9HQNbg=
[-35, 91, -101, 102, 22, -32, -22, 1, -49, 90, -64, -42, -111, 27, 115, -60]
Decrypted(解密内容): hello,world!
优点:同样的原文生成的密文不一样。
缺点:串行处理数据,使得加密速度很慢。