在 C 中使用 AES-256 和 openssl 计算 CBC-MAC
Compute the CBC-MAC with AES-256 and openssl in C
我想用 openssl 计算给定明文的 CBC-MAC。我有以下明文 (hexdump):
hexdump -C example.txt
00000000 4d 41 43 73 20 61 72 65 20 76 65 72 79 20 75 73 |MACs are very us|
00000010 65 66 75 6c 20 69 6e 20 63 72 79 70 74 6f 67 72 |eful in cryptogr|
00000020 61 70 68 79 21 20 20 20 20 20 20 20 20 20 20 20 |aphy! |
如果我使用openssl的命令行功能,我得到以下解决方案:
openssl aes-256-cbc -in example.txt -K 8000000000000000000000000000000000000000000000000000000000000001 -e -iv 00 | hexdump -C
00000000 e8 e9 a4 ce 5d 20 c4 ad f5 52 b2 c6 38 2e 12 4e |....] ...R..8..N|
00000010 20 f5 63 65 b4 b3 96 9f ad 8d ca e4 e8 34 2a e5 | .ce.........4*.|
00000020 0d 82 0e 3a 1e 10 5d 30 72 16 fc 00 c7 a5 b4 49 |...:..]0r......I|
00000030 f5 63 9f 85 ff e3 a4 a4 23 6e 6f 09 20 ed b1 ae |.c......#no. ...|
到目前为止一切顺利。我有一个额外的块,因为第一个块应该是加密的 IV。现在最后一行应该是我的 CBC-MAC,如果我没有理解错的话。接下来我尝试在 C 中做同样的事情,这里是示例代码:
#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>
#include <openssl/aes.h>
int main(int argc, char *argv[])
{
unsigned char obuf[64] = {0};
unsigned char decbuf[48] = {0};
unsigned char msg1[] = {0x4d, 0x41, 0x43, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, 0x75, 0x73,
0x65, 0x66, 0x75, 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72,
0x61, 0x70, 0x68, 0x79, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20};
unsigned char key[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
unsigned char ivenc[] ={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char ivdec[] ={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
int i=0;
AES_KEY enc_key, dec_key;
AES_set_encrypt_key(key, 256, &enc_key);
AES_cbc_encrypt(msg1, obuf, 48, &enc_key, ivenc, AES_ENCRYPT);
for (i = 0; i < 64; i++) {
if (!(i%16))
printf("\n");
printf("%02x ", obuf[i]);
}
printf("\n");
AES_set_decrypt_key(key, 256, &dec_key);
AES_cbc_encrypt(obuf, decbuf, 64, &dec_key, ivdec, AES_DECRYPT);
for (i = 0; i < 48; i++) {
if (!(i%16))
printf("\n");
printf("%02x ", decbuf[i]);
}
printf("\n");
return 0;
}
我之后解密加密的消息以验证我的代码。我的代码的输出非常令人惊讶:
e8 e9 a4 ce 5d 20 c4 ad f5 52 b2 c6 38 2e 12 4e
20 f5 63 65 b4 b3 96 9f ad 8d ca e4 e8 34 2a e5
0d 82 0e 3a 1e 10 5d 30 72 16 fc 00 c7 a5 b4 49
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
4d 41 43 73 20 61 72 65 20 76 65 72 79 20 75 73
65 66 75 6c 20 69 6e 20 63 72 79 70 74 6f 67 72
61 70 68 79 21 20 20 20 20 20 20 20 20 20 20 20
密文与命令行输出完全一致,只是最后一行全为0。我想,第一行是密文IV,后面三行是密文,所以按照我的解释消息的最后一行未加密。但令我惊讶的是,解密结果与我用作输入的文本完全一致,因此似乎没有丢失信息。
问题:
- 我怎么可能解密我的加密输出,即使我没有最后一行?
- 我的 CBC-MAC 是什么?它是我的命令行输出的最后一行还是我的 C 代码输出的最后一行?
- 我的 C 代码是不是做错了什么?我使用 this so question 作为帮助。
你的错误在这里:
I have one extra block because the first block should be the encrypted IV.
额外的块是因为 OpenSSL 在纯文本中添加了填充,因此它是块大小的倍数(AES 为 16 字节)。在这种情况下,明文已经是 16 字节的倍数,但使用的填充方案 (PKCS7) always 会添加填充,因此这里在加密之前添加了整个块。
通常将 IV 添加到密文的前面,但这不是这里发生的事情。
为了从您的代码中获得相同的结果,您需要自己添加此填充。在这种情况下它相当简单,只需在 msg1
的末尾添加 16 个 0x10
字节(因此它的总长度为 64),并将调用中的 48
更改为 AES_cbc_encrypt
到 64
。您看到的零只是您初始化 obuf
的值,因为您只将 48 个字节写入此缓冲区。
我想用 openssl 计算给定明文的 CBC-MAC。我有以下明文 (hexdump):
hexdump -C example.txt
00000000 4d 41 43 73 20 61 72 65 20 76 65 72 79 20 75 73 |MACs are very us|
00000010 65 66 75 6c 20 69 6e 20 63 72 79 70 74 6f 67 72 |eful in cryptogr|
00000020 61 70 68 79 21 20 20 20 20 20 20 20 20 20 20 20 |aphy! |
如果我使用openssl的命令行功能,我得到以下解决方案:
openssl aes-256-cbc -in example.txt -K 8000000000000000000000000000000000000000000000000000000000000001 -e -iv 00 | hexdump -C
00000000 e8 e9 a4 ce 5d 20 c4 ad f5 52 b2 c6 38 2e 12 4e |....] ...R..8..N|
00000010 20 f5 63 65 b4 b3 96 9f ad 8d ca e4 e8 34 2a e5 | .ce.........4*.|
00000020 0d 82 0e 3a 1e 10 5d 30 72 16 fc 00 c7 a5 b4 49 |...:..]0r......I|
00000030 f5 63 9f 85 ff e3 a4 a4 23 6e 6f 09 20 ed b1 ae |.c......#no. ...|
到目前为止一切顺利。我有一个额外的块,因为第一个块应该是加密的 IV。现在最后一行应该是我的 CBC-MAC,如果我没有理解错的话。接下来我尝试在 C 中做同样的事情,这里是示例代码:
#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>
#include <openssl/aes.h>
int main(int argc, char *argv[])
{
unsigned char obuf[64] = {0};
unsigned char decbuf[48] = {0};
unsigned char msg1[] = {0x4d, 0x41, 0x43, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, 0x75, 0x73,
0x65, 0x66, 0x75, 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72,
0x61, 0x70, 0x68, 0x79, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20};
unsigned char key[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
unsigned char ivenc[] ={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char ivdec[] ={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
int i=0;
AES_KEY enc_key, dec_key;
AES_set_encrypt_key(key, 256, &enc_key);
AES_cbc_encrypt(msg1, obuf, 48, &enc_key, ivenc, AES_ENCRYPT);
for (i = 0; i < 64; i++) {
if (!(i%16))
printf("\n");
printf("%02x ", obuf[i]);
}
printf("\n");
AES_set_decrypt_key(key, 256, &dec_key);
AES_cbc_encrypt(obuf, decbuf, 64, &dec_key, ivdec, AES_DECRYPT);
for (i = 0; i < 48; i++) {
if (!(i%16))
printf("\n");
printf("%02x ", decbuf[i]);
}
printf("\n");
return 0;
}
我之后解密加密的消息以验证我的代码。我的代码的输出非常令人惊讶:
e8 e9 a4 ce 5d 20 c4 ad f5 52 b2 c6 38 2e 12 4e
20 f5 63 65 b4 b3 96 9f ad 8d ca e4 e8 34 2a e5
0d 82 0e 3a 1e 10 5d 30 72 16 fc 00 c7 a5 b4 49
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
4d 41 43 73 20 61 72 65 20 76 65 72 79 20 75 73
65 66 75 6c 20 69 6e 20 63 72 79 70 74 6f 67 72
61 70 68 79 21 20 20 20 20 20 20 20 20 20 20 20
密文与命令行输出完全一致,只是最后一行全为0。我想,第一行是密文IV,后面三行是密文,所以按照我的解释消息的最后一行未加密。但令我惊讶的是,解密结果与我用作输入的文本完全一致,因此似乎没有丢失信息。
问题:
- 我怎么可能解密我的加密输出,即使我没有最后一行?
- 我的 CBC-MAC 是什么?它是我的命令行输出的最后一行还是我的 C 代码输出的最后一行?
- 我的 C 代码是不是做错了什么?我使用 this so question 作为帮助。
你的错误在这里:
I have one extra block because the first block should be the encrypted IV.
额外的块是因为 OpenSSL 在纯文本中添加了填充,因此它是块大小的倍数(AES 为 16 字节)。在这种情况下,明文已经是 16 字节的倍数,但使用的填充方案 (PKCS7) always 会添加填充,因此这里在加密之前添加了整个块。
通常将 IV 添加到密文的前面,但这不是这里发生的事情。
为了从您的代码中获得相同的结果,您需要自己添加此填充。在这种情况下它相当简单,只需在 msg1
的末尾添加 16 个 0x10
字节(因此它的总长度为 64),并将调用中的 48
更改为 AES_cbc_encrypt
到 64
。您看到的零只是您初始化 obuf
的值,因为您只将 48 个字节写入此缓冲区。