使用 openssl 的 AES 加密

AES encryption using openssl

我正在尝试使用 openssl AES 加密功能进行加密。

这是我的代码:

#include <stdio.h>
#include <openssl/aes.h>
#include <string.h>

const static unsigned char aes_key[]={"passwordpasswor"}; //15 characters + [=10=]
void print_data(const char *tittle, const void* data, int len);

int main() {
unsigned char aes_input[]="#!/bin/bash\necho hello world";
unsigned char iv[AES_BLOCK_SIZE];
memset (iv,0x00,AES_BLOCK_SIZE);
unsigned char enc_out[sizeof(aes_input)];
unsigned char dec_out[sizeof(aes_input)];
AES_KEY enc_key,dec_key;
AES_set_encrypt_key(aes_key,sizeof(aes_key)*8,&enc_key);
AES_cbc_encrypt(aes_input,enc_out,sizeof(aes_input),&enc_key,iv,AES_ENCRYPT);
//decryption
memset(iv,0x00,AES_BLOCK_SIZE);
AES_set_decrypt_key(aes_key,sizeof(aes_key)*8,&dec_key);
AES_cbc_encrypt(enc_out,dec_out,sizeof(aes_input),&dec_key,iv,AES_DECRYPT);
//verify
printf("original %s\n",aes_input);
printf("encrypted %s\n",enc_out);
printf("decrypted %s\n",dec_out);
return 0;
}

代码产生以下输出(为清楚起见,每个输出之间有一个额外的换行符):

original #!/bin/bash
echo hello world

encrypted ���jv�.)��$I���b�:dmPvTQޜ�#!/bin/bash
echo hello world

decrypted #!/bin/bash
echo hello world

我试过其他的消息,好像用printf加密消息会显示原来的消息。

您已使用参数

对字符串的终止 nul 进行了加密
sizeof(aes_input)

你是一致的,所以它也被解密了。不幸的是,加密的字符串不再有 nul 终止符,因为它也被加密了。所以我推荐

strlen(aes_input)

用于参数(但不用于字符串分配)。您还必须终止两个字符串 enc_out[]dec_out[].

您正在尝试将不可打印的数据转储到终端设备,具体来说是使用期望空终止的库调用来执行此操作。 AES 加密的输出可以包含 any 值的字节(包括嵌入的 nullchar 值)

您需要以下内容:

  • 适当调整输出缓冲区大小。默认情况下 AES_cbc_encrypt 使用 pkcs 填充并将使用最多一个完整的附加块来填充数据。
  • 使用替代机制转储您的输出,例如简单的 hexdump 例程。

以上两个都在下面完成:

#include <stdio.h>
#include <openssl/aes.h>
#include <string.h>

static hex_print(const void *pv, size_t len)
{
    static const char alpha[] = "0123456789abcdef";
    const unsigned char *beg = pv, *end = beg+len;

    for (; beg != end; ++beg)
    {
        putc(alpha[(*beg >> 4) & 0xF], stdout);
        putc(alpha[*beg & 0xF], stdout);
    }
    putc('\n', stdout);
}

const static unsigned char aes_key[]={"passwordpasswor"}; //15 characters + [=10=]
void print_data(const char *tittle, const void* data, int len);

int main() {
    unsigned char aes_input[]="#!/bin/bash\necho hello world";
    unsigned char enc_out[AES_BLOCK_SIZE * ((sizeof(aes_input) + AES_BLOCK_SIZE)/AES_BLOCK_SIZE)];
    unsigned char dec_out[sizeof(aes_input)];
    unsigned char iv[AES_BLOCK_SIZE] = {0};

    AES_KEY enc_key,dec_key;
    AES_set_encrypt_key(aes_key,sizeof(aes_key)*8,&enc_key);
    AES_cbc_encrypt(aes_input,enc_out,sizeof(aes_input),&enc_key,iv,AES_ENCRYPT);

    //decryption
    memset(iv,0x00,AES_BLOCK_SIZE);
    AES_set_decrypt_key(aes_key,sizeof(aes_key)*8,&dec_key);
    AES_cbc_encrypt(enc_out,dec_out,sizeof(aes_input),&dec_key,iv,AES_DECRYPT);

    //verify
    printf("original %s\n",aes_input);
    hex_print(enc_out, sizeof enc_out);
    printf("decrypted %s\n",dec_out);
    return 0;
}

输出

original #!/bin/bash
echo hello world
e389c96a76d708b42e29b4b4052449f1ffc762db3a646d1650765451de9c1dd0
decrypted #!/bin/bash
echo hello world

请特别注意加密的 last 字节。它不是 00,这意味着您错误使用的 printf 调用正在超出该缓冲区并进入 未定义行为 的领域。事实上,该字符串中 no nullchar 字节(这是一个不同但密切相关的问题,当 一个嵌入的 00 在数据的中间,在这种情况下,printf过早

在这种情况下,我可以推测(价值不大;这就是未定义行为的本质)行军printf进入栈上的下一个自动变量,即解密后的数组。

修改输出序列以使用 all 十六进制输出将演示明文和加密数据之间的区别。例如,将程序的最后三个功能行更改为:

//verify
hex_print(aes_input, sizeof(aes_input));
hex_print(enc_out, sizeof enc_out);
hex_print(dec_out, sizeof(dec_out));

将提供以下输出:

23212f62696e2f626173680a6563686f2068656c6c6f20776f726c6400
e389c96a76d708b42e29b4b4052449f1ffc762db3a646d1650765451de9c1dd0
23212f62696e2f626173680a6563686f2068656c6c6f20776f726c6400

这是有道理的。如果您遍历原始字符串和解密字符串中的字节(每个两位数),您可以看到它们 (a) 相等,(b) 不等于密文,以及 (c) ascii table 将向您显示它们确实是原始短信。

祝你好运。