☰
《Go Web 编程》9.6 加密与解密资料:base64、AES 与 DES 对称加密实战指南
2026/10/7 21:14:35 网站建设 项目流程
  • 文档
  • 教程

【免费下载链接】build-web-application-with-golang

A golang ebook intro how to build a web with golang

项目地址:https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang
点击查看免费下载

导读

本指南对应《Go Web 编程》(build-web-application-with-golang)第 9.6 节「加密和解密資料」,聚焦于 Web 应用中最常见的对称加密需求:当我们需要把敏感资料(如支付凭证、接口令牌、密钥备份)加密后持久化存储,并在未来某个时刻按需解密还原时,应选用对称加密算法。读完本文,你将掌握 Go 语言中encoding/base64的简易加解密写法、crypto/aes与crypto/des高级对称加密套件的完整使用流程、cipher.Block接口的底层原理,以及基于仓库英文版演进出的 AES-GCM 现代实践建议,可直接落地到自己的 Web 服务中。

base64 加解密:最简单的资料变换方案

如果 Web 应用足够简单,对资料安全性没有严格要求,可以采用一种实现成本最低的加解密方式——base64。它本质上是编码(encoding)而非加密(encryption),并不具备密钥,任何人都能直接解码还原原文,因此只适合用于「防肉眼直读」而非「防攻击者解密」的场景。

Go 语言的encoding/base64标准包已经完整支持这一能力。下面是仓库中 zh-tw/09.6.md 提供的完整示例:

