1.介绍
高级加密标准(英语: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为名投稿高级加密标准的甄选流程。
2. 电码本模式(ECB
)
2.1 加密
a.代码
func AesEncryptByECB(data, key string) string { keyLenMap := map[int]struct{}{16: {}, 24: {}, 32: {}} if _,ok := keyLenMap[len(key)]; !ok { panic("key长度必须是 16、24、32 其中一个") } originByte := []byte(data) keyByte := []byte(key) block, _ := aes.NewCipher(keyByte) blockSize := block.BlockSize() originByte = PKCS7Padding(originByte, blockSize) encryptResult := make([]byte, len(originByte)) for bs, be := 0, blockSize; bs < len(originByte); bs, be = bs+blockSize, be+blockSize { block.Encrypt(encryptResult[bs:be], originByte[bs:be]) } return base64.StdEncoding.EncodeToString(encryptResult) }
func PKCS7Padding(originByte []byte, blockSize int) []byte { padding := blockSize - len(originByte)%blockSize padText := bytes.Repeat([]byte{byte(padding)}, padding) return append(originByte, padText...) }
|
b.测试
package crypto import ( "52lu/go-study-example/package/crypto" "fmt" "strings" "testing" )
func TestECBEncrypt(t *testing.T) { key := strings.Repeat("a", 16) data := "hello word" s := crypto.AesEncryptByECB(data, key) fmt.Printf("加密密钥: %v \n", key) fmt.Printf("加密数据: %v \n", data) fmt.Printf("加密结果: %v \n", s) }
|
c.第三方加密验证
2.2 解密
a.代码
func AesDecryptByECB(data, key string) string { keyLenMap := map[int]struct{}{16: {}, 24: {}, 32: {}} if _,ok := keyLenMap[len(key)]; !ok { panic("key长度必须是 16、24、32 其中一个") } originByte, _ := base64.StdEncoding.DecodeString(data) keyByte := []byte(key) block, _ := aes.NewCipher(keyByte) blockSize := block.BlockSize() decrypted := make([]byte, len(originByte)) for bs, be := 0, blockSize; bs < len(originByte); bs, be = bs+blockSize, be+blockSize { block.Decrypt(decrypted[bs:be], originByte[bs:be]) } return string(PKCS7UNPadding(decrypted)) }
func PKCS7UNPadding(originDataByte []byte) []byte { length := len(originDataByte) unpadding := int(originDataByte[length-1]) return originDataByte[:(length-unpadding)] }
|
b.测试
func TestECBDecrypt(t *testing.T) { key := strings.Repeat("a", 16) data := "mMAsLF/fPBfUrP0mPqZm1w==" s := crypto.AesDecryptByECB(data, key) fmt.Printf("解密密钥: %v \n", key) fmt.Printf("解密数据: %v \n", data) fmt.Printf("解密结果: %v \n", s) }
|
3. 密码分组链模式(CBC
)
3.1 加密
a.代码
func AesEncryptByCBC(str, key string) string { keyLenMap := map[int]struct{}{16: {}, 24: {}, 32: {}} if _,ok := keyLenMap[len(key)]; !ok { panic("key长度必须是 16、24、32 其中一个") } originDataByte := []byte(str) keyByte := []byte(key) block, _ := aes.NewCipher(keyByte) blockSize := block.BlockSize() originDataByte = PKCS7Padding(originDataByte, blockSize) blockMode := cipher.NewCBCEncrypter(block, keyByte[:blockSize]) encrypted := make([]byte, len(originDataByte)) blockMode.CryptBlocks(encrypted, originDataByte) return base64.StdEncoding.EncodeToString(encrypted) }
func PKCS7Padding(originByte []byte, blockSize int) []byte { padding := blockSize - len(originByte)%blockSize padText := bytes.Repeat([]byte{byte(padding)}, padding) return append(originByte, padText...) }
|
b.测试
package crypto import ( "52lu/go-study-example/package/crypto" "fmt" "strings" "testing" )
func TestAesEncryptByCBC(t *testing.T) { key := strings.Repeat("a", 16) fmt.Printf("key: %v 长度: %d \n", key, len(key)) text := "abc" fmt.Printf("带加密文案: %v \n", text) encrypt := crypto.AesEncryptByCBC(text, key) fmt.Printf("加密结果: %v \n", encrypt) }
|
c.第三方验证
3.2 解密
a.代码
func AesDecryptByCBC(encrypted,key string) string { keyLenMap := map[int]struct{}{16: {}, 24: {}, 32: {}} if _,ok := keyLenMap[len(key)]; !ok { panic("key长度必须是 16、24、32 其中一个") } decodeString, _ := base64.StdEncoding.DecodeString(encrypted) keyByte := []byte(key) block, _ := aes.NewCipher(keyByte) blockSize := block.BlockSize() blockMode := cipher.NewCBCDecrypter(block, keyByte[:blockSize]) decodeResult := make([]byte, blockSize) blockMode.CryptBlocks(decodeResult,decodeString) padding := PKCS7UNPadding(decodeResult) return string(padding) }
func PKCS7UNPadding(originDataByte []byte) []byte { length := len(originDataByte) unpadding := int(originDataByte[length-1]) return originDataByte[:(length-unpadding)] }
|
b.测试
package crypto import ( "52lu/go-study-example/package/crypto" "fmt" "strings" "testing" )
func TestAesDecryptByCBC(t *testing.T) { key := strings.Repeat("a", 16) fmt.Printf("key: %v 长度: %d \n", key, len(key)) text := "rMX6r9x+PnTOhfgDH4jjXg==" fmt.Printf("待解密文案: %v \n", text) decrypt := crypto.AesDecryptByCBC(text, key) fmt.Printf("解密结果: %v \n", decrypt) }
|
4. 计算器模式(CTR
)
4.1 加密
a.代码
func AesEncryptByCTR(data, key string) (string,string) { keyLenMap := map[int]struct{}{16: {}, 24: {}, 32: {}} if _,ok := keyLenMap[len(key)]; !ok { panic("key长度必须是 16、24、32 其中一个") } dataByte := []byte(data) keyByte := []byte(key) block, err := aes.NewCipher(keyByte) if err != nil { panic(fmt.Sprintf("NewCipher error:%s",err)) } blockSize := block.BlockSize() iv := []byte(key[:blockSize]) padding := PKCS7Padding(dataByte, blockSize) stream := cipher.NewCTR(block, iv) out := make([]byte,len(padding)) stream.XORKeyStream(out,padding) hexRes := fmt.Sprintf("%x",out) base64Res := base64.StdEncoding.EncodeToString(out) return hexRes,base64Res }
func PKCS7Padding(originByte []byte, blockSize int) []byte { padding := blockSize - len(originByte)%blockSize padText := bytes.Repeat([]byte{byte(padding)}, padding) return append(originByte, padText...) }
|
b.测试
package crypto import ( "52lu/go-study-example/package/crypto" "fmt" "strings" "testing" )
func TestAesEncryptByCTR(t *testing.T) { key := strings.Repeat("a", 16) data := "hello word" hex, base64 := crypto.AesEncryptByCTR(data, key) fmt.Printf("加密key: %v \n", key) fmt.Printf("加密key长度: %v \n", len(key)) fmt.Printf("加密数据: %v \n", data) fmt.Printf("加密结果(hex): %v \n", hex) fmt.Printf("加密结果(base64): %v \n", base64) }
|
c.第三方加密验证
4.2 解密
a.代码
func AesDecryptByCTR(dataBase64,key string) string { keyLenMap := map[int]struct{}{16: {}, 24: {}, 32: {}} if _,ok := keyLenMap[len(key)]; !ok { panic("key长度必须是 16、24、32 其中一个") } decodeStringByte, err := base64.StdEncoding.DecodeString(dataBase64) if err != nil { panic(fmt.Sprintf("base64 DecodeString error: %s",err)) } block, err := aes.NewCipher([]byte(key)) if err != nil { panic(fmt.Sprintf("NewCipher error: %s",err)) } blockSize := block.BlockSize() iv := []byte(key[:blockSize]) stream := cipher.NewCTR(block, iv) out := make([]byte,len(decodeStringByte)) stream.XORKeyStream(out,decodeStringByte) return string(PKCS7UNPadding(out)) }
|
b.测试
package crypto import ( "52lu/go-study-example/package/crypto" "fmt" "strings" "testing" )
func TestAesDecryptByCTR(t *testing.T) { key := strings.Repeat("a", 16) data := "Oe2qKyQC+9KgJu8UWLgbVQ==" res := crypto.AesDecryptByCTR(data, key) fmt.Printf("解密key: %v \n", key) fmt.Printf("解密数据: %v \n", data) fmt.Printf("解密结果: %v \n", res) }
|
5. CFB、OFB
和CTR
模式一样,只需要修改加密模式即可,查看具体源码 https://github.com/52lu/go-study-example
... stream := cipher.NewCFBDecrypter(block, iv) ...
... stream := cipher.NewOFB(block, iv) ...
|
不理解的点: 在学习使用中,发现CFB/OFB/CTR 在加密很短的字符串时,发现加密结果一致。
func TestAesEncryptByOFB(t *testing.T) { key := strings.Repeat("a", 16) data := "123" _, base64 := crypto.AesEncryptByOFB(data, key) _, base642 := crypto.AesEncryptByCTR(data, key) _, base643 := crypto.AesEncryptByCFB(data, key) fmt.Printf("加密key: %v \n", key) fmt.Printf("加密key长度: %v \n", len(key)) fmt.Printf("加密数据: %v \n", data) fmt.Printf("加密结果(OFB): %v \n", base64) fmt.Printf("加密结果(CTR): %v \n", base642) fmt.Printf("加密结果(CFB): %v \n", base643) }
|
6.AES-GCM
GCM
全称为Galois/Counter Mode
,可以看出 G
是指 GMAC
,C
是指 CTR
。它在 CTR
加密的基础上增加 GMAC
的特性,解决了 CTR
不能对加密消息进行完整性校验的问题。
6.1 加密
func AesEncryptByGCM(data, key string) string { block, err := aes.NewCipher([]byte(key)) if err != nil { panic(fmt.Sprintf("NewCipher error:%s", err)) } gcm, err := cipher.NewGCM(block) if err != nil { panic(fmt.Sprintf("NewGCM error:%s", err)) } nonceStr := key[:gcm.NonceSize()] nonce := []byte(nonceStr) fmt.Printf("nonceStr = %v \n", nonceStr) seal := gcm.Seal(nonce, nonce, []byte(data), nil) return base64.StdEncoding.EncodeToString(seal) }
|
6.2 解密
func AesDecryptByGCM(data, key string) string { dataByte,err := base64.StdEncoding.DecodeString(data) if err != nil { panic(fmt.Sprintf("base64 DecodeString error:%s", err)) } block, err := aes.NewCipher([]byte(key)) if err != nil { panic(fmt.Sprintf("NewCipher error:%s", err)) } gcm, err := cipher.NewGCM(block) if err != nil { panic(fmt.Sprintf("NewGCM error:%s", err)) } nonceSize := gcm.NonceSize() if len(dataByte) < nonceSize { panic("dataByte to short") } nonce, ciphertext := dataByte[:nonceSize], dataByte[nonceSize:] open, err := gcm.Open(nil, nonce, ciphertext, nil) if err != nil { panic(fmt.Sprintf("gcm Open error:%s", err)) } return string(open) }
|
6.3 测试
package crypto import ( "52lu/go-study-example/package/crypto" "fmt" "strings" "testing" ) func TestAesGCM(t *testing.T) { key := strings.Repeat("a",16) data := "hello word!" gcm := crypto.AesEncryptByGCM(data, key) fmt.Printf("密钥key: %s \n",key) fmt.Printf("加密数据: %s \n",data) fmt.Printf("加密结果: %s \n",gcm) byGCM := crypto.AesDecryptByGCM(gcm, key) fmt.Printf("解密结果: %s \n",byGCM) }
|