对称加密算法
对称加密算法就是传统的用一个密码进行加密和解密;例如,我们常用的WinZIP和WinRAR对压缩包的加密和解密,就是使用对称加密算法。
常用的对称加密算法
算法
|
密钥长度
|
工作模式
|
DES(
版本太老)
|
56/64
|
ECB/CBC/PCBC/CTR/...
|
AES
|
128/192/256
|
ECB/CBC/PCBC/CTR/...
|
IDEA
|
128
|
ECB
|
使用AES/
ECB模式(没有IV参数)
核心代码
加密步骤
- 设置算法/工作模式/ECB/填充模式
- 根据key的字节内容,恢复秘钥对象
- 初始化密钥:设置加密模式
- 根据原始内容字节进行加密:返回的是字节数组
解密步骤
- 设置算法/工作模式/ECB/填充模式
- 根据key的字节内容,恢复秘钥对象
- 初始化密钥:设置解密模式
- 根据加密内容字节进行解密:
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
//ECB模式
public class Test {
public static void main(String[] args) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
//原文
String message = "Hello";
//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 IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
//创建密码对象:需要传入算法/工作模式/填充模式
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
//根据key的字节内容,“恢复”秘钥对象
SecretKey keySpec = new SecretKeySpec(key,"AES");
//初始化秘钥:设置加密模式ENCRYPT_MOD
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
//根据原始内容(字节)进行解密
return cipher.doFinal(input);
}
//解密
public static byte[] decrypt(byte[] key,byte[] input) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
//创建密码对象:需要传入算法/工作模式/填充模式
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
//根据key的字节内容,“恢复”秘钥对象
SecretKey keySpec = new SecretKeySpec(key,"AES");
//初始化秘钥:设置加密模式DECRYPT_MOD
cipher.init(Cipher.DECRYPT_MODE, keySpec);
//根据原始内容(字节)进行解密
return cipher.doFinal(input);
}
}
使用AES CBC模式(带有IV参数) 此处的IV参数相当于对密钥进行"加盐",步骤与ECB模式大致相同
。不同的是CBC模式需要生成一个16 bytes的IV,随后在初始化密钥时将IV设置进去;解密时 我们首先得拿到他的IV字节内容,根据IV字节内容恢复IV,同样在初始化密钥时将IV设置进去。
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Test {
public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
//原文
String message = "Hello";
//256位密钥 =32 bytes key:
byte[] key = "1234567890abcdef1234567890abcdef".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 NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
//设置算法/工作模式CBC/填充
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//恢复密钥对象
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
//CBC模式需要生成一个16bytes的initialization vector
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] iv = sr.generateSeed(16);//生成16个字节的随机数
System.out.println(Arrays.toString(iv));
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[] decrypt(byte[] key,byte[] input) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
//把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;
}
}
非对称加密算法
非对称加密算法使用一组密钥对进行对文件加密解密,也就是说加密和解密使用的不是相同的密钥,当我们想让A向B发送加密文件时,A首先需要将自己的公钥发送给B,B使用A的公钥对文件进行加密,随后将加密文件发送给A,A可以通过自己私钥对文件进行解密。
步骤实现
- 首先必须生成一个密钥对
- 使用公钥加密
- 使用私钥解密
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import javax.crypto.Cipher;
// RSA
public class Test {
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();
System.out.println(String.format("public key(公钥): %x", new BigInteger(1, pk)));
// 使用公钥加密
byte[] encrypted = alice.encrypt(plain);
System.out.println(String.format("encrypted: %x", new BigInteger(1, encrypted)));
// 用Alice的私钥解密:
// 获取Alice的私钥,并输出
byte[] sk = alice.getPrivateKey();
System.out.println(String.format("private key: %x", new BigInteger(1, sk)));
// 使用私钥解密
byte[] decrypted = alice.decrypt(encrypted);
System.out.println(new String(decrypted, "UTF-8"));
}
}
// 用户类
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();
}
// 用公钥加密:
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);
}
}
对称式加密与非对称式加密的区别
1、对称加密中加密和解密使用的秘钥是同一个;非对称加密中采用两个密钥,一般使用公钥进行加密,私钥进行解密。
2、对称加密解密的速度比较快,非对称加密和解密花费的时间长、速度相对较慢。
3、对称加密的安全性相对较低,非对称加密的安全性较高。