2013-11-10 3 views
6

Я хочу зашифровать структуру, содержащую несколько строк, а затем расшифровать ее. Я попробовал следующий код. Исходный код найден из Интернета, и он отлично работает. Я меняю его на структуру. Ниже приведен код.Простое шифрование AES с помощью библиотеки openssl в C

#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; 
} 

Проблема заключается только в том, что первые два элемента структуры дешифруют правильно. После этого данные будут обновляться. Что я здесь делаю неправильно?

+1

Я поднял это как ошибка в OpenSSL - см правку на мой ответ. Общий консенсус заключается в том, что вместо этих низкоуровневых функций следует использовать соответствующие подпрограммы «EVP». – Iridium

+1

Вы должны * не * использовать 'AES_encrypt' и друзей. Вы должны использовать 'EVP_ *' функции. См. [Симметричное шифрование и дешифрование EVP] (https://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption) в вики OpenSSL. Фактически, вы, вероятно, должны использовать аутентифицированное шифрование, поскольку оно обеспечивает * и * конфиденциальность и аутентичность. См. [EVP Authenticated Encryption and Decryption] (https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption) в вики OpenSSL. – jww

ответ

7

Я бы почти зайдите так далеко, что это проблема с OpenSSL. Похоже, что когда параметр length, переданный в, составляет>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); 

Затем вы должны быть в состоянии расшифровать, как вы в настоящее время без проблем. Однако стоит отметить, что вы должны объявить dec_out таким же размером, как enc_out, потому что в настоящее время вы используете буфер dec_out при расшифровке.

Edit:

Я поднял это как ошибка в OpenSSL: https://rt.openssl.org/Ticket/Display.html?id=3182&user=guest&pass=guest и в то время как есть некоторый спор о том, является ли на самом деле ошибка, или просто (без документов) неопределенное поведение, общее мнение состоит в том, что Вместо этого следует использовать подпрограммы EVP вместо этих низкоуровневых функций.

2

Проблема может быть в структуре, которую вы используете, в основном из-за структурных дополнений и размеров элементов. Попробуйте сериализовать свой ввод, и он должен работать. простым способом, попробуйте выделить один буфер символов и скопировать содержимое структуры в буфер один за другим и попытаться зашифровать этот буфер, а при расшифровке также следовать одному и тому же методу. Отправьте сюда то, что вы наблюдаете после этого. Мы должны уметь лучше комментировать.

2

Оригинальный код сделал прокладку для вас. Проблема в том, что вы передали неправильную длину открытого текста в функцию AES. Вы должны пройти encslength (заполненная длина) до AES_cbc_encrypt. Просто измените эту строку

AES_cbc_encrypt((unsigned char *)&ticket, enc_out, inputslength, &enc_key, iv_enc, AES_ENCRYPT); 

в

AES_cbc_encrypt((unsigned char *)&ticket, enc_out, encslength, &enc_key, iv_enc, AES_ENCRYPT); 

Это должно решить вашу проблему.

+0

Выполнение, как вы укажете, будет читаться за пределами структуры, если его размер не кратен размеру блока. – Iridium

+0

@ Иридий, ты прав. Ему нужно скопировать билет в буфер размером 'encslength', чтобы избежать этого. – ChiaraHsieh

+0

Или вы можете избежать любого дополнительного копирования, используя код в моем ответе ... – Iridium

0

По крайней мере, я также считаю, что это не очень хорошая практика использования struct и sizeof(a_struct_type) здесь.

Фактическая двоичная последовательность struct USR_TICKET, а результат sizeof будет варьироваться в зависимости от реализации различных дополнений и байт.

Вот результат теста.

1) Во-первых, я загружаю последнюю версию openssl1.0.2c и строю ее и тестирую код здесь с этой библиотекой на OS X 10.10.3, код работает отлично.

2) И я попытался запустить тест 5 раз. Каждый раз, даже обычный текст original будет изменен из-за заполнения struct (в моей ОС последние 4 байта будут меняться каждый раз).

3) Итак, тот же ввод в struct производил разные plain text, которые, наконец, произвели различные шифровальные тексты. Пользователь может быть смущен разным выпуском шифротекста, когда они пытаются зашифровать одну и ту же информацию ввода (здесь raving, 1, 2015-6-25) с тем же ключом и iv.

4) Кроме того, просто измените внутреннее определение struct, которое будет вызывать такую ​​же озабоченность в описании (3).

typedef struct ticket { /* test field */ 
int ticketId; 
char username[19]; // here try to change from 20 to 19 
    char date[20]; // here try to change from 20 to other size 
} USR_TICKET; 

PS. Некоторые выходы являются результатом выше описания (2),

output1:

Give a key length [only 128 or 192 or 256!]: 
128 
Username - ravinda 
Ticket Id - 1 
Date - 2015-06-25 
original: 01 00 00 00 72 61 76 69 6E 64 61 00 00 00 00 00 00 00 00 00 00 00 00 00 32 30 31 35 2D 30 36 2D 32 35 00 00 00 00 00 00 58 BB 3A 50 
encrypt: BA 32 86 CC 71 55 2F 73 ED A1 C9 DE 00 32 1A 20 D9 A5 16 52 8A CD F0 F7 38 04 76 38 5A 47 35 3B A3 07 97 41 C4 C2 05 53 74 93 91 26 7E DE 40 47 
decrypt: 01 00 00 00 72 61 76 69 6E 64 61 00 00 00 00 00 00 00 00 00 00 00 00 00 32 30 31 35 2D 30 36 2D 32 35 00 00 00 00 00 00 58 BB 3A 50 
Username - ravinda 
Ticket Id - 1 
Date - 2015-06-25 

Выход2:

Give a key length [only 128 or 192 or 256!]: 
128 
Username - ravinda 
Ticket Id - 1 
Date - 2015-06-25 
original: 01 00 00 00 72 61 76 69 6E 64 61 00 00 00 00 00 00 00 00 00 00 00 00 00 32 30 31 35 2D 30 36 2D 32 35 00 00 00 00 00 00 58 0B 10 5A 
encrypt: BE 60 0F FC 17 A3 42 4A 95 7C 39 DB BF 2C BA 59 42 DC 0C AD B2 20 76 6A 04 E3 DE 11 3E D0 AF 88 A5 B9 D2 25 D4 AE F0 B7 82 9F 13 39 80 39 61 9D 
decrypt: 01 00 00 00 72 61 76 69 6E 64 61 00 00 00 00 00 00 00 00 00 00 00 00 00 32 30 31 35 2D 30 36 2D 32 35 00 00 00 00 00 00 58 0B 10 5A 
Username - ravinda 
Ticket Id - 1 
Date - 2015-06-25 
Смежные вопросы