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 協議》,轉載必須註明作者和本文連結