使用C语言的openssl库进行简单的AES加密解密

Sus*_*ath 6 c encryption openssl cryptography aes

我想加密一个包含少量String的结构,然后解密它.我试过以下代码.原始代码可以从网上找到,并且工作正常.我将它的输入更改为结构.以下是代码.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <openssl/aes.h>
#include <openssl/rand.h>

typedef struct ticket { /* test field */
int ticketId;
char username[20];
    char date[20];
} USR_TICKET;

// a simple hex-print routine. could be modified to print 16 bytes-per-line
static void hex_print(const void* pv, size_t len)
{
const unsigned char * p = (const unsigned char*)pv;
if (NULL == pv)
    printf("NULL");
else
{
    size_t i = 0;
    for (; i<len;++i)
        printf("%02X ", *p++);
}
printf("\n");
}

// main entrypoint
int main(int argc, char **argv)
{
    int keylength;
    printf("Give a key length [only 128 or 192 or 256!]:\n");
    scanf("%d", &keylength);

    /* generate a key with a given length */
    unsigned char aes_key[keylength/8];
    memset(aes_key, 0, keylength/8);
    if (!RAND_bytes(aes_key, keylength/8))
        exit(-1);

    /* input struct creation */
    size_t inputslength = sizeof(USR_TICKET);
    USR_TICKET ticket;
    ticket.ticketId = 1;
    time_t now = time(NULL);
    strftime(ticket.date, 20, "%Y-%m-%d", localtime(&now));
    strcpy(ticket.username, "ravinda");

    printf("Username - %s\n", ticket.username);
    printf("Ticket Id - %d\n", ticket.ticketId);
    printf("Date - %s\n", ticket.date);

    /* init vector */
    unsigned char iv_enc[AES_BLOCK_SIZE], iv_dec[AES_BLOCK_SIZE];
    RAND_bytes(iv_enc, AES_BLOCK_SIZE);
    memcpy(iv_dec, iv_enc, AES_BLOCK_SIZE);

    // buffers for encryption and decryption
    const size_t encslength = ((inputslength + AES_BLOCK_SIZE) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
    unsigned char enc_out[encslength];
    unsigned char dec_out[inputslength];
    memset(enc_out, 0, sizeof(enc_out));
    memset(dec_out, 0, sizeof(dec_out));

    // so i can do with this aes-cbc-128 aes-cbc-192 aes-cbc-256
    AES_KEY enc_key, dec_key;
    AES_set_encrypt_key(aes_key, keylength, &enc_key);
    AES_cbc_encrypt((unsigned char *)&ticket, enc_out, inputslength, &enc_key, iv_enc, AES_ENCRYPT);

    AES_set_decrypt_key(aes_key, keylength, &dec_key);
    AES_cbc_encrypt(enc_out, dec_out, encslength, &dec_key, iv_dec, AES_DECRYPT);

    printf("original:\t");
    hex_print((unsigned char *)&ticket, inputslength);

    printf("encrypt:\t");
    hex_print(enc_out, sizeof(enc_out));

    printf("decrypt:\t");
    hex_print(dec_out, sizeof(dec_out));

    USR_TICKET * dyc = (USR_TICKET *)dec_out;
    printf("Username - %s\n", dyc->username);
    printf("Ticket Id - %d\n", dyc->ticketId);
    printf("Date - %s\n", dyc->date);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

问题是结构的前两个成员正在正确解密.之后数据会被破坏.我在这做错了什么?

Iri*_*ium 8

几乎可以说这是OpenSSL的一个问题.似乎当length传递给的参数AES_cbc_encrypt是> AES_BLOCK_SIZE但不是其整数倍(即length mod AES_BLOCK_SIZE != 0)时,则使用初始 IV而不是前一个密文块来加密最后一个块,如CBC模式的情况.

您可以通过以下两种方式之一解决此问题:

将结构复制到大小为整数倍的缓冲区AES_BLOCK_SIZE,或者分为两部分加密 - 完整块,后跟单个部分块.后者具有避免额外内存使用的优点,可以按如下方式完成:

size_t fullBlocks = inputslength - (inputslength % AES_BLOCK_SIZE);
size_t remainingBlock = inputslength - fullBlocks;

AES_cbc_encrypt((unsigned char *)&ticket, enc_out, fullBlocks, &enc_key, iv_enc, AES_ENCRYPT);
AES_cbc_encrypt((unsigned char *)&ticket + fullBlocks, enc_out + fullBlocks, remainingBlock, &enc_key, iv_enc, AES_ENCRYPT);
Run Code Online (Sandbox Code Playgroud)

然后,您应该可以解密,因为您目前没有问题.但是值得注意的是,您应该声明dec_out与大小相同enc_out,因为您dec_out在解密时当前正在超越缓冲区.

编辑:

我提出这是OpenSSL中的一个错误:https://rt.openssl.org/Ticket/Display.html? id = 3182 & user = think & pass =guest,虽然有一些争论,这是否实际上是一个错误,或只是(没有记录) )未定义的行为,一般的共识是EVP应该使用例程而不是这些低级函数.