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

AES的安全性:

密码学的意义上,只要存在一个方法,比穷举法还要更有效率,就能被视为一种“破解”。故一个针对AES 128位密钥的攻击若“只”需要2120计算复杂度(少于穷举法 2128),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模式相同。

实现

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整除,不用=补足

const encodeStd = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
const encodeURL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
//编码
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)