Golang 没有像 PHP 那样提供一个现成的 AES 加密函数, 不过标准库里有 crypto, 利用里面的 AES 等可以自己封装个加密函数, 不过需要理解下整个加解密的过程和原理
这里使用的是 AES 加密中的 CBC 模式, 块加密需要划分成整数长度相等个消息块不断加密 (串行), 分组长度是固定 128 位, 但密钥的长度可以使用 128 位, 192 位或者 256 位 (这里指的是 bit), 即密钥 16, 24, 32 长度对应 AES-128, AES-192, AES-256
package encrypt
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"io"
)
// CBC 加密 按照 golang 标准库的例子代码
// 不过里面没有填充的部分, 所以补上
// 使用 PKCS7 进行填充, iOS 也是 7
// 只要少于 256 就能放到一个 byte 中, 默认的 blockSize=16 (即采用 16*8=128, AES-128 长的密钥)
// 最少填充 1 个 byte, 如果明文刚好是 blocksize 的整数倍, 则再填充一个 blocksize
func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
// 计算需要 padding 的数目, 并生成填充的文本
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
func PKCS7UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}
// AES 加密, 填充秘钥 key 的 16 位, 24, 32 分别对应 AES-128, AES-192, or AES-256
func AesCBCEncrypt(rawData, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// 填充原文
blockSize := block.BlockSize()
rawData = PKCS7Padding(rawData, blockSize)
// 初始向量 IV 必须是唯一, 但不需要保密
cipherText := make([]byte, blockSize+len(rawData))
// block 大小 16
iv := cipherText[:blockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
// block 大小和初始向量大小一定要一致
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(cipherText[blockSize:], rawData)
return cipherText, nil
}
func AesCBCDncrypt(encryptData, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
blockSize := block.BlockSize()
if len(encryptData) < blockSize {
panic("ciphertext too short")
}
iv := encryptData[:blockSize]
encryptData = encryptData[blockSize:]
// CBC mode always works in whole blocks.
if len(encryptData)%blockSize != 0 {
panic("ciphertext is not a multiple of the block size")
}
mode := cipher.NewCBCDecrypter(block, iv)
// CryptBlocks can work in-place if the two arguments are the same.
mode.CryptBlocks(encryptData, encryptData)
// 解填充
encryptData = PKCS7UnPadding(encryptData)
return encryptData, nil
}
func Encrypt(rawData, key []byte) (string, error) {
data, err := AesCBCEncrypt(rawData, key)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(data), nil
}
func Dncrypt(rawData string, key []byte) (string, error) {
data, err := base64.StdEncoding.DecodeString(rawData)
if err != nil {
return "", err
}
dnData, err := AesCBCDncrypt(data, key)
if err != nil {
return "", err
}
return string(dnData), nil
}
其他填充模式 PKCS5Padding
package encrypt
import (
"bytes"
)
func PKCS5Padding(ciphertext []byte) []byte {
// PKCS5Padding 中 blockSize 固定为 8
blockSize := 8
// 计算需要 padding 的数目, 并生成填充的文本
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
func PKCS5UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}
其他填充模式 ZeroPadding
package encrypt
import (
"bytes"
)
func ZeroPadding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
// 用 0 去填充
padtext := bytes.Repeat([]byte{0}, padding)
return append(ciphertext, padtext...)
}
func ZeroUnPadding(origData []byte) []byte {
return bytes.TrimFunc(origData,
func(r rune) bool {
return r == rune(0)
})
}
其他实例 cipher.go
这个例子中的错误的 PKCS5Padding
函数名, 实际在做 PKCS7Padding
的工作。blockSize
作为参数传入函数, 并非固定为 8
package config
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"fmt"
"strings"
)
var ivspec = []byte("0000000000000000")
const Key = "iKwb6Kt5pnqcVZcd"
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
func PKCS5Trimming(encrypt []byte) []byte {
padding := encrypt[len(encrypt)-1]
return encrypt[:len(encrypt)-int(padding)]
}
func AESEncodeStr(src, key string) string {
block, err := aes.NewCipher([]byte(key))
if err != nil {
fmt.Println("key error1", err)
}
if src == "" {
fmt.Println("plain content empty")
}
ecb := cipher.NewCBCEncrypter(block, ivspec)
content := []byte(src)
content = PKCS5Padding(content, block.BlockSize())
crypted := make([]byte, len(content))
ecb.CryptBlocks(crypted, content)
return hex.EncodeToString(crypted)
}
func AESDecodeStr(crypt, key string) string {
crypted, err := hex.DecodeString(strings.ToLower(crypt))
if err != nil || len(crypted) == 0 {
fmt.Println("plain content empty")
}
block, err := aes.NewCipher([]byte(key))
if err != nil {
fmt.Println("key error1", err)
}
ecb := cipher.NewCBCDecrypter(block, ivspec)
decrypted := make([]byte, len(crypted))
ecb.CryptBlocks(decrypted, crypted)
return string(PKCS5Trimming(decrypted))
}
原文