AES CBC 加密解密
简介
密码分组链接模式 CBC (Cipher Block Chaining),这种模式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。
这时候就有个问题,那第一段的明文怎么加密呢?这时候就引入了初始化向量(英语:initialization vector,缩写为IV)。
初始化向量是随机的,就是你可以自定义这个初始化向量,不同的初始化向量加密出来的结果也不一样。
在 Go 中,我们可以用官方提供的 crypto/aes
标准库来给我们进行 AES
加密,不过这个库并没有给我们指定加密模式,需要我们自己通过 crypto/cipher
来选择加密模式。
加密
// cbc+PKCS7+16位key+16位偏移量(直接用key)
// input 未加密的明文字符串
// key 秘钥
func EncryptWithAES128(input, key string) (string, error) {
result, err := aes128EncryptPKCS7UnPadding([]byte(input), []byte(key))
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(result), err
}
// origData 待加密的明文
// key 秘钥
func aes128EncryptPKCS7UnPadding(origData []byte, key []byte) ([]byte, error) {
// 首先我们可以调用 crypto/aes 的函数来返回一个密码块
// NewCipher 创建并返回一个新的 cipher.Block。 key参数应为 16、24 或 32 个字节长度的 AES 密钥,以选择 AES-128,AES-192 或AES-256。
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
// 填充
origData = pkcs7Padding(origData, blockSize)
//使用key充当偏移量
iv := key[:blockSize]
//使用cbc
blocMode := cipher.NewCBCEncrypter(block, iv)
encrypted := make([]byte, len(origData))
blocMode.CryptBlocks(encrypted, origData)
return encrypted, nil
}
// cbc建议使用PKCS#7或PKCS#5,这里使用PKCS#7
// ciphertext:明文内容, blockSize:分组块大小
// PKCS7Padding填充模式:假设数据长度需要填充 n(n>0) 个字节才对齐,
// 那么填充 n 个字节,每个字节都是 n。如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小
func pkcs7Padding(ciphertext []byte, blockSize int) []byte {
// 计算待填充的长度
padding := blockSize - len(ciphertext)%blockSize
// 未对齐 填充 padding 个数据,每个数据为 padding
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padText...)
}
解密
// CBC 解密
// input 解密的密文
// key 秘钥
func DecryptWithAES128(input, key string) (string, error) {
// 对密文base64解码
pwdByte, err := base64.StdEncoding.DecodeString(input)
if err != nil {
return "", err
}
// 解密
res, err := aes128DecryptPKCS7UnPadding(pwdByte, []byte(key))
return string(res), err
}
// cypted 待解密的密文
// key 秘钥
func aes128DecryptPKCS7UnPadding(cypted []byte, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
//使用key充当偏移量
iv := key[:blockSize]
blockMode := cipher.NewCBCDecrypter(block, iv)
origData := make([]byte, len(cypted))
blockMode.CryptBlocks(origData, cypted)
// 去除填充
origData, err = pkcs7UnPadding(origData)
if err != nil {
return nil, err
}
return origData, err
}
// 去除填充
// origData 待去除填充数据的原文
func pkcs7UnPadding(origData []byte) ([]byte, error) {
length := len(origData)
// 取出填充的数据 以此来获得填充数据长度
if length == 0 {
return nil, errors.New("wrong encryption parameters")
} else {
unPadding := int(origData[length-1])
return origData[:(length - unPadding)], nil
}
}
测试
func main() {
// 明文字符串
text := `{"os":"12.231.64","mac":"acavkdak","guid":"adfadafe","version":"1.0.0.1","proxy":"d1231"}`
// 加密秘钥
key := `36231a2522c3c2d4a991e17aa5fb6732`
// 加密
aes128, err := EncryptWithAES128(text, key)
if err != nil {
fmt.Printf("EncryptWithAES128 err: %v", err)
}
fmt.Printf("EncryptWithAES128 aes128:%v", aes128)
// 结果: aes128: Kbf8PIgVU0RZYMlHw1C7UoV0S4RUeUu01T7SO3JP456d0qulp19JKtFoF1Qq75hGpBiJgnPupc4/aTxklvgsBL4bEbl5FLdX+kbwzoKLynurnanf8ovnIhoKEJQAMSTl
// 解密
withAES128, err := DecryptWithAES128(aes128, key)
if err != nil {
fmt.Printf("DecryptWithAES128 err: %v", err)
}
fmt.Printf("DecryptWithAES128 withAES128:%v", withAES128)
// 结果: withAES128:{"os":"12.231.64","mac":"acavkdak","guid":"adfadafe","version":"1.0.0.1","proxy":"d1231"}
}