Go 里的 AES 加密与解密:看懂 ECB、PKCS7Padding 及跨语言兼容关键点

Finn
Finn
发布时间:2026-08-25| 111人浏览过
原创

前言

在 Go 里实现 AES 并不难,真正影响结果是否可用的,往往是填充方式、工作模式和输出编码这些细节有没有和对端保持一致。本文从 AES 的固定规则讲起,再拆开 PKCS7Padding 与 ECB 的实现逻辑,最后用一段可直接复用的 Go 示例说明,怎样更稳地对接 Java、PHP 等常见实现。

在 Go 里做 AES 加解密,真正容易出错的往往不是 aes.NewCipher 这一层,而是模式、填充和跨语言约定是否一致。本文按“先理解规则,再看代码落地”的顺序整理 AES 的基础概念、PKCS7Padding 的处理方式,以及 AES-ECB-PKCS7Padding 的完整示例,方便你快速判断 Go 实现怎样才能和 Java、PHP 等语言对得上。

AES 基础:先看块大小和密钥长度

AES 是密码学中的高级加密标准,英文全称为 Advanced Encryption Standard,也常被称为 Rijndael。它被用来替代更早期的 DES。

在使用 AES 前,有两个基础参数需要先明确:

  • 数据块分组长度固定为 128bit
  • 密钥长度可以是 128bit、192bit、256bit

这意味着,无论你选择哪种工作模式,AES 处理数据时都以固定块大小运作;而密钥长度则决定你实际使用的是哪一档 AES 规格。实际开发里,如果你要和其他语言互通,首先要确认的就是双方是否使用了同样的密钥长度、模式和填充方式。

如果你希望直接使用现成封装,文中推荐的 Go 加解密库是 forgoer/openssl,项目地址为:https://github.com/forgoer/openssl。

这个库支持的加密模式包括:

  • AES-ECB/AES-CBC
  • DES-ECB/DES-CBC
  • 3DES-ECB/3DES-CBC
  • ...

填充怎么选:ZeroPadding、PKCS7Padding、PKCS5Padding

块加密算法有一个绕不开的问题:明文长度往往不是块大小的整数倍。此时就需要先做填充,再进入加密流程。常见的填充方式主要有三种。

用纸面卡片说明 AES 的块大小、密钥长度与三种填充方式差异
AES 参数与填充方式一图看懂先看 AES 的固定块大小,再区分 ZeroPadding、PKCS7Padding、PKCS5。

ZeroPadding

ZeroPadding 的特点是:当数据长度不对齐时,用 0 进行补齐;如果原始数据本身已经对齐,则不再填充。

它实现简单,但在某些场景下不够稳妥,因为原文末尾如果本来就可能出现 0,解密后边界判断会变得不够明确。

PKCS7Padding

PKCS7Padding 是跨语言开发中更常见的一种方案。规则是:假设当前数据还差 n 个字节才能对齐,那么就补 n 个字节,并且每个字节的值都写成 n。

用步骤便签展示 AES-ECB-PKCS7Padding 的完整加解密流程和关键输出
AES-ECB-PKCS7Padding 加把示例拆成四步后,更容易看清哪里决定了跨语言结果是否一致。

它还有一个很关键的细节:如果原始数据长度本身已经对齐,也要额外再补一整块,每个字节都等于块大小。正因为这个规则明确,解密时才能可靠地把填充去掉。

PKCS5Padding

PKCS5Padding 可以理解为 PKCS7Padding 的子集,区别在于它的块大小固定为 8 字节。遇到 AES 这类以 16 字节分组的算法时,实际开发里更常见的说法仍然是 PKCS7Padding。

Go 中的 PKCS7Padding 实现

下面这段代码给出了常见的填充与去填充方法,适合直接放进 Go 项目中使用:

func PKCS7Padding(src []byte, blockSize int) []byte {
    padding := blockSize - len(src)%blockSize
    padtext := bytes.Repeat([]byte{byte(padding)}, padding)
    return append(src, padtext...)
}

func PKCS7UnPadding(src []byte) []byte {
    length := len(src)
    unpadding := int(src[length-1])
    return src[:(length - unpadding)]
}

这段实现的核心很直接:

  • PKCS7Padding 先计算还差多少字节补齐,再把对应数量的字节追加到原数据尾部
  • PKCS7UnPadding 则读取最后一个字节的值,把尾部补齐内容裁掉

工作模式决定加密过程怎么跑

AES 的工作模式,决定的是“明文块如何被处理成密文块”。同一个 AES 算法,在不同工作模式下,数据处理过程并不相同。

常见的五种工作模式包括:

  • CBC
  • ECB
  • CTR
  • CFB
  • OFB

这篇文章重点展示的是 ECB 模式,因为后文示例使用的是 AES-ECB-PKCS7Padding 组合。

ECB 模式的 Go 实现

func ECBEncrypt(block cipher.Block, src, key []byte) ([]byte, error) {
    blockSize := block.BlockSize()

    encryptData := make([]byte, len(src))
    tmpData := make([]byte, blockSize)

    for index := 0; index < len(src); index += blockSize {
        block.Encrypt(tmpData, src[index:index+blockSize])
        copy(encryptData, tmpData)
    }
    return encryptData, nil
}

func ECBDecrypt(block cipher.Block, src, key []byte) ([]byte, error) {
    dst := make([]byte, len(src))

    blockSize := block.BlockSize()
    tmpData := make([]byte, blockSize)

    for index := 0; index < len(src); index += blockSize {
        block.Decrypt(tmpData, src[index:index+blockSize])
        copy(dst, tmpData)
    }

    return dst, nil
}

从代码结构看,ECB 的做法就是把数据按块切开,然后逐块调用 block.Encrypt 或 block.Decrypt。因此它对输入长度有明确要求:进入这一步之前,数据必须已经完成对齐,也就是必须先做填充。

AES-ECB-PKCS7Padding:一段完整的加解密示例

AES/ECB/PKCS7Padding 是实际项目里较常见的一组组合,尤其是在需要兼容历史系统或对接 Java、PHP 等现有实现时,经常会遇到。

下面是完整示例:

    // 加密
    src := []byte("123456")
    key := []byte("1234512345123451")

    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }

    src = PKCS7Padding(src, block.BlockSize())

    dst, err := ECBEncrypt(block, src, key)
    if err != nil {
        panic(err)
    }

    fmt.Println(base64.StdEncoding.EncodeToString(dst)) // SpfAShHImQhWjd/21Pgz2Q==

    // 解密
    src, err = ECBDecrypt(block, dst, key)
    if err != nil {
        panic(err)
    }

    src = PKCS7UnPadding(src)

    fmt.Println(string(src)) // 123456

这段示例里有哪些关键点

  • 明文是 "123456"
  • 密钥是 "1234512345123451",长度为 16 字节,对应 128bit
  • 在进入 ECB 加密前,先调用 PKCS7Padding 做补齐
  • 加密结果再通过 base64.StdEncoding.EncodeToString 转成字符串输出
  • 示例输出密文为 SpfAShHImQhWjd/21Pgz2Q==
  • 解密后还要执行一次 PKCS7UnPadding,才能还原得到原始字符串 123456

如果你在和 Java、PHP 对接时发现结果不一致,通常就从这几个点开始排查:密钥长度是否一致、模式是不是都为 ECB、填充是否同为 PKCS7Padding、输出编码是否都使用了 Base64。

落地时怎么判断能否兼容其他语言

Go 里做 AES 加解密,代码本身并不复杂,真正决定结果是否一致的是参数约定。只要块算法、密钥长度、工作模式、填充方式和编码输出完全一致,Go 通常就能和 Java、PHP 等语言的实现互通。

如果你不想自己维护底层细节,也可以直接参考或复用 forgoer/openssl 的完整实现。关于 Aes、Des 等加密的更多完整代码,可以查看:https://github.com/forgoer/openssl。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。