你的AES加密结果相同吗


AES加密后的结果,数据接收方解不出来,是哪里的问题?

AES的安全性:

密码学的意义上,只要存在一个方法,比穷举法还要更有效率,就能被视为一种“破解”。故一个针对AES 128位密钥的攻击若“只”需要2^120^计算复杂度(少于穷举法 2^128^),128位密钥的AES就算被破解了。从应用的角度来看,这种程度的破解依然太不切实际。

AES加密方式有五种:

  • **电码本模式(Electronic Codebook Book (ECB)**,将整个明文分成若干段相同的小段,然后对每一小段进行加密。

  • 密码分组链接模式(Cipher Block Chaining (CBC)),先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。

    优点:能掩盖明文结构信息,保证相同密文可得不同明文,所以不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL和IPSec的标准。

    缺点:(1)不利于并行计算;(2)传递误差——前一个出错则后续全错;(3)第一个明文块需要与一个初始化向量IV进行抑或,初始化向量IV的选取比较复杂。

    初始化IV的选取方式:固定IV,计数器IV,随机IV(只能得到伪随机数,用的最多),瞬时IV(难以得到瞬时值)

  • 计算器模式(Counter (CTR)), 完全的流模式。将瞬时值与计数器连接起来,然后对此进行加密产生密钥流的一个密钥块,再进行XOR操作 。

  • 密码反馈模式(Cipher FeedBack (CFB))

  • 输出反馈模式(Output FeedBack (OFB)),密码算法的输出(指密码key而不是密文)会反馈到密码算法的输入中,OFB模式并不是通过密码算法对明文直接加密,而是通过将明文分组和密码算法的输出进行XOR来产生密文分组。

密钥长度三种:

​ 密钥越长,安全强度越高,运算开销就会越大。

  • AES-128:16byte
  • AES-192:24byte
  • AES-256:32byte

Padding:

  • PKCS5:PKCS5是指分组数据缺少几个字节,就在数据的末尾填充几个字节的几,比如缺少5个字节,就在末尾填充5个字节的5。
  • PKCS7:PKCS7是指分组数据缺少几个字节,就在数据的末尾填充几个字节的0,比如缺少7个字节,就在末尾填充7个字节的0。
  • NOPADDING:指不需要填充,也就是说数据的发送方肯定会保证最后一段数据也正好是16个字节。

实际使用中要注意的地方

  1. 密钥、初始向量相同。
  2. 加密模式相同。
  3. Padding模式相同。

实现

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
func aesEncrypt(orig string, key string) string {
// 转成字节数组
origData := []byte(orig)
k := []byte(key)
// 分组秘钥
block, err := aes.NewCipher(k)
if err != nil {
panic(fmt.Sprintf("key 长度必须 16/24/32长度: %s", err.Error()))
}
// 获取秘钥块的长度
blockSize := block.BlockSize()
// 补全码
origData = PKCS7Padding(origData, blockSize)
// 加密模式
blockMode := cipher.NewCBCEncrypter(block, k[:blockSize])
// 创建数组
cryted := make([]byte, len(origData))
// 加密
blockMode.CryptBlocks(cryted, origData)
return base64.RawURLEncoding.EncodeToString(cryted)

}

func aesDecrypt(cryted string, key string) string {
crytedByte, _ := base64.RawURLEncoding.DecodeString(cryted)
k := []byte(key)

// 分组秘钥
block, err := aes.NewCipher(k)
if err != nil {
panic(fmt.Sprintf("key 长度必须 16/24/32长度: %s", err.Error()))
}
// 获取秘钥块的长度
blockSize := block.BlockSize()
// 加密模式
blockMode := cipher.NewCBCDecrypter(block, k[:blockSize])
// 创建数组
orig := make([]byte, len(crytedByte))
// 解密
blockMode.CryptBlocks(orig, crytedByte)
// 去补全码
orig = PKCS7UnPadding(orig)
return string(orig)
}

结束了吗?

加密或解密结果通常是byte数组,需要进一步转换为字符串。这时候通常又有两种选择,hexbase64。如果使用了base64进行编码,那么还有一个需要双方约定的地方。

Base64

Base64编码好处:简短、不可读性,即所编码的数据不会被人用肉眼所直接看到。

注意:后端与js通信时base64模式的选择🐶

模式 区别 备注
StdEncoding 字符串由A-Za-z0-9+/组成,字节长度不能被3整除,用=补足
RawStdEncoding 字符串由A-Za-z0-9+/组成,字节长度不能被3整除,不用=补足
URLEncoding 字符串由A-Za-z0-9-_组成,字节长度不能被3整除,用=补足
RawURLEncoding 字符串由A-Za-z0-9-_组成,字节长度不能被3整除,不用=补足
1
2
const encodeStd = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
const encodeURL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
1
2
3
4
5
6
7
8
9
10
//编码
base64.StdEncoding.EncodeToString(data)
base64.StdEncoding.DecodeString(data)
base64.RawStdEncoding.EncodeToString(data)
base64.RawStdEncoding.DecodeString(data)
//解码
base64.URLEncoding.EncodeToString(data)
base64.URLEncoding.DecodeString(data)
base64.RawURLEncoding.EncodeToString(data)
base64.RawURLEncoding.DecodeString(data)

  目录