目录
一、什么是对称加密算法
对称加密算法就是用一个秘钥进行加密和解密。
从程序的角度看:
加密:一个函数,接收密码和明文,然后输出密文
解密:一个函数,接收密文和密码,然后输出明文
二、常用的对称加密算法
在软件开发的过程中,常用的对称加密算法有:
本篇博客以AES算法为例,实现对称加密。
三、AES算法
在AES算法中常用的是ECB与CBC两种工作模式,我们分别来介绍:
1、ECB工作模式
在ECB工作模式下,我们的秘钥是固定的,这里为了方便大家理解,加密与解密的过程使用方法来进行封装。在主函数里直接调用加密与解密的方法。
//原文
String message = "hello";
System.out.println("Message:" + message);
//128位秘钥 = 16 bytes key:
byte[] key = "987654321zxcvbnm".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("解密:" + new String(decrypted));
加密:
1.创建密码对象,需要传入算法名称/工作模式/填充模式,这里使用ECB工作模式
2.根据我们写入的16字节的秘钥,使用SecretKey来恢复秘钥(秘钥必须是指定长度的)
3.初始化秘钥,设置加密模式
//加密
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");
//初始化秘钥:设置解密模式
cipher.init(Cipher.DECRYPT_MODE, keySpec);
//根据原始内容(字节),进行加密
return cipher.doFinal(input);
}
2、CBC工作模式
CBC的算法步骤大致相同,与EBC最大的不同之处,就是CBC多了一个动态IV,一个随机数作为参数,相同的明文,每次产生的密文都不同。
加密:
与EBC相比多了一个需要使用SecureRandom 实例化一个对象,来生成一个16字节的随机数,把它存放在一个字节数组,将随机数封装成ParameterSpec对象。
在初始化秘钥的时候,把这个参数iv一起传进去。
最后在调用一个数组合并的方法将iv与传入的数据都返回出去
//加密
public static byte[] encrypt(byte[] key,byte[] input) throws GeneralSecurityException {
//创建密码对象,需要传入算法名称/CBC工作模式/填充模式
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//根据key的字节内容,“恢复”秘钥
SecretKey keySpec = new SecretKeySpec(key,"AES");
//CBC模式需要生成一个16bytes的initialization vector:
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] iv = sr.generateSeed(16); //生成16个字节的随机数
IvParameterSpec ivps = new IvParameterSpec(iv); //随机数封装成IvParameterSpec对象
//初始化秘钥:设置加密模式、秘钥、iv
cipher.init(Cipher.ENCRYPT_MODE, keySpec,ivps);
//进行加密
byte[] data = cipher.doFinal(input);
//IV不需要保密,把IV和密文一起返回
return join(iv, 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;
}
解密:
我们需要将输入的内容分割成IV和密文,创建两个字节数组分别来保存。
将分割出的iv封装成ParameterSpec对象ipvs,在秘钥初始化时,选择解密模式,连同秘钥一起传进去。
最后调用doFinal()方法进行解密。
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");
//根据key的字节内容,“恢复”秘钥
SecretKey keySpec = new SecretKeySpec(key,"AES");
IvParameterSpec ivps = new IvParameterSpec(iv);
//初始化秘钥:设置解密模式、秘钥、IV参数
cipher.init(Cipher.DECRYPT_MODE, keySpec,ivps);
//解密
return cipher.doFinal(data);
}
3、小结
两种工作模式,ECB模式采用固定秘钥,安全性略低,CBC模式使用动态随机数iv,来保证安全性更高,其实不论是ECB模式还是CBC模式,在进行解密与加密的过程中,步骤大致相似,只有在初始化秘钥是,选择的模式不同:加密使用的是==>Cipher.ENCRYPT_MODE,解密使用的是==>Cipher.DECRYPT_MODE,但自始至终使用的都是同一个秘钥,这就是对称加密算法。
四、非对称加密算法
非对称加密算法也叫RSA算法。
与对称加密算法不同的是,进行加密与解密使用的是不同的秘钥,只有一个公钥-私钥,秘钥对才可以正常的加解密。
例如:如果我向小王准备发送一个加密文件,我需要先得到小王的公钥进行加密,而在进行解密的过程中,只有小王的与之匹配的私钥才可以进行解密,除此之外,任何人都解不开。
非对称加密的优点: 对称加密需要协商密钥,而非对称加密可以安全地公开各自的公钥,在N个人之间通信的时候:使用非对称加密只需要N个密钥对,每个人只管理自己的密钥对。而使用对称加密需要则需要N(N-1)/2个密钥,因此每个人需要管理N 1个密钥,密钥管理难度大,而且非常容易泄漏。
非对称加密的缺点:运算速度非常慢,比对称加密要慢很多。
具体实现步骤如下:
为了方便我们使用,我们把加密与解密的过程封装在方法中。
第一步:我们需要一个用户类,有用户的姓名,公钥、私钥、及构造方法,传入当前对象的名字,以及实例化一个KeyPairGenerator 对象调用generateKeyPair()方法生成一对儿秘钥。
// 用户类
class Human {
// 姓名
String name;
// 私钥:
PrivateKey sk;
// 公钥:
PublicKey pk;
// 构造方法
public Human(String name) throws GeneralSecurityException {
// 初始化姓名
this.name = name;
// 生成公钥/私钥对:
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
kpGen.initialize(1024);
KeyPair kp = kpGen.generateKeyPair();
this.sk = kp.getPrivate();
this.pk = kp.getPublic();
}
}
第二步:把生成的秘钥对编码成字节
// 把私钥导出为字节
public byte[] getPrivateKey() {
return this.sk.getEncoded();
}
// 把公钥导出为字节
public byte[] getPublicKey() {
return this.pk.getEncoded();
}
第三步:使用公钥进行加密
1.使用Cipher 实例化对象声明使用的是RSA算法
2.使用公钥进行初始化
3.调用doFianl()方法进行加密,返回一个字节数组。
// 用公钥加密:
public byte[] encrypt(byte[] message) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, this.pk); // 使用公钥进行初始化(使用公钥加密)
return cipher.doFinal(message);
}
第四步:使用私钥进行解密
与加密不同的是,在传入参数时,传入的字节数组是加密后的密文字节数组,初始化时,使用的是私钥。
// 用私钥解密:
public byte[] decrypt(byte[] input) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, this.sk); // 使用私钥进行初始化 (使用私钥解密)
return cipher.doFinal(input);
}
在主函数调用想要的方法来完成:
值得注意的是,在完成对象创建时,就已经在构造方法中创建好了一对秘钥对(公钥---私钥)
public static void main(String[] args) throws Exception {
// 明文:
byte[] plain = "Hello, encrypt use RSA".getBytes("UTF-8");
// 创建公钥/私钥对:
Human alice = new Human("Alice");
// 用Alice的公钥加密:
// 获取Alice的公钥
byte[] pk = alice.getPublicKey();
// 使用公钥加密
byte[] encrypted = alice.encrypt(plain);
System.out.println(String.format("encrypted(加密): %x", new BigInteger(1, encrypted)));
// 用Alice的私钥解密:
// 获取Alice的私钥
byte[] sk = alice.getPrivateKey();
// 使用私钥解密
byte[] decrypted = alice.decrypt(encrypted);
System.out.println("解密:" + new String(decrypted, "UTF-8"));
}
五、对称加密算法与非对称加密算法的区别
1、对称加密算法不论是在加密还是解密的过程中,使用的都是同一个秘钥,这也体现了它的对称性,而非对称加密算法使用的是一对儿秘钥(公钥---私钥),加密过程使用的是公钥,解密过程中只有使用同一个公钥--私钥对才可以解开,体现了不对称性。
2.在安全性方面来看,非对称加密算法的安全性更高
3.从运行速度来看:非对称加密的运行速度比对称加密慢得多
对称加密算法 | 非对称加密算法 | |
秘钥 | 同一个秘钥 | 一对儿秘钥 |
安全性 | 较低 | 更高 |
运行速度 | 快 | 很慢 |