AES CBC 加密解密

Golang
648
0
0
2022-10-27

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

在线测试工具