
ctr(counter)模式是一种块密码工作模式,它将块密码转换为流密码。与 cbc 或 ecb 模式不同,ctr 模式不直接对数据块进行加密,而是对一个不断递增的计数器进行加密,然后将加密后的计数器(称为密钥流)与明文进行 xor 运算得到密文。
CTR 模式具有以下显著特点:
Go 语言的 crypto/cipher 包提供了实现 CTR 模式所需的接口和函数。核心组件包括:
IV 的生成至关重要。对于 CTR 模式,IV 必须是不可预测且每次加密都独一无二的。通常,IV 的长度应与底层块密码的块大小相同。
import (
"crypto/rand"
"fmt"
"io"
)
// generateIV 负责生成一个适合加密使用的初始化向量 (IV)。
// IV 的长度通常与块密码的块大小相同。
func generateIV(blockSize int) ([]byte, error) {
iv := make([]byte, blockSize)
// 使用 crypto/rand 生成安全的随机 IV
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, fmt.Errorf("failed to generate IV: %w", err)
}
return iv, nil
}在 CTR 模式中,加密操作是将明文与密钥流进行 XOR。一个常见的实践是将生成的 IV 附加到密文的前面,以便解密时可以轻松地提取 IV。
需要特别注意的是 XORKeyStream 方法的使用。它接受源切片和目标切片。当源和目标是同一个切片时,操作会原地进行。
import (
"crypto/cipher"
)
// encrypt 使用 CTR 模式对数据进行加密,并将 IV 附加到密文前。
// 注意:此函数会修改输入的 plaintext 字节切片,将其转换为密文。
// 返回值为 IV + 密文。
func encrypt(block cipher.Block, plaintext []byte) ([]byte, error) {
iv, err := generateIV(block.BlockSize())
if err != nil {
return nil, err
}
stream := cipher.NewCTR(block, iv)
// XORKeyStream 会将 plaintext 的内容加密后写入 plaintext。
// 因此,plaintext 在此操作后变为密文。
// 这是一个原地操作,效率较高。
stream.XORKeyStream(plaintext, plaintext)
// 将 IV 附加到已变为密文的 plaintext 前返回
return append(iv, plaintext...), nil
}实现误区提示: 原始问题中 encrypted = append(encrypted, iv...) 后再 stream.XORKeyStream(encrypted, value) 的做法是错误的。append 会创建一个新的切片,其容量可能大于原 value 的长度。然后 XORKeyStream 会尝试处理整个 encrypted 切片,包括 IV 部分,导致结果不正确。正确的做法是先对明文进行加密,得到密文,然后将 IV 和密文拼接。如果 XORKeyStream 允许原地操作(即源和目标相同),则可以利用这一特性简化代码。
CTR 模式的解密过程与加密过程几乎相同,也是将密文与密钥流进行 XOR。首先需要从接收到的数据中提取 IV,然后使用相同的 IV 和密钥创建 CTR 流,最后执行 XORKeyStream。
import (
"crypto/cipher"
)
// decrypt 使用 CTR 模式对包含 IV 的密文进行解密。
// 注意:此函数会修改输入的 encryptedData 字节切片中表示密文的部分,将其转换为明文。
// 输入的 encryptedData 期望格式为 IV + 密文。
func decrypt(block cipher.Block, encryptedData []byte) ([]byte, error) {
blockSize := block.BlockSize()
if len(encryptedData) < blockSize {
return nil, fmt.Errorf("encrypted data too short to contain IV")
}
// 提取 IV
iv := encryptedData[:blockSize]
// 提取密文部分。注意:这里是切片,指向原底层数组。
ciphertextPart := encryptedData[blockSize:]
stream := cipher.NewCTR(block, iv)
// XORKeyStream 会将 ciphertextPart 的内容解密后写入 ciphertextPart。
// 因此,ciphertextPart 在此操作后变为明文。
// 同样是原地操作。
stream.XORKeyStream(ciphertextPart, ciphertextPart)
// 返回已变为明文的 ciphertextPart
return ciphertextPart, nil
}以下是一个完整的 Go 程序,演示了如何使用 AES 算法和 CTR 模式进行加密和解密:
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"io"
)
// generateIV 负责生成一个适合加密使用的初始化向量 (IV)。
// IV 的长度通常与块密码的块大小相同。
func generateIV(blockSize int) ([]byte, error) {
iv := make([]byte, blockSize)
// 使用 crypto/rand 生成安全的随机 IV
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, fmt.Errorf("failed to generate IV: %w", err)
}
return iv, nil
}
// encrypt 使用 CTR 模式对数据进行加密,并将 IV 附加到密文前。
// 注意:此函数会修改输入的 plaintext 字节切片,将其转换为密文。
// 返回值为 IV + 密文。
func encrypt(block cipher.Block, plaintext []byte) ([]byte, error) {
iv, err := generateIV(block.BlockSize())
if err != nil {
return nil, err
}
stream := cipher.NewCTR(block, iv)
// XORKeyStream 会将 plaintext 的内容加密后写入 plaintext。
// 因此,plaintext 在此操作后变为密文。
// 这是一个原地操作,效率较高。
stream.XORKeyStream(plaintext, plaintext)
// 将 IV 附加到已变为密文的 plaintext 前返回
return append(iv, plaintext...), nil
}
// decrypt 使用 CTR 模式对包含 IV 的密文进行解密。
// 注意:此函数会修改输入的 encryptedData 字节切片中表示密文的部分,将其转换为明文。
// 输入的 encryptedData 期望格式为 IV + 密文。
func decrypt(block cipher.Block, encryptedData []byte) ([]byte, error) {
blockSize := block.BlockSize()
if len(encryptedData) < blockSize {
return nil, fmt.Errorf("encrypted data too short to contain IV")
}
// 提取 IV
iv := encryptedData[:blockSize]
// 提取密文部分。注意:这里是切片,指向原底层数组。
ciphertextPart := encryptedData[blockSize:]
stream := cipher.NewCTR(block, iv)
// XORKeyStream 会将 ciphertextPart 的内容解密后写入 ciphertextPart。
// 因此,ciphertextPart 在此操作后变为明文。
// 同样是原地操作。
stream.XORKeyStream(ciphertextPart, ciphertextPart)
// 返回已变为明文的 ciphertextPart
return ciphertextPart, nil
}
func main() {
// 1. 创建 AES 密钥
// AES-128 密钥长度为 16 字节
key := []byte("1234567890123456")
block, err := aes.NewCipher(key)
if err != nil {
fmt.Printf("Error creating cipher block: %v\n", err)
return
}
// 2. 待加密的原始数据
// 注意:由于 encrypt 函数会修改原始切片,这里复制一份以保留原始数据用于比较。
originalData := []byte("foobarbaz")
dataToEncrypt := make([]byte, len(originalData))
copy(dataToEncrypt, originalData)
fmt.Printf("Original data: %s\n", string(originalData))
// 3. 加密数据
encryptedData, err := encrypt(block, dataToEncrypt) // 传入复制的切片
if err != nil {
fmt.Printf("Encryption error: %v\n", err)
return
}
fmt.Printf("Encrypted data (IV + Ciphertext, hex): %x\n", encryptedData)
// 以上就是使用 Go 语言实现 CTR 模式加密与解密教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号