一、常见加密方式
1、对称加密
采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。
(1)示例
- 我们现在有一个原文3要发送给B
- 设置密钥为108, 3 * 108 = 324, 将324作为密文发送给B
- B拿到密文324后, 使用324/108 = 3 得到原文
(2)常见加密算法
DES
Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。
AES
Advanced Encryption Standard, 高级加密标准 .在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
(3)特点
- 加密速度快, 可以加密大文件
- 密文可逆, 一旦密钥文件泄漏, 就会导致数据暴露
- 加密后编码表找不到对应字符, 出现乱码
- 一般结合Base64使用
二、DES 加解密
示例代码 des加密算法
Cipher :文档 https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html#getInstance-java.lang.String-
使用 DES 进行加密:
public class DESDemo {
public static void main(String[] args) throws Exception {
// 原文
String input = "硅谷";
// 定义密钥,des加密必须是8位
String key = "123456";
// 定义加密算法
String algorithm = "DES";
//定义加密类型
String transformation = "DES";
// Cipher:密码,获取加密对象
// transformation:参数表示使用什么类型加密
Cipher cipher = Cipher.getInstance(transformation);
// 指定秘钥规则
// 第一个参数表示:密钥,key的字节数组
// 第二个参数表示:算法
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// 对加密进行初始化
// 第一个参数:表示模式,有加密模式和解密模式
// 第二个参数:表示秘钥规则
cipher.init(Cipher.ENCRYPT_MODE, sks);
// 进行加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 打印字节,因为ascii码有负数,解析不出来,所以乱码
for (byte b : bytes) {
System.out.println(b);
}
// 打印密文
System.out.println(new String(bytes));
}
}
运行:
修改密钥 key = "12345678" ,再次运行,出现乱码是因为对应的字节出现负数,但负数,没有出现在 ascii 码表里面,所以出现乱码,需要配合base64进行转码
使用 base64 进行编码
base64 导包的时候,需要注意 ,别导错了,需要导入 apache 包
public class DESDemo {
public static void main(String[] args) throws Exception {
// 原文
String input = "硅谷";
// 定义密钥,des加密必须是8位
String key = "12345678";
// 定义加密算法
String algorithm = "DES";
//定义加密类型
String transformation = "DES";
// Cipher:密码,获取加密对象
// transformation:参数表示使用什么类型加密
Cipher cipher = Cipher.getInstance(transformation);
// 指定秘钥规则
// 第一个参数表示:密钥,key的字节数组
// 第二个参数表示:算法
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// 对加密进行初始化
// 第一个参数:表示模式,有加密模式和解密模式
// 第二个参数:表示秘钥规则
cipher.init(Cipher.ENCRYPT_MODE, sks);
// 进行加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 打印字节,因为ascii码有负数,解析不出来,所以乱码
for (byte b : bytes) {
System.out.println(b);
}
// 打印密文
System.out.println(new String(bytes));
//对数据进行 Base64 编码
String encode = Base64.encode(bytes);
System.out.println("Base64 编码为:" + encode);
}
}
再次运行:
使用 DES 解密
public class DESDemo {
// DES加密算法,key的大小必须是8个字节
public static void main(String[] args) throws Exception {
// 原文
String input = "硅谷";
// 定义密钥,des加密必须是8位
String key = "12345678";
//定义加密类型
String transformation = "DES";
// 指定获取密钥的算法
String algorithm = "DES";
String encryptDES = encryptDES(input, key, transformation, algorithm);
System.out.println("加密:" + encryptDES);
String decryptDES = decryptDES(encryptDES, key, transformation, algorithm);
System.out.println("解密: " + decryptDES);
}
/**
*
* 使用DES加密数据
*
* @param input 原文
* @param key 密钥(DES,密钥的长度必须是8个字节)
* @param transformation 获取Cipher对象的算法
* @param algorithm 获取密钥的算法
* @return 密文
* @throws Exception
*/
private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception, BadPaddingException {
// Cipher:密码,获取加密对象
// transformation:参数表示使用什么类型加密
Cipher cipher = Cipher.getInstance(transformation);
// 指定秘钥规则
// 第一个参数表示:密钥,key的字节数组
// 第二个参数表示:算法
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// 对加密进行初始化
// 第一个参数:表示模式,有加密模式和解密模式
// 第二个参数:表示秘钥规则
cipher.init(Cipher.ENCRYPT_MODE, sks);
// 进行加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 打印字节,因为ascii码有负数,解析不出来,所以乱码
//for (byte b : bytes) {
// System.out.println(b);
//}
// 打印密文
//System.out.println(new String(bytes));
//对数据进行 Base64 编码
String encode = Base64.encode(bytes);
return encode;
}
/**
* 使用DES解密
*
* @param input 密文
* @param key 密钥
* @param transformation 获取Cipher对象的算法
* @param algorithm 获取密钥的算法
* @return 原文
* @throws Exception
*/
private static String decryptDES(String input, String key, String transformation, String algorithm) throws Exception {
//1.获取 Cipher 对象
Cipher cipher = Cipher.getInstance(transformation);
//2.指定密钥规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
cipher.init(Cipher.DECRYPT_MODE, sks);
//3. 解密,上面使用的base64编码,下面直接用密文
byte[] bytes = cipher.doFinal(Base64.decode(input));
// 因为是明文,所以直接返回
return new String(bytes);
}
}
运行程序:
三、Base64
1、Base64 算法简介
- Base64是网络上最常见的用于传输8Bit字节码的可读性编码算法之一
- 可读性编码算法不是为了保护数据的安全性,而是为了可读性
- 可读性编码不改变信息内容,只改变信息内容的表现形式
- 所谓Base64,即是说在编码过程中使用了64种字符:大写A到Z、小写a到z、数字0到9、“+”和“/”
- Base58是Bitcoin(比特币)中使用的一种编码方式,主要用于产生Bitcoin的钱包地址 相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"i",以及"+"和"/"符号
2、Base64 算法原理
base64 是3个字节为一组,一个字节8位,一共就是24位,然后,把3个字节转成4组,每组6位。
3 * 8 = 4 * 6 = 24 ,每组6位,缺少的2位,会在高位进行补0,这样做的好处在于 ,base取的是后面6位,去掉高2位 ,那么base64的取值就可以控制在0-63位了,所以就叫base64,111 111 = 32 + 16 + 8 + 4 + 2 + 1 = 64
3、Base64 构成原则
① 小写 a - z = 26个字母
② 大写 A - Z = 26个字母
③ 数字 0 - 9 = 10 个数字
④ + / = 2个符号
大家可能发现一个问题,咱们的base64有个 = 号,但是在映射表里面没有发现 = 号 , 这个地方需要注意,等号非常特殊,因为base64是三个字节一组 ,如果当我们的位数不够的时候,会使用等号来补齐
4、Base64补等号测试
Demo:
public class Base64Demo {
public static void main(String[] args) {
// 1:MQ== 表示一个字节,不够三个字节,所以需要后面通过 == 号补齐
System.out.println(Base64.encode("1".getBytes()));
System.out.println(Base64.encode("12".getBytes()));
System.out.println(Base64.encode("123".getBytes()));
// 硅谷:中文占6个字节,6 * 8 = 48 ,刚刚好被整除,所以没有等号
System.out.println(Base64.encode("硅谷".getBytes()));
}
}
运行结果:
下面以字符 “1” 说明:
字符:1
对应的ASCII:49
对应的二进制:00110001
转换前:00110001 00000000 00000000(不够3个字节的补0,补了几个字节最后的编码就有几个=号)
转换后:00001100 00010000 00000000 00000000(每一个前面加两个0)
换算成对应的10进制:12 16 0 0
在参考base64的码表得到最终的值为:MQ==
四、AES加解密
AES 加密解密和 DES 加密解密代码一样,只需要修改加密算法就行,拷贝 ESC 代码,修改加密方式为AES:
public class AESDemo {
// AES加密算法,key的大小必须是8个字节
public static void main(String[] args) throws Exception {
// 原文
String input = "硅谷";
// 定义密钥,AES加密算法,比较高级,所以key的大小必须是16个字节
String key = "12345678";
//定义加密类型
String transformation = "AES";
// 指定获取密钥的算法
String algorithm = "AES";
String encryptDES = encryptAES(input, key, transformation, algorithm);
System.out.println("加密:" + encryptDES);
String decryptDES = decryptAES(encryptDES, key, transformation, algorithm);
System.out.println("解密: " + decryptDES);
}
/**
*
* 使用AES加密数据
*
* @param input 原文
* @param key 密钥(AES,密钥的长度必须是8个字节)
* @param transformation 获取Cipher对象的算法
* @param algorithm 获取密钥的算法
* @return 密文
* @throws Exception
*/
private static String encryptAES(String input, String key, String transformation, String algorithm) throws Exception, BadPaddingException {
// Cipher:密码,获取加密对象
// transformation:参数表示使用什么类型加密
Cipher cipher = Cipher.getInstance(transformation);
// 指定秘钥规则
// 第一个参数表示:密钥,key的字节数组
// 第二个参数表示:算法
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// 对加密进行初始化
// 第一个参数:表示模式,有加密模式和解密模式
// 第二个参数:表示秘钥规则
cipher.init(Cipher.ENCRYPT_MODE, sks);
// 进行加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 打印字节,因为ascii码有负数,解析不出来,所以乱码
//for (byte b : bytes) {
// System.out.println(b);
//}
// 打印密文
//System.out.println(new String(bytes));
//对数据进行 Base64 编码
String encode = Base64.encode(bytes);
return encode;
}
/**
* 使用AES解密
*
* @param input 密文
* @param key 密钥
* @param transformation 获取Cipher对象的算法
* @param algorithm 获取密钥的算法
* @return 原文
* @throws Exception
*/
private static String decryptAES(String input, String key, String transformation, String algorithm) throws Exception {
//1.获取 Cipher 对象
Cipher cipher = Cipher.getInstance(transformation);
//2.指定密钥规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
cipher.init(Cipher.DECRYPT_MODE, sks);
//3. 解密,上面使用的base64编码,下面直接用密文
byte[] bytes = cipher.doFinal(Base64.decode(input));
// 因为是明文,所以直接返回
return new String(bytes);
}
}
运行程序:AES 加密的密钥key , 需要传入16个字节
再运行程序:
五、toString()与new String ()用法区别
举例:
public class StringDemo {
public static void main(String[] args) {
String str="TU0jV0xBTiNVYys5bEdiUjZlNU45aHJ0bTdDQStBPT0jNjQ2NDY1Njk4IzM5OTkwMDAwMzAwMA==";
String rlt1=new String(Base64.decode(str));
String rlt2=Base64.decode(str).toString();
System.out.println(rlt1);
System.out.println(rlt2);
}
}
运行结果:
哪一个是正确的?为什么?
这里应该用new String()的方法,因为Base64加解密是一种转换编码格式的原理
toString()与 new String ()用法区别
str.toString是调用了这个object对象的类的toString方法。一般是返回这么一个String:[class name]@[hashCode]
new String(str)是根据parameter是一个字节数组,使用java虚拟机默认的编码格式,将这个字节数组decode为对应的字符。若虚拟机默认的编码格式是ISO-8859-1,按照ascii编码表即可得到字节对应的字符。
什么时候用什么方法呢?
new String()一般使用字符转码的时候,byte[]数组的时候
toString()对象打印的时候使用
六、加密模式
加密模式:https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html
1、ECB
ECB : Electronic codebook, 电子密码本. 需要加密的消息按照块密码的块大小被分为数个块,并对每个块进行独立加密
- 优点 : 可以并行处理数据
- 缺点 : 同样的原文生成同样的密文, 不能很好的保护数据
- 同时加密,原文是一样的,加密出来的密文也是一样的
2、CBC
CBC : Cipher-block chaining, 密码块链接. 每个明文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块
- 优点 : 同样的原文生成的密文不一样
- 缺点 : 串行处理数据.
七、填充模式
当需要按块处理的数据, 数据长度不符合块处理需求时, 按照一定的方法填充满块长的规则
NoPadding
- 不填充
- 在DES加密算法下, 要求原文长度必须是8byte的整数倍
- 在AES加密算法下, 要求原文长度必须是16byte的整数倍
PKCS5Padding
- 数据块的大小为8位, 不够就补足
Tips
- 默认情况下, 加密模式和填充模式为 : ECB/PKCS5Padding
- 如果使用CBC模式, 在初始化Cipher对象时, 需要增加参数, 初始化向量IV :
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
加密模式和填充模式
AES/CBC/NoPadding (128)
AES/CBC/PKCS5Padding (128)
AES/ECB/NoPadding (128)
AES/ECB/PKCS5Padding (128)
DES/CBC/NoPadding (56)
DES/CBC/PKCS5Padding (56)
DES/ECB/NoPadding (56)
DES/ECB/PKCS5Padding (56)
DESede/CBC/NoPadding (168)
DESede/CBC/PKCS5Padding (168)
DESede/ECB/NoPadding (168)
DESede/ECB/PKCS5Padding (168)
RSA/ECB/PKCS1Padding (1024, 2048)
RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048)
RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048)
加密模式和填充模式例子
(1)使用 DES 方式,使用 ECB 加密方式
默认情况下ECB/PKCS5Padding就是默认值
public class DESPaddingDemo {
// DES加密算法,key的大小必须是8个字节
public static void main(String[] args) throws Exception{
String input = "硅谷";
//DES加密算法,key的大小必须是8个字节
String key = "12345678";
//指定获取Cipher的算法,如果没有指定加密模式和填充模式,ECB/PKCS5Padding就是默认值
//String transformation = "DES"; 密文:qANksk5lvqM=
//String transformation = "DES/ECB/PKCS5Padding"; //qANksk5lvqM=
String transformation = "DES/ECB/PKCS5Padding";
// 指定获取密钥的算法
String algorithm = "DES";
String encryptDES = encryptDES(input, key, transformation, algorithm);
System.out.println("DES加密:" + encryptDES);
String decryptDES = decryptDES(encryptDES, key, transformation, algorithm);
System.out.println("DES解密:" + decryptDES);
}
/**
* 使用DES解密
*
* @param encryptDES 密文
* @param key 密钥
* @param transformation 获取Cipher对象的算法
* @param algorithm 获取密钥的算法
* @return 原文
*/
private static String decryptDES(String encryptDES, String key, String transformation, String algorithm) throws Exception{
// 1,获取Cipher对象
Cipher cipher = Cipher.getInstance(transformation);
// 指定密钥规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
cipher.init(Cipher.DECRYPT_MODE, sks);
// 3. 解密
byte[] bytes = cipher.doFinal(Base64.decode(encryptDES));
return new String(bytes);
}
/**
* 使用DES加密数据
* @param input 原文
* @param key 密钥(DES,密钥的长度必须是8个字节)
* @param transformation 获取Cipher对象的算法
* @param algorithm 获取密钥的算法
* @return 密文
*/
private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception{
// 获取加密对象
Cipher cipher = Cipher.getInstance(transformation);
// 创建加密规则
// 第一个参数key的字节
// 第二个参数表示加密算法
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// ENCRYPT_MODE:加密模式
// DECRYPT_MODE: 解密模式
// 初始化加密模式和算法
cipher.init(Cipher.ENCRYPT_MODE,sks);
// 加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 输出加密后的数据
String encode = Base64.encode(bytes);
return encode;
}
}
使用ECB/NoPadding 的加密模式
public class DESPaddingDemo {
// DES加密算法,key的大小必须是8个字节
public static void main(String[] args) throws Exception{
String input = "硅谷";
//DES加密算法,key的大小必须是8个字节
String key = "12345678";
//指定获取Cipher的算法,如果没有指定加密模式和填充模式,ECB/PKCS5Padding就是默认值
//String transformation = "DES"; 密文:qANksk5lvqM=
//String transformation = "DES/ECB/PKCS5Padding"; //qANksk5lvqM=
String transformation = "DES/ECB/NoPadding";
// 指定获取密钥的算法
String algorithm = "DES";
String encryptDES = encryptDES(input, key, transformation, algorithm);
System.out.println("DES加密:" + encryptDES);
String decryptDES = decryptDES(encryptDES, key, transformation, algorithm);
System.out.println("DES解密:" + decryptDES);
}
/**
* 使用DES解密
*
* @param encryptDES 密文
* @param key 密钥
* @param transformation 获取Cipher对象的算法
* @param algorithm 获取密钥的算法
* @return 原文
*/
private static String decryptDES(String encryptDES, String key, String transformation, String algorithm) throws Exception{
// 1,获取Cipher对象
Cipher cipher = Cipher.getInstance(transformation);
// 指定密钥规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
cipher.init(Cipher.DECRYPT_MODE, sks);
// 3. 解密
byte[] bytes = cipher.doFinal(Base64.decode(encryptDES));
return new String(bytes);
}
/**
* 使用DES加密数据
* @param input 原文
* @param key 密钥(DES,密钥的长度必须是8个字节)
* @param transformation 获取Cipher对象的算法
* @param algorithm 获取密钥的算法
* @return 密文
*/
private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception{
// 获取加密对象
Cipher cipher = Cipher.getInstance(transformation);
// 创建加密规则
// 第一个参数key的字节
// 第二个参数表示加密算法
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// ENCRYPT_MODE:加密模式
// DECRYPT_MODE: 解密模式
// 初始化加密模式和算法
cipher.init(Cipher.ENCRYPT_MODE,sks);
// 加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 输出加密后的数据
String encode = Base64.encode(bytes);
return encode;
}
}
使用 Nopadding 的情况,就需要原文本身是8个字节的倍数
修改一下原文为8个字节长度:
(2)使用 DES 方式,使用 CBC 加密方式
使用 CBC/PKCS5Padding 的加密方式
public class DESPaddingDemo {
// DES加密算法,key的大小必须是8个字节
public static void main(String[] args) throws Exception{
String input = "硅谷12";
//DES加密算法,key的大小必须是8个字节
String key = "12345678";
//指定获取Cipher的算法,如果没有指定加密模式和填充模式,ECB/PKCS5Padding就是默认值
//String transformation = "DES"; 密文:qANksk5lvqM=
//String transformation = "DES/ECB/PKCS5Padding"; //qANksk5lvqM=
// NoPadding模式,原文的长度必须是8个字节的整倍数 ,所以必须把 硅谷改成硅谷12
//String transformation = "DES/ECB/NoPadding"; //hPIdFgHHP3Y=
// CBC模式,必须指定初始向量,初始向量中密钥的长度必须是8个字节
//String transformation = "DES/CBC/PKCS5Padding"; //Y6htKI/ceJgxuMpKeTKQpA==
String transformation = "DES/CBC/PKCS5Padding";
// 指定获取密钥的算法
String algorithm = "DES";
String encryptDES = encryptDES(input, key, transformation, algorithm);
System.out.println("DES加密:" + encryptDES);
String decryptDES = decryptDES(encryptDES, key, transformation, algorithm);
System.out.println("DES解密:" + decryptDES);
}
/**
* 使用DES解密
*
* @param encryptDES 密文
* @param key 密钥
* @param transformation 获取Cipher对象的算法
* @param algorithm 获取密钥的算法
* @return 原文
*/
private static String decryptDES(String encryptDES, String key, String transformation, String algorithm) throws Exception{
// 1,获取Cipher对象
Cipher cipher = Cipher.getInstance(transformation);
// 指定密钥规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
cipher.init(Cipher.DECRYPT_MODE, sks, iv);
// 3. 解密
byte[] bytes = cipher.doFinal(Base64.decode(encryptDES));
return new String(bytes);
}
/**
* 使用DES加密数据
* @param input 原文
* @param key 密钥(DES,密钥的长度必须是8个字节)
* @param transformation 获取Cipher对象的算法
* @param algorithm 获取密钥的算法
* @return 密文
*/
private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception{
// 获取加密对象
Cipher cipher = Cipher.getInstance(transformation);
// 创建加密规则
// 第一个参数key的字节
// 第二个参数表示加密算法
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// 初始向量,参数表示跟谁进行异或,初始向量的长度必须是8位
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
// ENCRYPT_MODE:加密模式
// DECRYPT_MODE: 解密模式
// 初始化加密模式和算法
cipher.init(Cipher.ENCRYPT_MODE,sks, iv);
// 加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 输出加密后的数据
String encode = Base64.encode(bytes);
return encode;
}
}
CBC模式,必须指定初始向量,初始向量中密钥的长度必须是8个字节,否则将出以下错误
使用 CBC/NoPadding 的加密方式
public class DESPaddingDemo {
// DES加密算法,key的大小必须是8个字节
public static void main(String[] args) throws Exception{
String input = "硅谷12";
//DES加密算法,key的大小必须是8个字节
String key = "12345678";
//指定获取Cipher的算法,如果没有指定加密模式和填充模式,ECB/PKCS5Padding就是默认值
//String transformation = "DES"; 密文:qANksk5lvqM=
//String transformation = "DES/ECB/PKCS5Padding"; //qANksk5lvqM=
// NoPadding模式,原文的长度必须是8个字节的整倍数 ,所以必须把 硅谷改成硅谷12
//String transformation = "DES/ECB/NoPadding"; //hPIdFgHHP3Y=
// CBC模式,必须指定初始向量,初始向量中密钥的长度必须是8个字节
//String transformation = "DES/CBC/PKCS5Padding"; //Y6htKI/ceJgxuMpKeTKQpA==
//String transformation = "DES/CBC/NoPadding"; //Y6htKI/ceJg=
String transformation = "DES/CBC/NoPadding";
// 指定获取密钥的算法
String algorithm = "DES";
String encryptDES = encryptDES(input, key, transformation, algorithm);
System.out.println("DES加密:" + encryptDES);
String decryptDES = decryptDES(encryptDES, key, transformation, algorithm);
System.out.println("DES解密:" + decryptDES);
}
/**
* 使用DES解密
*
* @param encryptDES 密文
* @param key 密钥
* @param transformation 获取Cipher对象的算法
* @param algorithm 获取密钥的算法
* @return 原文
*/
private static String decryptDES(String encryptDES, String key, String transformation, String algorithm) throws Exception{
// 1,获取Cipher对象
Cipher cipher = Cipher.getInstance(transformation);
// 指定密钥规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
cipher.init(Cipher.DECRYPT_MODE, sks, iv);
// 3. 解密
byte[] bytes = cipher.doFinal(Base64.decode(encryptDES));
return new String(bytes);
}
/**
* 使用DES加密数据
* @param input 原文
* @param key 密钥(DES,密钥的长度必须是8个字节)
* @param transformation 获取Cipher对象的算法
* @param algorithm 获取密钥的算法
* @return 密文
*/
private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception{
// 获取加密对象
Cipher cipher = Cipher.getInstance(transformation);
// 创建加密规则
// 第一个参数key的字节
// 第二个参数表示加密算法
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// 初始向量,参数表示跟谁进行异或,初始向量的长度必须是8位
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
// ENCRYPT_MODE:加密模式
// DECRYPT_MODE: 解密模式
// 初始化加密模式和算法
cipher.init(Cipher.ENCRYPT_MODE,sks, iv);
// 加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 输出加密后的数据
String encode = Base64.encode(bytes);
return encode;
}
}
在测试 AES 的时候需要注意,key 需要16个字节,加密向量也需要16个字节 ,其他方式跟 DES 一样
八、非对称加密
1、简介
① 非对称加密算法又称现代加密算法。
② 非对称加密是计算机通信安全的基石,保证了加密数据不会被破解。
③ 与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey) 和私有密(privatekey)
④ 公开密钥和私有密钥是一对
⑤ 如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密。
⑥ 如果用私有密钥对数据进行加密,只有用对应的公开密钥才能解密。
⑦ 因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
示例
- 首先生成密钥对, 公钥为(5,14), 私钥为(11,14)
- 现在A希望将原文2发送给B
- A使用公钥加密数据. 2的5次方 mod 14 = 4 , 将密文4发送给B
- B使用私钥解密数据. 4的11次方 mod14 = 2, 得到原文2
特点
- 加密和解密使用不同的密钥
- 如果使用私钥加密, 只能使用公钥解密
- 如果使用公钥加密, 只能使用私钥解密
- 处理数据的速度较慢, 因为安全级别高
常见算法
- RSA
- ECC
2、生成公钥和私钥
public class RSADemo1 {
public static void main(String[] args) throws Exception{
// 加密算法
String algorithm = "RSA";
// 创建密钥对生成器对象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 生成私钥
PrivateKey privateKey = keyPair.getPrivate();
// 生成公钥
PublicKey publicKey = keyPair.getPublic();
System.out.println("privateKey = " + privateKey);
System.out.println("publicKey = " + publicKey);
// 获取私钥字节数组
byte[] privateKeyEncoded = privateKey.getEncoded();
// 获取公钥字节数组
byte[] publicKeyEncoded = publicKey.getEncoded();
// 对公私钥进行base64编码
String privateKeyString = Base64.encode(privateKeyEncoded);
String publicKeyString = Base64.encode(publicKeyEncoded);
// 打印私钥
System.out.println("私钥为:");
System.out.println(privateKeyString);
// 打印公钥
System.out.println("公钥为:");
System.out.println(publicKeyString);
}
}
运行程序:
3、RSA 的加解密
(1)私钥加密
public class RSADemo2 {
public static void main(String[] args) throws Exception{
// 加密算法
String algorithm = "RSA";
// 创建密钥对生成器对象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 生成私钥
PrivateKey privateKey = keyPair.getPrivate();
// 生成公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥字节数组
byte[] privateKeyEncoded = privateKey.getEncoded();
// 获取公钥字节数组
byte[] publicKeyEncoded = publicKey.getEncoded();
// 对公私钥进行base64编码
String privateKeyString = Base64.encode(privateKeyEncoded);
String publicKeyString = Base64.encode(publicKeyEncoded);
String input = "硅谷";
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 初始化加密
// 第一个参数:加密的模式
// 第二个参数:使用私钥进行加密
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
// 私钥加密
byte[] bytes = cipher.doFinal(input.getBytes());
System.out.println("使用RSA的私钥进行加密:" + Base64.encode(bytes));
}
}
运行程序:
(2)私钥加密私钥解密
public class RSADemo2 {
public static void main(String[] args) throws Exception{
// 加密算法
String algorithm = "RSA";
// 创建密钥对生成器对象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 生成私钥
PrivateKey privateKey = keyPair.getPrivate();
// 生成公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥字节数组
byte[] privateKeyEncoded = privateKey.getEncoded();
// 获取公钥字节数组
byte[] publicKeyEncoded = publicKey.getEncoded();
// 对公私钥进行base64编码
String privateKeyString = Base64.encode(privateKeyEncoded);
String publicKeyString = Base64.encode(publicKeyEncoded);
String input = "硅谷";
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 初始化加密
// 第一个参数:加密的模式
// 第二个参数:使用私钥进行加密
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
// 私钥加密
byte[] bytes = cipher.doFinal(input.getBytes());
System.out.println("使用RSA的私钥进行加密:" + Base64.encode(bytes));
// 私钥进行解密
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 对密文进行解密,不需要使用base64,因为原文不会乱码
byte[] decryptBytes = cipher.doFinal(bytes);
System.out.println("原文为:" + new String(decryptBytes));
}
}
运行程序报错,因为私钥加密,只能公钥解密
(3)私钥加密公钥解密
public class RSADemo2 {
public static void main(String[] args) throws Exception {
// 加密算法
String algorithm = "RSA";
// 创建密钥对生成器对象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 生成私钥
PrivateKey privateKey = keyPair.getPrivate();
// 生成公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥字节数组
byte[] privateKeyEncoded = privateKey.getEncoded();
// 获取公钥字节数组
byte[] publicKeyEncoded = publicKey.getEncoded();
// 对公私钥进行base64编码
String privateKeyString = Base64.encode(privateKeyEncoded);
String publicKeyString = Base64.encode(publicKeyEncoded);
String input = "硅谷";
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 初始化加密
// 第一个参数:加密的模式
// 第二个参数:使用私钥进行加密
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
// 私钥加密
byte[] bytes = cipher.doFinal(input.getBytes());
System.out.println("使用RSA的私钥进行加密:" + Base64.encode(bytes));
// 公钥进行解密
cipher.init(Cipher.DECRYPT_MODE, publicKey);
// 对密文进行解密,不需要使用base64,因为原文不会乱码
byte[] decryptBytes = cipher.doFinal(bytes);
System.out.println("原文为:" + new String(decryptBytes));
}
}
运行程序:
(4)公钥加密和公钥解密
一样会报错,使用公钥加密必须使用私钥进行解密。
4、保存公钥和私钥
前面代码每次都会生成 加密和解密 ,咱们需要把加密和解密的方法全部到本地的根目录下面。
public class RSADemo3 {
public static void main(String[] args) throws Exception {
String input = "硅谷";
// 加密算法
String algorithm = "RSA";
//生成密钥对并保存在本地文件中
generateKeyToFile(algorithm, "a.pub", "a.pri");
}
/**
* 生成密钥对并保存在本地文件中
*
* @param algorithm : 算法
* @param pubPath : 公钥保存路径
* @param priPath : 私钥保存路径
* @throws Exception
*/
private static void generateKeyToFile(String algorithm, String pubPath, String priPath) throws Exception {
// 获取密钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 获取密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 获取公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥
PrivateKey privateKey = keyPair.getPrivate();
// 获取byte数组
byte[] publicKeyEncoded = publicKey.getEncoded();
byte[] privateKeyEncoded = privateKey.getEncoded();
// 进行Base64编码
String publicKeyString = Base64.encode(publicKeyEncoded);
String privateKeyString = Base64.encode(privateKeyEncoded);
// 保存文件
FileUtils.writeStringToFile(new File(pubPath), publicKeyString, StandardCharsets.UTF_8);
FileUtils.writeStringToFile(new File(priPath), privateKeyString, StandardCharsets.UTF_8);
}
}
运行在项目根目录生成私钥:
运行在项目根目录生成公钥:
5、读取私钥和公钥并加解密
public class RSADemo3 {
public static void main(String[] args) throws Exception {
String input = "硅谷";
// 加密算法
String algorithm = "RSA";
//生成密钥对并保存在本地文件中
//generateKeyToFile(algorithm, "a.pub", "a.pri");
PrivateKey privateKey = getPrivateKey("a.pri", algorithm);
System.out.println("privateKey = " + privateKey);
PublicKey publicKey = getPublicKey("a.pub", algorithm);
System.out.println("publicKey = " + publicKey);
String encryptStr = encryptRSA(algorithm, privateKey, input);
System.out.println("encryptStr密钥:" + encryptStr);
String decryptStr = decryptRSA(algorithm, publicKey, encryptStr);
System.out.println("decryptStr原文: " + decryptStr);
}
//从文件读取私钥,并转换为私钥对象
public static PrivateKey getPrivateKey(String priPath, String algorithm) throws Exception {
// 将文件内容转为字符串
String privateKeyString = FileUtils.readFileToString(new File(priPath), Charset.defaultCharset());
// 获取密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// 构建密钥规范 进行Base64解码
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.decode(privateKeyString));
// 生成私钥
return keyFactory.generatePrivate(spec);
}
//从文件读取公钥,并转换为公钥对象
public static PublicKey getPublicKey(String pubPath, String algorithm) throws Exception {
// 将文件内容转为字符串
String publicKeyString = FileUtils.readFileToString(new File(pubPath), Charset.defaultCharset());
// 获取密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// 构建密钥规范 进行Base64解码
X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.decode(publicKeyString));
// 生成公钥
return keyFactory.generatePublic(spec);
}
/**
* 使用密钥加密数据
*
* @param algorithm 算法
* @param key 密钥
* @param input 原文
* @return 密文
*/
public static String encryptRSA(String algorithm, Key key, String input) throws Exception {
//创建加密对象
//参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
//初始化加密
//第一个参数:加密的模式
//第二个参数:使用私钥进行加密
cipher.init(Cipher.ENCRYPT_MODE, key);
//私钥加密
byte[] bytes = cipher.doFinal(input.getBytes());
//对密文进行 Base64 编码
return Base64.encode(bytes);
}
/**
* 使用密钥解密数据
*
* @param algorithm 算法
* @param key 密钥
* @param encrypted 密文
* @return 原文
* @throws Exception
*/
public static String decryptRSA(String algorithm, Key key, String encrypted) throws Exception {
// 创建解密对象
// 参数表示解密算法
Cipher cipher = Cipher.getInstance(algorithm);
//使用解密模式
cipher.init(Cipher.DECRYPT_MODE, key);
//由于密文进行了Base64编码,在这里需要进行解码
byte[] decode = Base64.decode(encrypted);
//对密文进行解密,不需要使用Base64,因为原文不会乱码
byte[] bytes = cipher.doFinal(decode);
return new String(bytes);
}
/**
* 生成密钥对并保存在本地文件中
*
* @param algorithm : 算法
* @param pubPath : 公钥保存路径
* @param priPath : 私钥保存路径
* @throws Exception
*/
private static void generateKeyToFile(String algorithm, String pubPath, String priPath) throws Exception {
// 获取密钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 获取密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 获取公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥
PrivateKey privateKey = keyPair.getPrivate();
// 获取byte数组
byte[] publicKeyEncoded = publicKey.getEncoded();
byte[] privateKeyEncoded = privateKey.getEncoded();
// 进行Base64编码
String publicKeyString = Base64.encode(publicKeyEncoded);
String privateKeyString = Base64.encode(privateKeyEncoded);
// 保存文件
FileUtils.writeStringToFile(new File(pubPath), publicKeyString, StandardCharsets.UTF_8);
FileUtils.writeStringToFile(new File(priPath), privateKeyString, StandardCharsets.UTF_8);
}
}
运行程序: