0

0

使用 Go 语言实现 CTR 模式加密与解密教程

聖光之護

聖光之護

发布时间:2025-08-08 14:34:26

|

968人浏览过

|

来源于php中文网

原创

使用 Go 语言实现 CTR 模式加密与解密教程

本教程详细介绍了如何在 Go 语言中使用 crypto/cipher 包实现 CTR(Counter)模式的对称加密与解密。文章将深入探讨 CTR 模式的工作原理,特别是其加密和解密都使用 XORKeyStream 操作的特性。通过提供包含初始化向量(IV)生成、数据加密、IV 与密文合并存储以及数据解密的完整示例代码,本教程旨在帮助读者理解并正确应用 CTR 模式,同时指出常见的实现误区和最佳实践。

CTR 模式简介

ctr(counter)模式是一种块密码工作模式,它将块密码转换为流密码。与 cbc 或 ecb 模式不同,ctr 模式不直接对数据块进行加密,而是对一个不断递增的计数器进行加密,然后将加密后的计数器(称为密钥流)与明文进行 xor 运算得到密文。

CTR 模式具有以下显著特点:

  1. 流密码特性:它像流密码一样工作,可以直接加密任意长度的数据,无需填充。
  2. 可并行化:每个计数器块的加密是独立的,因此加密和解密过程可以并行执行,这对于高性能应用非常有利。
  3. 加密与解密操作相同:由于其基于 XOR 运算的特性,加密和解密都使用相同的 XORKeyStream 操作。这是本教程重点强调的一个概念。
  4. 初始化向量(IV):CTR 模式要求每次加密都使用一个唯一(但不必保密)的初始化向量(IV)。IV 通常与计数器结合使用,以确保即使使用相同的密钥加密相同的数据,也能产生不同的密文,从而增强安全性。IV 必须随密文一起传输给解密方。

Go 语言中的 CTR 模式实现

Go 语言的 crypto/cipher 包提供了实现 CTR 模式所需的接口和函数。核心组件包括:

  • cipher.Block 接口:表示一个块密码算法(如 AES)。通过 aes.NewCipher 创建。
  • cipher.NewCTR(block cipher.Block, iv []byte) 函数:根据块密码和 IV 创建一个 CTR 流。
  • stream.XORKeyStream(dst, src []byte) 方法:这是 CTR 模式进行加密和解密的核心操作。它将 src 中的数据与生成的密钥流进行 XOR 运算,结果写入 dst。

初始化向量(IV)的生成与管理

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。

白月生产企业订单管理系统GBK2.0  Build 080807
白月生产企业订单管理系统GBK2.0 Build 080807

请注意以下说明:1、本程序允许任何人免费使用。2、本程序采用PHP+MYSQL架构编写。并且经过ZEND加密,所以运行环境需要有ZEND引擎支持。3、需要售后服务的,请与本作者联系,联系方式见下方。4、本程序还可以与您的网站想整合,可以实现用户在线服务功能,可以让客户管理自己的信息,可以查询自己的订单状况。以及返点信息等相关客户利益的信息。这个功能可提高客户的向心度。安装方法:1、解压本系统,放在

下载

需要特别注意的是 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)

    // 

相关专题

更多
硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

991

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

51

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

232

2025.12.29

go语言 数组和切片
go语言 数组和切片

本专题整合了go语言数组和切片的区别与含义,阅读专题下面的文章了解更多详细内容。

45

2025.09.03

append用法
append用法

append是一个常用的命令行工具,用于将一个文件的内容追加到另一个文件的末尾。想了解更多append用法相关内容,可以阅读本专题下面的文章。

339

2023.10.25

python中append的用法
python中append的用法

在Python中,append()是列表对象的一个方法,用于向列表末尾添加一个元素。想了解更多append的更多内容,可以阅读本专题下面的文章。

1062

2023.11.14

python中append的含义
python中append的含义

本专题整合了python中append的相关内容,阅读专题下面的文章了解更多详细内容。

167

2025.09.12

页面置换算法
页面置换算法

页面置换算法是操作系统中用来决定在内存中哪些页面应该被换出以便为新的页面提供空间的算法。本专题为大家提供页面置换算法的相关文章,大家可以免费体验。

389

2023.08.14

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

74

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号