文章目录
- 1.简介
- 区块长度
- 密钥长度
- 加密模式
- 2.加解密实现
- 3.dablelv/cyan
- 参考文献
1.简介
利用 Go 提供的 AES 加解密与 Base64 编解码包,我们可以轻松实现 AES 加解密。
实现之前,首先了解一下 AES 的基本知识。
AES(Advanced Encryption Standard)高级加密标准,是流行的对称加密算法,由美国国家标准与技术研究院(NIST)于 2001 年发布,用于取代 DES。Rijndael 算法 是 AES 标准的一个实现,一般说 AES 指的就是 Rijndael 算法。
区块长度
AES 是一种对称分组加密算法,区块长度固定为 128bits(16 字节)。
密钥长度
AES 秘钥的长度只能是16、24 或 32 字节,分别对应三种加密模式 AES-128、AES-192 和 AES-256,三者的区别是加密轮数不同。
AES | 分组长度(字节) | 密钥长度(字节) | 加密轮数 |
AES-128 | 16 | 16 | 10 |
AES-192 | 16 | 24 | 12 |
AES-256 | 16 | 32 | 14 |
加密模式
AES 支持 5 种加密模式。
AES(Advanced Encryption Standard)本身是一个块密码算法,它只能加密固定大小的数据块。然而,在实际应用中,我们通常需要加密更大的数据流或消息,并且需要解决一些特定的安全性问题,如数据传输中的完整性、随机性和错误传播问题。这就是为什么需要加密模式的原因。
不同的加密模式采用不同的策略来处理这些问题。以下是 AES 支持的常见加密模式。
- ECB(Electronic Codebook)模式: 最简单的模式,将明文分成固定大小的块,然后分别加密。这种模式的问题是相同的明文块会加密成相同的密文块,因此缺乏随机性和安全性。
- CBC(Cipher Block Chaining)模式: 每个明文块与前一个密文块进行异或操作,然后加密。这增加了随机性,并且在错误传播方面具有良好的性质,但需要一个初始化向量(IV)。
- CFB(Cipher Feedback)模式: 通过将前一个密文块作为输入与明文块进行异或操作,产生密文块。这种模式对于错误传播具有良好的性质,但也需要一个初始化向量。
- OFB(Output Feedback)模式: 类似于 CFB,但是通过生成一个密钥流来与明文进行异或操作。也对错误传播具有良好的性质。
- CTR(Counter)模式: 使用计数器来生成一个密钥流,然后与明文进行异或操作。在并行计算环境中效率较高。
2.加解密实现
下面以 CBC 模式为例,实现 AES 加解密。
package main
import (
"fmt"
"crypto/cipher"
"crypto/aes"
"bytes"
"encoding/base64"
)
// PKCS7Padding fills plaintext as an integral multiple of the block length
func PKCS7Padding(p []byte, blockSize int) []byte {
pad := blockSize - len(p)%blockSize
padtext := bytes.Repeat([]byte{byte(pad)}, pad)
return append(p, padtext...)
}
// PKCS7UnPadding removes padding data from the tail of plaintext
func PKCS7UnPadding(p []byte) []byte {
length := len(p)
paddLen := int(p[length-1])
return p[:(length - paddLen)]
}
// AESCBCEncrypt encrypts data with AES algorithm in CBC mode
// Note that key length must be 16, 24 or 32 bytes to select AES-128, AES-192, or AES-256
// Note that AES block size is 16 bytes
func AESCBCEncrypt(p, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
p = PKCS7Padding(p, block.BlockSize())
ciphertext := make([]byte, len(p))
blockMode := cipher.NewCBCEncrypter(block, key[:block.BlockSize()])
blockMode.CryptBlocks(ciphertext, p)
return ciphertext, nil
}
// AESCBCDecrypt decrypts cipher text with AES algorithm in CBC mode
// Note that key length must be 16, 24 or 32 bytes to select AES-128, AES-192, or AES-256
// Note that AES block size is 16 bytes
func AESCBCDecrypt(c, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
plaintext := make([]byte, len(c))
blockMode := cipher.NewCBCDecrypter(block, key[:block.BlockSize()])
blockMode.CryptBlocks(plaintext, c)
return PKCS7UnPadding(plaintext)
}
// Base64AESCBCEncrypt encrypts data with AES algorithm in CBC mode and encoded by base64
func Base64AESCBCEncrypt(p, key []byte) (string, error) {
c, err := AESCBCEncrypt(p, key)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(c), nil
}
// Base64AESCBCDecrypt decrypts cipher text encoded by base64 with AES algorithm in CBC mode
func Base64AESCBCDecrypt(c string, key []byte) ([]byte, error) {
oriCipher, err := base64.StdEncoding.DecodeString(c)
if err != nil {
return nil, err
}
p, err := AESCBCDecrypt(oriCipher, key)
if err != nil {
return nil, err
}
return p, nil
}
使用示例如下:
func main() {
p := []byte("plaintext")
key := []byte("12345678abcdefgh")
ciphertext, _ := Base64AESCBCEncrypt(p, key)
fmt.Println(ciphertext)
plaintext, _ := Base64AESCBCDecrypt(ciphertext, key)
fmt.Println(string(plaintext))
}
运行输出:
A67NhD3RBiNaMgG6HTm8LQ==
plaintext
3.dablelv/cyan
以上代码已放置实用函数库 dablelv/cyan,可直 import 直接使用,欢迎大家 star 和 pr。
import (
"github.com/dablelv/cyan/crypto"
)
p := []byte("plaintext")
key := []byte("12345678abcdefgh")
ciphertext, _ := crypto.Base64AESCBCEncrypt(p, key) // A67NhD3RBiNaMgG6HTm8LQ==
plaintext, _ := crypto.Base64AESCBCDecrypt(ciphertext, key) // plaintext
参考文献
crypto/aes - Go PackagesAES Encryption and Decryption Online Tool