Go语言aes加密解密处理


文章目录

1. 概述

1.1 AES概述

高级加密标准(英语:Advanced Encryption Standard,​​缩写​​:AES),又称Rijndael加密法(荷兰语发音:[​​ˈrɛindaːl]​​​,音似英文的“Rhine doll”),是​​美国联邦政府​​​采用的一种​​区块加密​​​标准。这个标准用来替代原先的​​DES​​​,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由​​美国国家标准与技术研究院​​​(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。现在,高级加密标准已然成为​​对称密钥加密​​​中最流行的​​算法​​之一。

该算法为​​比利时​​密码学家Joan Daemen和Vincent Rijmen所设计,结合两位作者的名字,以Rijndael为名投稿高级加密标准的甄选流程。

更多说明请查看:

​https://zh.wikipedia.org/wiki/高级加密标准​

1.2 分组密码工作模式

​密码学​​​中,​​分组密码​​的工作模式(mode of operation)允许使用同一个分组密码​​密钥​​​对多于一块的数据进行加密,并保证其安全性。将数据扩展到符合密码块大小的长度。一种工作模式描述了加密每一数据块的过程,并常常使用基于一个通常称为​​初始化向量​​的附加输入值以进行随机化,以保证安全。

工作模式主要用来进行加密和​​认证​​​。对加密模式的研究曾经包含数据的完整性保护,即在某些数据被修改后的情况下密码的​​误差传播​​​特性。后来的研究则将​​完整性保护​​​作为另一个完全不同的,与加密无关的密码学目标。部分现代的工作模式用有效的方法将加密和认证结合起来,称为​​认证加密模式​​。

虽然工作模式通常应用于​​对称加密​​​,它亦可以应用于​​公钥加密​​​,例如在原理上对​​RSA​​进行处理,但在实用中,公钥密码学通常不用于加密较长的信息,而是使用结合对称加密和公钥加密的混合加密方案。

最早出现的工作模式,ECB,CBC,OFB和CFB可以追溯到1981年。2001年,NIST修订了其早先发布的工作模式任务栏表,加入了​​AES​​​,并加入了CTR模式。最后,在2010年1月,NIST加入了​​XTS-AES​​​,而其余的可信模式并没有为NIST所认证。例如CTS是一种​​密文窃取​​的模式,许多常见的密码学运行库提供了这种模式。

ECB,CBC,OFB,CFB,CTR和​​XTS​​​模式仅仅提供了机密性;为了保证加密信息没有被意外修改或恶意篡改,需要采用分离的​​消息验证码​​​,例如​​CBC-MAC​​​。密码学社区认识到了对专用的保证完整性的方法的需求,NIST因此提出了HMAC,CMAC和GMAC。​​HMAC​​​在2002年通过了认证,​​CMAC​​在2005年通过,GMAC则在2007年被标准化。

在发现将认证模式与加密模式联合起来的难度之后,密码学社区开始研究结合了加密和认证的单一模式,这种模式被称为​​认证加密模式​​​(AE,Authenticated Encryption),或称为authenc。AE模式的例子包括​​CCM​​​,​​GCM​​​[​​11]​​​,​​CWC​​​,​​EAX​​​,​​IAPM​​​和​​OCB​​。

现在,工作模式为许多国家和国内的标准认证实体所定义,其中最有影响力的来源是美国的​​NIST​​​,而其它有影响力的组织包括​​ISO​​​,​​IEC​​​,​​IEEE​​​,美国的​​ANSI​​​,以及​​IETF​​。

常见模式:电子密码本(ECB)、密码块链接(CBC)、填充密码块链接(PCBC)、密文反馈(CFB)、输出反馈(OFB)、计数器模式(CTR)。

更多请查看:https://zh.wikipedia.org/wiki/%E5%88%86%E7%BB%84%E5%AF%86%E7%A0%81%E5%B7%A5%E4%BD%9C%E6%A8%A1%E5%BC%8F#%E8%AE%A1%E6%95%B0%E5%99%A8%E6%A8%A1%E5%BC%8F%EF%BC%88CTR%EF%BC%89

2. go实现

2.1 CBC模式

func PKCS5Padding(plaintext []byte, blockSize int) []byte {
padding := blockSize - len(plaintext)%blockSize
paddingText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(plaintext, paddingText...)
}

func PKCS5UnPadding(origData []byte) []byte {
length := len(origData)
unPadding := int(origData[length-1])
return origData[:(length - unPadding)]
}

func AESEncryptCBC(origData, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

//AES分组长度为128位,所以blockSize=16,单位字节
blockSize := block.BlockSize()
origData = PKCS5Padding(origData, blockSize)
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) //初始向量的长度必须等于块block的长度16字节
encrypted := make([]byte, len(origData))
blockMode.CryptBlocks(encrypted, origData)
return encrypted, nil
}

func AESDecryptCBC(encrypted, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

//AES分组长度为128位,所以blockSize=16,单位字节
blockSize := block.BlockSize()
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) //初始向量的长度必须等于块block的长度16字节
origData := make([]byte, len(encrypted))
blockMode.CryptBlocks(origData, encrypted)
origData = PKCS5UnPadding(origData)
return origData, nil
}

填充和去除填充的模式可以更改。

2.2 ECB模式

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)]
}

func AESDecryptECB(data, key []byte) []byte {
block, _ := aes.NewCipher(key)
decrypted := make([]byte, len(data))
size := block.BlockSize()

for bs, be := 0, size; bs < len(data); bs, be = bs+size, be+size {
block.Decrypt(decrypted[bs:be], data[bs:be])
}

return PKCS7UnPadding(decrypted)
}

func AESEncryptECB(data, key []byte) []byte {
block, _ := aes.NewCipher(key)
data = PKCS7Padding(data, block.BlockSize())
decrypted := make([]byte, len(data))
size := block.BlockSize()

for bs, be := 0, size; bs < len(data); bs, be = bs+size, be+size {
block.Encrypt(decrypted[bs:be], data[bs:be])
}

return decrypted
}

2.3 使用实例

package main

import (
"encoding/base64"
"fmt"
"gitlab.com/xxx/util"
)

func main() {
password := "admin"

data := util.AESEncryptECB([]byte(password), []byte("0123456789abcdef"))
cre := util.AESDecryptECB(data, []byte("0123456789abcdef"))
fmt.Println(string(cre))
fmt.Println(base64.StdEncoding.EncodeToString(data))

data, _ = util.AESEncryptCBC([]byte(password), []byte("0123456789abcdef"))
cre, _ = util.AESDecryptCBC(data, []byte("0123456789abcdef"))
fmt.Println(string(cre))
fmt.Println(base64.StdEncoding.EncodeToString(data))
}

结果:

admin
goLfAELVjjdnoF2e9yy11w==
admin
0bfPC68vdB69vK516zf/Ig==