Cifrado/descifrado AES (aes-cbc-128, aes-cbc-192, aes-cbc-256) con openssl C

12 minutos de lectura

avatar de usuario
hiedra

Solo quiero probar AES desde openSSL con estos 3 modos: con 128,192 y 256 longitudes de clave, pero mi texto descifrado es diferente de mi entrada y no sé por qué. Además, cuando paso una gran longitud de entradas (digamos 1024 bytes), mi programa muestra core dumped… Mi entrada es siempre la misma pero no importa, al menos por ahora. Aquí está el código:

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

int main(int argc, char **argv)
{
    int i;
    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];
    memset(aes_key, 0, sizeof(aes_key));
    if (!RAND_bytes(aes_key, keylength))
    {
        exit(-1);
    }
    aes_key[keylength-1] = '\0';

    int inputslength;
    printf("Give an input's length:\n");
    scanf("%d", &inputslength);

    /* generate input with a given length */
    unsigned char aes_input[inputslength+1];
    memset(aes_input, '0', sizeof(aes_input));
    aes_input[inputslength] = '\0';

    /*printf("original:\t");
    for(i=0; i<inputslength; i++)
    {
        printf("%c ", aes_input[i]);
    }
    printf("\n");*/

    /* init vector */
    unsigned char iv[AES_BLOCK_SIZE];
    if (!RAND_bytes(iv, AES_BLOCK_SIZE))
    {
        exit(-1);
    }

    //printf("AES_BLOCK_SIZE = %d\n", AES_BLOCK_SIZE); // aes block size is 16 bytes = 128 bits
    AES_KEY enc_key, dec_key;
    unsigned char enc_out[AES_BLOCK_SIZE];
    unsigned char dec_out[AES_BLOCK_SIZE];

    // so i can do with this aes-cbc-128 aes-cbc-192 aes-cbc-256
    AES_set_encrypt_key(aes_key, keylength, &enc_key);
    AES_cbc_encrypt(aes_input, enc_out, inputslength, &enc_key, iv, AES_ENCRYPT);

    AES_set_decrypt_key(aes_key, keylength, &dec_key);
    AES_decrypt(enc_out, dec_out, &dec_key);

    printf("original:\t");
    for(i=0;*(aes_input+i)!=0x00;i++)
        printf("%X ",*(aes_input+i));
    printf("\nencrypted:\t");

    for(i=0;*(enc_out+i)!=0x00;i++)
        printf("%X ",*(enc_out+i));

    printf("\ndecrypted:\t");
    for(i=0;*(dec_out+i)!=0x00;i++)
        printf("%X ",*(dec_out+i));
    printf("\n");

    /*printf("\n\noriginal:\t");
    for(i=0; i<inputslength; i++)
    {
        printf("%x ", dec_out[i]);
    }
    printf("\n");*/


    return 0;
}

EDITAR:

Cuando cambié los tamaños de salida a inputslength en lugar de AES_BLOCK_SIZE Obtuve resultados:

Give a key length [only 128 or 192 or 256!]:
128
Give an input's length:
5
original:       30 30 30 30 30 
encrypted:      94 56 50 7E 19 B2 1C CE 20 23 4A E7 10 AF DB E3 30 30 30 30 30 
decrypted:      E1 5F F4 3D E8 8D 91 19 CD 3E 22 1E AF 1C 8F 5A 94 56 50 7E 19 B2 1C CE 20 23 4A E7 10 AF DB E3 30 30 30 30 30

Entonces, ¿es posible que haya un problema con los tamaños de salida y el tamaño de la iv? ¿Qué tamaños deberían tener (para AES-CBC-128, AES-CBC-192, AES-CBC-256)?

  • Nota al margen: Su clave AES no necesita ser terminada en nulo. es un bloque aleatorio de bytes; eso es todo. No hay nada de término nulo al respecto, así que aes_key[keylength-1] = '\0' no tiene sentido (excepto que siempre establece el último byte de su clave (que, por cierto, es 8 veces más grande de lo que debe ser) en cero).

    – WhozCraig

    9 de agosto de 2013 a las 19:28


  • @WhozCraig: gracias, es bueno saber eso. ¿Qué tal el problema principal, tienes alguna idea?

    – hiedra

    09/08/2013 a las 19:52

  • Mucho. Los tamaños de búfer para el cifrado y descifrado no están en ninguna parte cerca suficientemente grande. y necesita una función de impresión hexadecimal general en lugar de tratar estas cosas como datos de caracteres. es binario. Formando una respuesta oficial ahora, pero comience a buscar allí.

    – WhozCraig

    9 de agosto de 2013 a las 19:58

  • Debería no utilizar AES_encrypt y amigos. deberías estar usando EVP_* funciones Ver Cifrado y descifrado simétrico EVP en la wiki de OpenSSL. De hecho, probablemente debería usar cifrado autenticado porque proporciona ambas cosas confidencialidad y autenticidad. Ver Cifrado y descifrado autenticado por EVP en la wiki de OpenSSL.

    – jww

    15 mayo 2015 a las 20:14

avatar de usuario
QuiénesCraig

Eche un vistazo a esta versión modificada de su código. Tenga en cuenta lo siguiente:

  1. Añadido hex_print (menor)
  2. Se agregó el tamaño adecuado del búfer clave (medio).
  3. Se agregó el tamaño adecuado del búfer de cifrado de salida (que debe ser un múltiplo de tamaño de bloque, y si el búfer de origen original es un múltiplo de tamaño de bloque exacto, aún necesita un bloque completo de relleno (consulte Relleno PKCS 5 para obtener más información).
  4. El mismo IV utilizado para cifrar y descifrar.
  5. Finalmente, por extraño que parezca AES_cbc_encrypt() se utiliza para ambas cosas cifrado y descifrado (ver el último parámetro en la llamada).

Código fuente

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

// 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);

    size_t inputslength = 0;
    printf("Give an input's length:\n");
    scanf("%lu", &inputslength);

    /* generate input with a given length */
    unsigned char aes_input[inputslength];
    memset(aes_input, 'X', inputslength);

    /* 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(aes_input, 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(aes_input, sizeof(aes_input));

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

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

    return 0;
}

Salida de prueba

Give a key length [only 128 or 192 or 256!]:
128
Give an input's length:
10
original:   58 58 58 58 58 58 58 58 58 58 
encrypt:    A9 66 C5 24 A4 02 AB 96 08 65 F7 22 A5 FB BE 26 
decrypt:    58 58 58 58 58 58 58 58 58 58 

Segunda salida de prueba

Give a key length [only 128 or 192 or 256!]:
128
Give an input's length:
10
original:   58 58 58 58 58 58 58 58 58 58 
encrypt:    C2 47 6D B1 A1 68 29 53 55 74 C5 CC 3F 27 0A 3F 
decrypt:    58 58 58 58 58 58 58 58 58 58 

Espero sinceramente que esto ayude.

  • Perdón por molestarte, tienes razón, todo está bien ahora 🙂

    – hiedra

    10 de agosto de 2013 a las 19:05

  • Creo que este código está mal. Mi caso de prueba: keylen=128, inputlen=100. Deberías probarlo de nuevo.

    – g10guang

    6 de agosto de 2018 a las 3:59

  • @ g10guang Si puede describir lo que cree que se supone que debe hacer, lo que realmente está haciendo y en qué se diferencian, me interesará saber por qué cree que está mal. no lo es esta haciendo exactamente lo que pretendía: generar una clave aleatoria, un IV aleatorio, cifrar un bloque constante de datos lleno de 'X' caracteres, descifrando los mismos datos y demostrando los datos originales, cifrados y descifrados (donde el original y el descifrado deberían ser iguales, y lo son), utilizando un volcado hexadecimal para la salida. Puede que no esté haciendo lo que crees que es, y ahí puede estar la desconexión.

    – WhozCraig

    6 de agosto de 2018 a las 9:15


avatar de usuario
hiedra

@WhozCraig: ¡muchas gracias por la ayuda! ¡Me explicó muchas cosas! Pero solo hay un problema más. Cambié matrices estáticas en dinámicas. Cuando lo hice, ocurrieron algunos errores. Pero ocurren solo cuando doy un tamaño de entrada enorme, eche un vistazo a la salida de valgrind: http://pastie.org/private/bzofrrtgrlzr0doyb3g. El error ocurre solo cuando paso una entrada enorme, cuando paso un tamaño pequeño (como en su ejemplo, 10) está bien. Todo lo demás está funcionando perfectamente.

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

// 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)
{
    size_t inputslength = 0;
    printf("Give an input's length:\n");
    scanf("%lu", &inputslength);

    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 = (unsigned char*)malloc(sizeof(unsigned char) * (keylength/8));
    memset(aes_key, 0, keylength/8);
    RAND_bytes(aes_key, keylength/8);

    //  generate input with a given length
    unsigned char *aes_input = (unsigned char*)malloc(sizeof(unsigned char) * (inputslength));
    memset(aes_input, 'X', sizeof(aes_input));

    // init vectors
    unsigned char *iv_enc = (unsigned char*)malloc(sizeof(unsigned char) * (AES_BLOCK_SIZE));
    unsigned char *iv_dec = (unsigned char*)malloc(sizeof(unsigned char) * (AES_BLOCK_SIZE));
    // iv_dec == iv_enc
    RAND_bytes(iv_enc, AES_BLOCK_SIZE);
    memcpy(iv_dec, iv_enc, AES_BLOCK_SIZE);

    // buffers for encryption and decryption
    const size_t length = (((inputslength + AES_BLOCK_SIZE) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE);
    unsigned char *enc_out = (unsigned char*)malloc(sizeof(unsigned char) * (length));
    unsigned char *dec_out = (unsigned char*)malloc(sizeof(unsigned char) * (inputslength));

    // so i can do with this aes-cbc-128 aes-cbc-192 aes-cbc-256
    AES_KEY encKey, decKey;
    AES_set_encrypt_key(aes_key, keylength, &encKey);
    AES_cbc_encrypt(aes_input, enc_out, inputslength, &encKey, iv_enc, AES_ENCRYPT);

    AES_set_decrypt_key(aes_key, keylength, &decKey);
    AES_cbc_encrypt(enc_out, dec_out, length, &decKey, iv_dec, AES_DECRYPT);

    printf("original:\t");
    hex_print(aes_input, sizeof(aes_input));

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

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

    free(aes_key);
    aes_key = NULL;
    free(aes_input);
    aes_input = NULL;
    free(iv_enc);
    iv_enc = NULL;
    free(iv_dec);
    iv_dec = NULL;
    free(enc_out);
    enc_out = NULL;
    free(dec_out);
    dec_out = NULL;

    return 0;
}

EDITAR:

Ok, algo estaba mal con el código anterior que publiqué, aquí hay uno nuevo, que funciona perfectamente, incluso para una gran cantidad de entradas. ¡Saludos una vez más por ayudarme! 🙂

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

// 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 = (unsigned char*)malloc(sizeof(unsigned char) * (keylength/8));
    memset(aes_key, 0, keylength/8);
    if (!RAND_bytes(aes_key, keylength/8))
        exit(-1);

    size_t inputslength = 0;
    printf("Give an input's length:\n");
    scanf("%lu", &inputslength);

    /* generate input with a given length */
    unsigned char *aes_input = (unsigned char*)malloc(sizeof(unsigned char) *inputslength);
    memset(aes_input, 'X', inputslength);

    /* init vector */
    unsigned char *iv_enc = (unsigned char*)malloc(sizeof(unsigned char) *AES_BLOCK_SIZE), *iv_dec = (unsigned char*)malloc(sizeof(unsigned char) *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 = (unsigned char*)malloc(sizeof(unsigned char) *encslength);
    unsigned char *dec_out = (unsigned char*)malloc(sizeof(unsigned char) *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(aes_input, 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(aes_input, inputslength);

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

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

    // free memory here

    return 0;
}

  • Mirar de nuevo. sizeof() adquiere un significado completamente nuevo cuando se usa con un puntero en lugar de una matriz fija o variable. Se vuelve del tamaño de un puntero, no el tamaño de los datos asignados para ello. Todos sus tamaños deben ser variables; no sizeof(something). Arregle eso y apuesto a que sus problemas finales desaparecerán. (nota al margen: no lanzar malloc() cuando se programa en C normal. Puede causar problemas muy sutiles, especialmente para la portabilidad. C ++ lo requiere, pero, de nuevo, no debería estar usando malloc() en C++ en primer lugar.) =P

    – WhozCraig

    10 de agosto de 2013 a las 19:11


  • muchas gracias de nuevo! Aquí hay un ejemplo de trabajo: pastie.org/private/dtvmftozmru02webkzzq. yo eché malloc porque mi gcc me dijo que – malloc devuelve nulo por lo que recuerdo, ¿verdad? Cuando escribo en C++, uso new y deletesé que no debería usar malloc en C++ 😉

    – hiedra

    10 de agosto de 2013 a las 19:23


  • Fundición malloc() en la programación C tiene sus propias trampas potenciales. Obviamente se requiere en C++, pero como dijiste, usas new y delete. Buscar “casting malloc [c]” en este sitio. Es un tema bastante debatido. Aparte de eso, me alegro de que esté funcionando.

    – WhozCraig

    10 de agosto de 2013 a las 20:46

  • Gracias por la implementación!!

    – dano

    5 de febrero de 2014 a las 11:20

  • @Puffin eso NO es correcto. Debe haber espacio para hasta uno completo bloque de relleno. Así es como funciona el relleno PKCS#5. Cualquier cosa que “funcione” para usted sin tener en cuenta ese caso, lo hace solo porque el texto sin formato que se cifra es no un múltiplo exacto del tamaño del bloque AES (16 bytes) y, en consecuencia, es una bomba de relojería para cuando finalmente se presente ese caso. Ver relleno PKCS5/7 aquíy específicamente, el párrafo que describe lo que sucede cuando se encuentra un múltiplo de tamaño de bloque exacto.

    – WhozCraig

    5 de agosto de 2017 a las 7:46


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad