Golang (Go) AES CBC 密文由于某种原因被填充了 16 个 0x00 字节

我正在 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

从此处提取的代码摘录,但稍作修改:


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))

CipherText = PlainText + Block - (PlainText MOD Block)


这个等式给出了 CBC 的密文长度。


因此,该行ciphertext := make([]byte, aes.BlockSize+len(plaintext))满足此要求,因为我的明文始终被填充为块大小的倍数。


问题:


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


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


如果我用在线 AES 计算器做同样的事情,我会得到这个密文: caf8fe667f4087e1b67d8c9c57fcb1f56b368cafb4bfecbda1e481661ab7b93d87703fb140368d3034d5187c53861c74ccd202bac41937be75731f23796f1516


前 48 个字节caf8fe667f4087e1b67d8c9c57fcb1f56b368cafb4bfecbda1e481661ab7b93d87703fb140368d3034d5187c53861c74是相同的。但我遗漏了最后 16 个字节。


这说:


传递大于 src 的 dst 是可以接受的,在这种情况下,CryptBlocks 只会更新 dst[:len(src)] 而不会触及 dst 的其余部分。


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


摇曳的蔷薇
浏览 309回答 1
1回答

慕村225694

在线工具结果的密文,如果是明文:aaaaaaaaaaaa{"key1": "value1", "key2": "value2"}用 PKCS#7 填充,发布的密钥和 IV 是 UTF8 编码的。由于明文的大小(48 字节)已经是块大小(AES 为 16 字节)的整数倍,因此根据PKCS#7 填充规则填充一个完整的块,从而产生 64 字节的明文和密文。从问题中不清楚使用的是哪种在线工具,但可以使用任何可靠的加密工具(例如 CyberChef,s。这个在线计算。CyberChef 默认为 AES/CBC 应用 PKCS#7 填充。发布的代码会产生不同的密文,因为:没有应用 PKCS#7 填充。这使得密文短了一个块(即最后一个块ccd202bac41937be75731f23796f1516丢失了)。aes.BlockSize + len(plaintext)为密文分配了字节大小。这导致分配的大小字节太大aes.BlockSize(即密文末尾包含 16 个 0x00 值)。因此,要使 Go 代码生成与在线工具相同的密文,1. 必须添加 PKCS#7 填充,并且 2.len(plaintext)必须为密文分配仅字节的大小。以下代码是一个可能的实现(对于 PKCS#7 ,使用pkcs7pad填充):import (    ...    "github.com/zenazn/pkcs7pad")...key := []byte("b8ae2fe8669c0401fb289e6ab6247924")iv := []byte("e0332fc2a9743e4f")plaintext := []byte("aaaaaaaaaaaa{\"key1\": \"value1\", \"key2\": \"value2\"}")plaintext = pkcs7pad.Pad(plaintext, aes.BlockSize)  // 1. pad the plaintext with PKCS#7block, err := aes.NewCipher(key)if err != nil {    panic(err)}ciphertext := make([]byte, len(plaintext))          // 2. allocate len(plaintext)mode := cipher.NewCBCEncrypter(block, iv)mode.CryptBlocks(ciphertext, plaintext)fmt.Printf("%x\n", ciphertext) // caf8fe667f4087e1b67d8c9c57fcb1f56b368cafb4bfecbda1e481661ab7b93d87703fb140368d3034d5187c53861c74ccd202bac41937be75731f23796f1516请注意,由于 PKCS#7 填充,a不再需要显式填充。上述代码中使用的静态IV 是一个漏洞,因为它会导致密钥/IV 对的重用,这是不安全的。因此,在实践中,通常会为每次加密生成一个随机 IV。IV 不是秘密的,是解密所必需的,并且通常与密文连接在一起。在解密端,将IV和密文分开,用于解密。由于 IV 的大小对应于块大小,aes.BlockSize + len(plaintext)因此必须为密文分配一个大小,它等于原始代码中的大小。可能这不是偶然的,而是在设计时考虑了随机 IV,但后来没有实现。一个后续的实现是: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)   // create a random IVif err != nil {    panic(err)}mode := cipher.NewCBCEncrypter(block, iv)mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)fmt.Printf("%x\n", ciphertext)输出的前 16 个字节对应于(随机)IV,其余对应于实际密文。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go