密码学

Byte和Bit

Byte : 字节. 数据存储的基本单位,比如移动硬盘1T , 单位是byte

bit : 比特, 又叫位. 一个位要么是0要么是1. 数据传输的单位 , 比如家里的宽带100MB,下载速度并没有达到100MB,一般都是12-13MB,那么是因为需要使用 100 / 8

关系: 1Byte = 8bit

英文对应的字节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ByteBit {
public static void main(String[] args) {
String a = "a";
byte[] bytes = a.getBytes();
for (byte b : bytes) {
int c=b;
// 打印发现byte实际上就是ascii码
System.out.println(c); //97
// 我们在来看看每个byte对应的bit,byte获取对应的bit
String s = Integer.toBinaryString(c);
System.out.println(s); //1100001
}
}
}

打印英文字符串的字节,实际打印的就是ascii编码!

中文对应的字节

1
2
3
4
5
6
7
8
9
10
11
public class ByteBitDemo {
public static void main(String[] args) throws Exception{
String a = "尚";
byte[] bytes = a.getBytes();
for (byte b : bytes) {
System.out.print(b + " ");
String s = Integer.toBinaryString(b);
System.out.println(s);
}
}
}

cryptography-12

在编码UTF-8的中,一个中文是由三个字节组成的!

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) throws Exception{
String a = "尚";
// 在中文情况下,不同的编码格式,对应不同的字节
//GBK :编码格式占2个字节
// UTF-8:编码格式占3个字节
byte[] bytes = a.getBytes("GBK");
// byte[] bytes = a.getBytes("UTF-8");
for (byte b : bytes) {
System.out.print(b + " ");
String s = Integer.toBinaryString(b);
System.out.println(s);
}
}

cryptography-13

在编码GBK的中,一个中文是由两个字节组成的!在UTF-8编码格式中,一个中文由占3个字节组成。

常见加密

cryptography-14

采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。

示例:

  • 我们现在有一个原文3要发送给B
  • 设置密钥为108, 3 * 108 = 324, 将324作为密文发送给B
  • B拿到密文324后, 使用324/108 = 3 得到原文

常见加密算法

DES : Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。

AES : Advanced Encryption Standard, 高级加密标准 .在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。

特点

  1. 加密速度快, 可以加密大文件
  1. 密文可逆, 一旦密钥文件泄漏, 就会导致数据暴露

  2. 加密后Ascii编码表找不到对应字符, 会出现乱码

  3. 一般结合Base64使用

Base64

Base64是网络上最常见的用于传输8Bit字节码的可读性编码算法之一,可读性编码算法不是为了保护数据的安全性,而是为了可读性,可读性编码不改变信息内容,只改变信息内容的表现形式。

所谓Base64,即是说在编码过程中使用了64种字符:

① 小写 a - z = 26个字母

② 大写 A - Z = 26个字母

③ 数字 0 - 9 = 10 个数字

④ + / = 2个符号

Base58Bitcoin(比特币)中使用的一种编码方式,主要用于产生Bitcoin的钱包地址,相比Base64Base58不使用数字”0”,字母大写”O”,字母大写”I”,和字母小写”i”,以及”+”和”/“符号。

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 = 63

cryptography-18

可能发现一个问题,base64加密后有个=号,但是在映射表里面没有发现 =号 , 这个地方需要注意,等号非常特殊,因为base64是三个字节一组 ,如果当我们的位数不够的时候,会使用等号来补齐

1
2
3
4
5
6
7
8
9
10
11
12
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

public class TestBase64 {
public static void main(String[] args) {
// 1表示一个字节,不够三个字节,所以需要后面通过 == 号补齐
System.out.println(Base64.encode("1".getBytes())); //MQ==
System.out.println(Base64.encode("12".getBytes())); //MTI=
System.out.println(Base64.encode("123".getBytes())); //MTIz
// 测试:中文(UTF-8)占6个字节,6 * 8 = 48 ,刚刚好被整除,所以没有等号
System.out.println(Base64.encode("测试".getBytes())); //5rWL6K+V
}
}

Base64的相关包默认使用apache提供的即可,不同的依赖包,提供的API略有不同。

Eclipse中,可以使用import org.apache.tomcat.util.codec.binary.Base64;

Idea中,可以使用import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

DES加密

Cipher文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

import org.apache.tomcat.util.codec.binary.Base64;
public class DesDemo {
public static void main(String[] args) throws Exception {
String input = "测试测试";
// 密钥
String key = "12345678";
// 加密算法
String transformation = "DES";
// 算法类型
String algorithm = "DES";

System.out.println("原文:" + input);
String encryptDes = encryptDes(input, key, transformation, algorithm);
System.out.println("加密:" + encryptDes);
String decryptDes = decryptDes(encryptDes, key, transformation, algorithm);
System.out.println("解密:" + decryptDes);
}

/**
* 加密算法
*
* @param input 原文
* @param key 密钥
* @param transformation 算法
* @param algorithm 加密类型
* @return
* @throws Exception
*/
public static String encryptDes(String input, String key, String transformation, String algorithm) throws Exception {
// 获取加密对象
Cipher cipher = Cipher.getInstance(transformation);
// 创建加密规则 第一个参数key的字节 第二个参数表示加密算法
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
// 初始化加密模式和算法 Cipher.ENCRYPT_MODE:加密模式
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
// 加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 输出加密后的数据
return Base64.encodeBase64String(bytes);
}

/**
* 解密算法
*
* @param encryptDes 密文
* @param key 密钥
* @param transformation 加密算法
* @param algorithm 加密类型
* @return
* @throws Exception
*/
public static String decryptDes(String encryptDes, String key, String transformation, String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
// Cipher.DECRYPT_MODE:解密模式
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] bytes = cipher.doFinal(Base64.decodeBase64(encryptDes));
return new String(bytes);
}
}

