我正在测试在Golang (Go)中的AES 256 CBC实现。
plaintext: {"key1": "value1", "key2": "value2"}
因为纯文本是36B,并且需要是块大小(16B)的倍数,我用12个随机字节手动填充到48B。我知道这不是最安全的方法,但我只是在测试,我会为生产设置找到一个更好的方法。
投入:
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 +块- (PlainText MOD块)
这个方程给出了CBC的密文长度。
因此,行ciphertext := make([]byte, aes.BlockSize+len(plaintext))
满足了这一要求,因为我的明文总是填充为块大小的倍数。
问题:
使用Go,我得到以下密文:caf8fe667f4087e1b67d8c9c57fcb1f56b368cafb4bfecbda1e481661ab7b93d87703fb140368d3034d5187c53861c7400000000000000000000000000000000
无论明文的长度如何,我总是在密文末尾得到160x00字节。
如果我对在线AES计算器做同样的操作,我会得到以下密文:caf8fe667f4087e1b67d8c9c57fcb1f56b368cafb4bfecbda1e481661ab7b93d87703fb140368d3034d5187c53861c74ccd202bac41937be75731f23796f1516
前48个字节的caf8fe667f4087e1b67d8c9c57fcb1f56b368cafb4bfecbda1e481661ab7b93d87703fb140368d3034d5187c53861c74
是相同的。但我错过了最后的16个字节。
这说:
传递大于src的dst是可以接受的,在这种情况下,CryptBlocks将只更新dst:len(src),而不会触及dst的其余部分。
但为什么会这样呢?密文的长度需要比明文的长度长,在线AES计算器证明了这一点。
发布于 2022-06-04 14:06:04
如果明文为明文,则在线工具的密文:
aaaaaaaaaaaa{"key1": "value1", "key2": "value2"}
是用PKCS#7填充的,发布的密钥和IV是UTF8编码的。由于明文的大小(48字节)已经是块大小的整数倍数(对于AES是16字节),因此根据PKCS#7填充的规则填充完整的块,从而生成64字节的明文和密文。
从使用哪一种在线工具的问题上还不清楚,但是可以使用任何可靠的加密工具(如CyberChef,s. 这个在线计算 )来重构已发布的密文。默认情况下,CyberChef为AES/CBC应用PKCS#7填充。
发布的代码产生不同的密文,因为:
aes.BlockSize + len(plaintext)
字节的大小。这会导致分配的大小被aes.BlockSize
字节过大(即密文在末尾包含160x00值)。因此,要使Go代码生成与联机工具相同的密文,必须添加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#7
block, 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 IV
if err != nil {
panic(err)
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
fmt.Printf("%x\n", ciphertext)
输出的前16个字节对应于(随机) IV,其余字节对应于实际的密文。
https://stackoverflow.com/questions/72499124
复制相似问题