golang实现AES有五种加密模式函数,Encrypt加解密字符串输出
/*
AES有五种加密模式
电码本模式(Electronic Codebook Book (ECB))、
密码分组链接模式(Cipher Block Chaining (CBC))、
计算器模式(Counter (CTR))、
密码反馈模式(Cipher FeedBack (CFB))
输出反馈模式(Output FeedBack (OFB))
*/
package libs
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/hex"
"strings"
"errors"
)
/***************CBC加密***************************
func main() {
orig := "hello world"
key := "0123456789012345"
fmt.Println("原文:", orig)
encryptCode := AesEncryptCBC(orig, key)
fmt.Println("密文:" , encryptCode)
decryptCode := AesDecryptCBC(encryptCode, key)
fmt.Println("解密结果:", decryptCode)
}
**************************************************/
func AesEncryptCBC(orig string, key string) string {
// 转成字节数组
origData := []byte(orig)
k := []byte(key)
// 分组秘钥
// NewCipher该函数限制了输入k的长度必须为16, 24或者32
block, _ := aes.NewCipher(k)
// 获取秘钥块的长度
blockSize := block.BlockSize()
// 补全码
origData = PKCS7Padding(origData, blockSize)
// 加密模式
blockMode := cipher.NewCBCEncrypter(block, k[:blockSize])
// 创建数组
cryted := make([]byte, len(origData))
// 加密
blockMode.CryptBlocks(cryted, origData)
return base64.StdEncoding.EncodeToString(cryted)
}
func AesDecryptCBC(cryted string, key string) string {
// 转成字节数组
crytedByte, _ := base64.StdEncoding.DecodeString(cryted)
k := []byte(key)
// 分组秘钥
block, _ := aes.NewCipher(k)
// 获取秘钥块的长度
blockSize := block.BlockSize()
// 加密模式
blockMode := cipher.NewCBCDecrypter(block, k[:blockSize])
// 创建数组
orig := make([]byte, len(crytedByte))
// 解密
blockMode.CryptBlocks(orig, crytedByte)
// 去补全码
orig = PKCS7UnPadding(orig)
return string(orig)
}
//补码
//AES加密数据块分组长度必须为128bit(byte[16]),密钥长度可以是128bit(byte[16])、192bit(byte[24])、256bit(byte[32])中的任意一个。
func PKCS7Padding(ciphertext []byte, blocksize int) []byte {
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)]
}
/***************AESEncryptECBStr加解密案列*********
func main() {
source:="asdasadgsdhfasf"
fmt.Println("原字符:",source)
//16byte密钥
key:="sdddd"
encryptCode:=AESEncryptECBStr(source,key)
fmt.Println("密文:",encryptCode)
decryptCode:=AESDecryptECBStr(encryptCode,key)
fmt.Println("解密:",decryptCode)
}
**************************************************/
func AESEncryptECBStr(source string, keys string) string {
// 字符串转换成切片
src := []byte(source)
key := []byte(keys)
cipher, _ := aes.NewCipher(generateKeys(key))
length := (len(src) + aes.BlockSize) / aes.BlockSize
plain := make([]byte, length*aes.BlockSize)
copy(plain, src)
pad := byte(len(plain) - len(src))
for i := len(src); i < len(plain); i++ {
plain[i] = pad
}
encrypted := make([]byte, len(plain))
// 分组分块加密
for bs, be := 0, cipher.BlockSize(); bs <= len(src); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
}
//return encrypted
encryptstr :=strings.ToUpper(hex.EncodeToString(encrypted))
return encryptstr
}
func AESDecryptECBStr(encrypteds string, keys string) string {
// 字符串转换成切片
//encrypted := []byte(encrypteds)
encrypted, _ := hex.DecodeString(encrypteds)
key := []byte(keys)
cipher, _ := aes.NewCipher(generateKeys(key))
decrypted := make([]byte, len(encrypted))
//
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
}
trim := 0
if len(decrypted) > 0 {
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
}
return string(decrypted[:trim])
}
func generateKeys(key []byte) (genKey []byte) {
genKey = make([]byte, 16)
copy(genKey, key)
for i := 16; i < len(key); {
for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
genKey[j] ^= key[i]
}
}
return genKey
}
// 为了方便,声明该函数,省去错误处理
// 测试解码
//const testStr = "R2/or63oqIDnvJbnqIs="
//str :=mustDecode(base64.StdEncoding,testStr) // 打印:R2/or63oqIDnvJbnqIs=
//fmt.Println(str)
func Base64DecodeStr(enc *base64.Encoding, str string) string {
data, err := enc.DecodeString(str)
if err != nil {
panic(err)
}
return string(data)
}
// 该函数测试编解码
// enc为要测试的Encoding对象,str为要测试的字符串
//const testStr = "Go语言编程"
// 测试StdEncoding,注意打印结果里的/为URL中的特殊字符,最后有一个padding,mysql的base64加密
//testEncoding(base64.StdEncoding, testStr) // 打印:R2/or63oqIDnvJbnqIs=
测试URLEncoding,可以看到/被替换为_
//testEncoding(base64.URLEncoding, testStr) // 打印:R2_or63oqIDnvJbnqIs=
//
测试RawStdEncoding,可以看到去掉了padding
//testEncoding(base64.RawStdEncoding, testStr) // 打印:R2/or63oqIDnvJbnqIs
//
测试RawURLEncoding,可以看到/被替换Wie_,并且却掉了padding
//testEncoding(base64.RawURLEncoding, testStr) // 打印:R2_or63oqIDnvJbnqIs
func Base64EncodeStr(enc *base64.Encoding, str string) string {
// 编码
encStr := enc.EncodeToString([]byte(str))
// 解码
decStr := Base64DecodeStr(enc, encStr)
if decStr != str { // 编码后再解码应该与原始字符串相同
// 这里判断如果不同,则panic
panic(errors.New("unequal!"))
}
return encStr
}
/**************************加密介绍
mysql的AES_DECRYPT方法,欲使用golang实现该方法, 但是研究发现golang当前默认支持CBC的方式,但是mysql当前使用的是ECB模式,
SELECT HEX(AES_ENCRYPT('hello world', '1443flfsaWfdas'));
SELECT AES_DECRYPT(UNHEX('ef845a0501a6f76da2de6fba84546f8b'),'1443flfsaWfdas')
Go语言的string模块包含了ToLower和ToUpper函数,用于将字符串转换成小写和大写,mysql都是返回大写
对称加密, 加解密都使用的是同一个密钥, 其中的代表就是AES
非对加解密, 加解密使用不同的密钥, 其中的代表就是RSA
签名算法, 如MD5、SHA1、HMAC等, 主要用于验证,防止信息被修改, 如:文件校验、数字签名、鉴权协议
AES:高级加密标准(Advanced Encryption Standard),又称Rijndael加密法,这个标准用来替代原先的DES。
AES加密数据块分组长度必须为128bit(byte[16]),密钥长度可以是128bit(byte[16])、192bit(byte[24])、256bit(byte[32])中的任意一个。
块:对明文进行加密的时候,先要将明文按照128bit进行划分。
填充方式:因为明文的长度不一定总是128的整数倍,所以要进行补位,我们这里采用的是PKCS7填充方式
AES实现的方式多样, 其中包括ECB、CBC、CFB、OFB等
1.电码本模式(Electronic Codebook Book (ECB))
将明文分组加密之后的结果直接称为密文分组。
2.密码分组链接模式(Cipher Block Chaining (CBC))
将明文分组与前一个密文分组进行XOR运算,然后再进行加密。每个分组的加解密都依赖于前一个分组。而第一个分组没有前一个分组,
因此需要一个初始化向量
3.计算器模式(Counter (CTR))
4.密码反馈模式(Cipher FeedBack (CFB))
前一个密文分组会被送回到密码算法的输入端。
在CBC和EBC模式中,明文分组都是通过密码算法进行加密的。而在CFB模式中,明文分组并没有通过加密算法直接进行加密,
明文分组和密文分组之间只有一个XOR。
加密模式 对应加解密方法
CBC NewCBCDecrypter, NewCBCEncrypter
CTR NewCTR
CFB NewCFBDecrypter, NewCFBEncrypter
OFB NewOFB
***********************************/