cryptography-19

DES规定密钥key必定是8个字节,如果加密算法的key不足8个字节时,如key=123456 将会出现以下错误

cryptography-15

1
2
3
4
5
6
7
byte[] bytes = cipher.doFinal(input.getBytes());
for (byte b : bytes) {
//会打印出负数
System.out.println(b);
}
//会出现乱码
System.out.println(new String(bytes));

当在加密的过程中执行上述代码,以字符串的形式直接输出加密后的bytes数组,会出现,将会出现以下错误:

cryptography-16

出现乱码是因为对应的字节出现负数,但负数,没有出现在 ascii 码表里面,所以常使用base64配合转码!

1
2
3
byte[] bytes = cipher.doFinal(input.getBytes());
//使用Base64配合输出
System.out.println(Base64.encodeBase64String(bytes));

cryptography-17

AES加密

AES 加密解密和 DES 加密解密代码一样,只需要修改加密算法就行,可以直接拷贝 DSC 加解密代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class AesDemo {
public static void main(String[] args) throws Exception {
String input = "测试测试";
//AES加密算法,比较高级,所以key的大小必须是16个字节
String key = "1234567812345678";
String transformation = "AES";
String algorithm = "AES";

String encryptDes = encryptDes(input, key, transformation, algorithm);
//d+tFlhF2ISyzd725BV0bRg==
System.out.println("加密:" + encryptDes);
}
}

AESDES要高级,所以Key的大小必须是16个字节,如果依旧使用8个字节,将会遇见以下错误:

cryptography-20

加密模式

加密模式:https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html

ECB

ECB : Electronic codebook, 电子密码本. 需要加密的消息按照块密码的块大小被分为数个块,并对每个块进行独立加密。

cryptography-21

优缺点:

优点 : 可以并行处理数据

缺点 : 同样的原文生成同样的密文, 不能很好的保护数据

CBC

CBC : Cipher-block chaining, 密码块链接. 每个明文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块。

cryptography-22

优缺点:

优点 : 同样的原文生成的密文不一样,加密很安全

缺点 : 串行处理数据,加密速度很慢

填充模式

当需要按块处理的数据, 数据长度不符合块处理需求时, 按照一定的方法填充满块长的规则。

NoPadding

不填充,在DES加密算法下, 要求原文长度必须是8byte的整数倍,在AES加密算法下, 要求原文长度必须是16byte的整数倍

PKCS5Padding

数据块的大小为8位, 不够就补足

Tips:

  1. 如果没有写加密模式和填充模式,则默认使用的加 密模式和填充模式为 : ECB/PKCS5Padding
  2. 如果使用CBC模式, 在初始化Cipher对象时, 需要增加参数, 初始化向量IV : IvParameterSpec iv = new IvParameterSpec(key.getBytes());

加密模式和填充模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)

ECB案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.apache.tomcat.util.codec.binary.Base64;
public class DesDemo {
public static void main(String[] args) throws Exception {
String input = "测试测试";
String key = "12345678";
//加密算法/加密模式/填充模式
String transformation = "DES/ECB/PKCS5Padding";
String algorithm = "DES";

String encryptDes = encryptDes(input, key, transformation, algorithm);
System.out.println("加密:" + encryptDes);
}
}

CBC案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//在加密模式为CBC下需要增加IV向量
public static String encryptDes(String input, String key, String transformation, String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
//增加iv向量
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
//传参Iv对象
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv);
byte[] bytes = cipher.doFinal(input.getBytes());
return Base64.encodeBase64String(bytes);
}

public static String decryptDes(String encryptDes, String key, String transformation, String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
//增加iv向量
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
//传参Iv对象
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec,iv);
byte[] bytes = cipher.doFinal(Base64.decodeBase64(encryptDes));
return new String(bytes);
}

NoPadding案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.apache.tomcat.util.codec.binary.Base64;
public class DesDemo {
public static void main(String[] args) throws Exception {
String input = "测试12";
String key = "12345678";
//加密算法/加密模式/填充模式
String transformation = "DES/ECB/NoPadding";
String algorithm = "DES";

String encryptDes = encryptDes(input, key, transformation, algorithm);
System.out.println("加密:" + encryptDes);
}
}

使用不填充模式Nopadding时,加密的文本必须是8个字节的倍数,若上述的input = "测试",只有6个字节时(UTF-8下),将会出现以下错误:

cryptography-23

值得一提的是,在使用iv向量进行加密时,加密的key也必须是8个字节的倍数!

最后更新: 2020年11月13日 13:52

原始链接: https://midkuro.gitee.io/2020/05/29/cryptography-symmetrical/

× 请我吃糖~
打赏二维码