AES CBC 加密解密

charliecen發表於2022-06-13

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

線上測試工具

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章