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"} | |
} |