package main import ( "encoding/base64" "fmt" ) func base64Encode(src []byte) []byte { return []byte(base64.StdEncoding.EncodeToString(src)) } func base64Decode(src []byte) ([]byte, error) { return base64.StdEncoding.DecodeString(string(src)) } func main() { // encode hello := "你好,世界! hello world" debyte := base64Encode([]byte(hello)) fmt.Println(debyte) // decode enbyte, err := base64Decode(debyte) if err != nil { fmt.Println(err.Error()) } if hello != string(enbyte) { fmt.Println("hello is not equal to enbyte") } fmt.Println(string(enbyte)) }

运行该程序,会先打印 base64 编码后的字符串,随后打印还原后的原文。代码的关键点在于:

  • base64.StdEncoding.EncodeToString(src)将[]byte编码为标准 base64 字符串;
  • base64.StdEncoding.DecodeString(string(src))将 base64 字符串解码回[]byte,解码可能失败(输入不是合法 base64 时返回错误),所以函数签名中必须携带error返回值;
  • 编码与解码互为逆运算,程序中用hello != string(enbyte)的比对来验证还原正确性。

如果你需要把编码结果放进 URL 查询参数、Cookie 或文件名中,标准库还提供了更适合的变体:base64.URLEncoding(使用-与_代替+与/,避免 URL 转义问题)、base64.RawStdEncoding与base64.RawURLEncoding(去除末尾的=填充符)。它们的使用方式与StdEncoding完全一致,只需替换对应的编码器常量。

高级加解密:Go crypto 家族的对称加密套件

当业务对安全性的要求上升,需要真正的密钥保护时,应改用对称加密算法。Go 语言的crypto标准库中提供了两类主流的对称加密套件:

  • crypto/aes套件:AES(Advanced Encryption Standard,高级加密标准),又称 Rijndael 加密法,是美国联邦政府采用的区块加密标准,也是当今事实上的对称加密主流选择;
  • crypto/des套件:DES(Data Encryption Standard,资料加密标准),是一种对称加密标准,曾是保护金融资料安全中使用最广泛的密钥系统,也是美国联邦政府的旧加密标准,但如今已被 AES 取代。

由于这两种算法的使用方法高度相似(都通过NewCipher创建分组密码、再配合分组模式进行流式加解密),本节以crypto/aes为例完整演示。

AES-CFB 完整示例:加密与解密「我的名字是 Astaxie」

仓库 zh-tw/09.6.md 给出的 AES 示例支持从命令行参数传入待加密资料与密钥,便于直接运行验证:

package main import ( "crypto/aes" "crypto/cipher" "fmt" "os" ) var commonIV = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f} func main() { //需要去加密的字串 plaintext := []byte("My name is Astaxie") //如果傳入加密串的話,plaint 就是傳入的字串 if len(os.Args) > 1 { plaintext = []byte(os.Args[1]) } //aes 的加密字串 key_text := "astaxie12798akljzmknm.ahkjkljl;k" if len(os.Args) > 2 { key_text = os.Args[2] } fmt.Println(len(key_text)) // 建立加密演算法 aes c, err := aes.NewCipher([]byte(key_text)) if err != nil { fmt.Printf("Error: NewCipher(%d bytes) = %s", len(key_text), err) os.Exit(-1) } //加密字串 cfb := cipher.NewCFBEncrypter(c, commonIV) ciphertext := make([]byte, len(plaintext)) cfb.XORKeyStream(ciphertext, plaintext) fmt.Printf("%s=>%x\n", plaintext, ciphertext) // 解密字串 cfbdec := cipher.NewCFBDecrypter(c, commonIV) plaintextCopy := make([]byte, len(plaintext)) cfbdec.XORKeyStream(plaintextCopy, ciphertext) fmt.Printf("%x=>%s\n", ciphertext, plaintextCopy) }

这段代码的完整执行链路可以拆解为四步:

  1. 确定明文与密钥:明文默认是"My name is Astaxie",密钥默认是"astaxie12798akljzmknm.ahkjkljl;k",均可用命令行参数覆盖(go run main.go、go run main.go "你的明文"、go run main.go "你的明文" "你的密钥");
  2. 创建分组密码:调用aes.NewCipher([]byte(key_text))校验密钥长度并生成cipher.Block;密钥必须为 16、24 或 32 字节的[]byte,分别对应AES-128、AES-192 或 AES-256三种算法强度,长度不符会直接返回错误并被os.Exit(-1)终止;
  3. 加密:用cipher.NewCFBEncrypter(c, commonIV)创建 CFB 模式的加密器,再调用XORKeyStream将明文逐块异或为密文。注意 CFB 模式需要初始化向量commonIV(示例中为 16 字节的全 0~F 序列),加密与解密必须使用同一个 IV才能正确还原;
  4. 解密:用cipher.NewCFBDecrypter(c, commonIV)创建 CFB 模式解密器,对同一段密文再次调用XORKeyStream,得到还原后的明文。

程序依次输出密钥长度、明文=>密文(十六进制)与密文(十六进制)=>明文,可作为对称加密「加密—解密」闭环的最小可运行参考。

cipher.Block 接口:三个方法撑起加解密核心

上面的示例中,aes.NewCipher返回的是一个cipher.Block接口,它定义了分组密码的基础能力。仓库文档完整给出了该接口的声明:

type Block interface { // BlockSize returns the cipher's block size. BlockSize() int // Encrypt encrypts the first block in src into dst. // Dst and src may point at the same memory. Encrypt(dst, src []byte) // Decrypt decrypts the first block in src into dst. // Dst and src may point at the same memory. Decrypt(dst, src []byte) }

这三个方法的分工如下:

  • BlockSize() int:返回分组的字节数,AES 固定为 16 字节(128 bit),DES 为 8 字节,这是选择初始化向量长度、判断密文对齐的依据;
  • Encrypt(dst, src []byte):加密第一个分组,将src写入dst,dst与src允许指向同一块内存(原地操作);
  • Decrypt(dst, src []byte):解密第一个分组,规则同上。

需要注意的是,Block接口本身只处理「单个分组」的加解密,无法直接处理任意长度的连续资料流。因此实际项目中还要借助crypto/cipher包的分组模式——示例中的NewCFBEncrypter/NewCFBDecrypter(CFB 密码反馈模式)就是通过XORKeyStream把Block包装成可连续处理的流式密码。常用的同类包装还有 CBC(NewCBCEncrypter)、CTR(NewCTREncrypter)与 GCM(NewGCM)等模式,可按吞吐量、是否需认证等需求选择。

进阶建议:优先采用 AES-GCM 认证加密

仓库英文版文档 en/09.6.md 在演进中给出了更贴合现代安全实践的结论:如果不确定自己在做什么,除 GCM 模式(Galois/Counter Mode,伽罗瓦计数器模式)的 AES 外不要使用其他任何方案。这一建议的缘由是:CFB/CBC/CTR 等传统模式只提供机密性,不提供完整性保护,密文在传输或存储中被篡改时无法被察觉;而 GCM 是一种认证加密(AEAD)模式,在同一套运算中同时完成加密与消息认证,密文被改动会被立即识别。

仓库英文版配套的 AES-GCM 示例(见 en/09.6.md 正文)展示了推荐写法——使用随机 nonce 而非固定 IV:

func encrypt(plaintext []byte, key []byte) ([]byte, error) { c, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(c) if err != nil { return nil, err } nonce := make([]byte, gcm.NonceSize()) if _, err = io.ReadFull(rand.Reader, nonce); err != nil { return nil, err } return gcm.Seal(nonce, nonce, plaintext, nil), nil } func decrypt(ciphertext []byte, key []byte) ([]byte, error) { c, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(c) if err != nil { return nil, err } nonceSize := gcm.NonceSize() if len(ciphertext) < nonceSize { return nil, errors.New("ciphertext too short") } nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:] return gcm.Open(nil, nonce, ciphertext, nil) }

与 CFB 示例相比,AES-GCM 写法有三个关键差异,值得在实战中沿用:

  1. nonce 随机生成:nonce(等价于 IV)由rand.Reader每次随机产生,不再使用硬编码的固定值,避免相同密钥与相同 IV 重复使用导致的安全性退化;
  2. nonce 与密文一起保存:gcm.Seal(nonce, nonce, plaintext, nil)把 nonce 直接作为密文的前缀输出,解密时用ciphertext[:nonceSize]切分取出,因此无需额外管理 IV 的同步问题;
  3. 内置完整性校验:gcm.Open在解密的同时验证认证标签,密文被篡改或 nonce 不对应时会返回错误,而decrypt中的errors.New("ciphertext too short")则先防御性地拦截过短输入。

总结

本小节围绕 Web 应用中「敏感资料加密存储、按需解密」的需求,介绍了三种层次的加解密方案,可以按业务对安全性的要求灵活选用:

  • base64:实现最简单,适合对安全性要求不高、仅需防直读的场景,属于编码而非加密,不具备密钥与保密性;
  • AES / DES 对称加密:通过aes.NewCipher(密钥必须为 16/24/32 字节,对应 AES-128/192/256)返回cipher.Block,再配合 CFB 等分组模式完成任意长度资料的加密与解密,适合常规敏感资料的落库保护;
  • AES-GCM 认证加密:现代实践的推荐方案,同时提供机密性与完整性校验,nonce 随机生成并随密文存储,可直接作为生产级实现的起点。

需要说明的是,本节的对称加密解决的是「加密后存储、按需解密」的问题;若目标是「只验证、不解密」的密码存储,则应采用上一节 9.5 存储密码 中介绍的加盐杂凑与 scrypt 方案。两种手段各司其职,共同构成 Web 应用资料安全的基础防线。

延伸阅读

  • 目录
  • 上一节:存储密码
  • 下一节:小结
  • 英文版: 9.6 Encrypting and decrypting data(含 AES-GCM 完整示例)
  • 简体中文版:9.6 加密和解密数据
  • 仓库配套示例代码目录:en/code/src(各章节可运行示例,运行前请参考 en/code/readme.md 设置GOPATH)
  • 文档
  • 教程

【免费下载链接】build-web-application-with-golang

A golang ebook intro how to build a web with golang

项目地址:https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang
点击查看免费下载
上一篇:TREK 自托管部署排障指南:从会话 Cookie 到 MCP 集成的全链路问题排查
下一篇:developer-roadmap AI Engineer:确定性评测(Deterministic Evals)规则化评估实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询