我正在Golang(Go)测试AES 256 CBC实现.

plaintext: {"key1": "value1", "key2": "value2"}

因为明文是36 B,需要是块大小(16 B)的倍数,所以我用12个随机字节手动填充到48 B.

输入:

plaintext: aaaaaaaaaaaa{"key1": "value1", "key2": "value2"}
AES 256 key: b8ae2fe8669c0401fb289e6ab6247924
AES IV: e0332fc2a9743e4f

代码摘录从here中提取,但进行了一些修改:

block, err := aes.NewCipher(key)
if err != nil {
    fmt.Println("Error creating a new AES cipher by using your key!");
    fmt.Println(err);
    os.Exit(1);
}

ciphertext := make([]byte, aes.BlockSize+len(plaintext))

mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext, plaintext)

fmt.Printf("%x\n", ciphertext)
fmt.Println("len(ciphertext):",len(ciphertext))

密文=明文+块

这个方程给出了CBC密文的长度.

所以,行ciphertext := make([]byte, aes.BlockSize+len(plaintext))满足了这个要求,因为我的明文总是被填充为块大小的倍数.

Problem:

使用Go,我得到以下密文:

我总是在密文末尾得到16个0x00字节,无论明文的长度如何.

如果我对在线AES计算器执行相同操作,我会得到以下密文:

前48个字节caf8fe667f4087e1b67d8c9c57fcb1f56b368cafb4bfecbda1e481661ab7b93d87703fb140368d3034d5187c53861c74相同.但我缺少最后16个字节.

This表示:

可以通过比src大的dst,在这种情况下,

但为什么会这样呢?密文的长度需要大于明文的长度,在线AES计算器证明了这一点.

推荐答案

由于Go代码中使用的AES/CBC实现不会隐式填充,因此只有在满足大小标准的情况下,即如果明文大小是块大小的整数倍(AES为16字节),代码才会工作.对于这里的纯文本示例,后者通过explicit填充a来满足.在这些条件下,密文大小等于明文大小,即len(plaintext).

因为在Go代码中,ciphertext的大小分配给aes.BlockSize+len(plaintext),其中aes.BlockSize是AES块大小(16字节),ciphertext比实际密文大16字节,这是最后16个0x00值的原因.要删除这些,只需将ciphertext的大小分配为len(plaintext)即可

此外,由于明文通常不符合大小标准,因此应添加填充.可靠的填充是PKCS#7,它在Go中实现,例如pkcs7pad.

由于联机工具使用PKCS#7填充,以下Go代码提供了联机工具的结果:

import (
    ...
    "github.com/zenazn/pkcs7pad"
)
...
key := []byte("b8ae2fe8669c0401fb289e6ab6247924")
iv := []byte("e0332fc2a9743e4f")
plaintext := []byte("aaaaaaaaaaaa{\"key1\": \"value1\", \"key2\": \"value2\"}")
plaintext = pkcs7pad.Pad(plaintext, aes.BlockSize)
block, err := aes.NewCipher(key)
if err != nil {
    panic(err)
}
ciphertext := make([]byte, len(plaintext))
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext, plaintext)
fmt.Printf("%x\n", ciphertext)

如果已经满足大小标准,则PKCS#7填充会添加一个完整的块,如本例所示(因为使用a进行了显式填充),这就是密文比不使用PKCS#7填充的密文更长的原因.

请注意,由于PKCS#7填充,当然不再需要使用a进行显式填充.


由于密钥/IV对的重用是不安全的,因此静态IV(如代码中所示)也是不安全的.因此,为了避免这种情况,通常 for each 加密生成random IV.IV不是秘密的,需要解密,因此通常与密文连接(IV|ciphertext)

import (
    ...
    "crypto/rand"
    "io"
    "github.com/zenazn/pkcs7pad"
)
...
key := []byte("b8ae2fe8669c0401fb289e6ab6247924")
plaintext := []byte("{\"key1\": \"value1\", \"key2\": \"value2\"}")
plaintext = pkcs7pad.Pad(plaintext, aes.BlockSize)
block, err := aes.NewCipher(key)
if err != nil {
    panic(err)
}
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
_, err = io.ReadFull(rand.Reader, iv)
if err != nil {
    panic(err)
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
fmt.Printf("%x\n", ciphertext)

在此实现中,前16个字节对应于IV,其余字节对应于实际密文.解密过程中,两部分必须分开.

Go相关问答推荐

使用GO从RDPMC获得价值

如何将GoFr筛选器用于查询参数?

Go中的net.SplitHostPort(r.RemoteAddr)安全性

Golang使用Run()执行的命令没有返回

转到http服务器头内容-类型设置为多部分/表单-数据,但在客户端获取内容-类型:文本/纯文本

埃拉托塞尼筛:加快交叉关闭倍数步骤

如何配置vscode以在Go中显示不必要的(过度指定的)泛型?

如何使redis池的等待超时

如何获取集群外go Kubernetes客户端的当前命名空间?

如何在 Go msgraph-sdk-go 中转发消息并包括抄送和/或密送收件人?

判断不同 go map 类型中的重复键

Gorm 预加载给出了模糊的列错误

使用 golang 生成 vim

go-libp2p - 从流中接收字节

为什么在单独的 go routine 中需要 wg.Wait() 和 close() ?

如何在 golang 中同时加载 .env 文件和 os 环境变量

golang 如何从字符串中查找表情符号?

实现接口的指针的泛型类型是什么?

Go Flag 用法 描述 包含 Word 值

golangci-lint 的 GitHub 操作失败,无法加载 fmt