Шифрование файлов в c ++ с использованием openssl, дешифрование в java с использованием AES / CBC / PKCS5Padding - PullRequest
1 голос
/ 04 февраля 2020

Используя openssl в проекте c ++, я выполнял шифрование файлов, используя AES / CB C 128 бит. У меня есть проект java, который выполняет дешифрование файла.

Идея в том, что в c ++ мы будем читать исходный файл фрагментом 4096 байт. Будет выполнять шифрование с использованием функции openssl EVP_CipherUpdate(), как только мы получим зашифрованный буфер от этой функции, мы запишем IV (из 16 байт) в выходной файл, затем зашифрованный буфер и, наконец, вызвав EVP_CipherFinal(), мы зашифруем блок (из 16 байт) и запишите это в файл. Этот лог c будет вызываться в while l oop до достижения EOF.

Таким образом, зашифрованный файл будет иметь 16 байтов (из IV) + 4096 или размер байта (фактический зашифрованный текст). ) + 16 байт (заполнения). Для каждых 4096 (и последних оставшихся) байтов эта структура следует в зашифрованном файле.

Ниже приведена функция, которая выполняет шифрование файлов в c ++ с использованием openssl: (Ссылка от https://medium.com/@amit.kulkarni / encrypting-decrypting-a -file-using-openssl-evp-b26e0e4d28d4 )

void file_encrypt_decrypt(cipher_params_t* params, FILE* ifp, FILE* ofp) {
    const size_t BUFSIZE = 4096;

    /* Allow enough space in output buffer for additional block */
    int cipher_block_size = EVP_CIPHER_block_size(params->cipher_type);

    unsigned char in_buf[BUFSIZE];
    OPENSSL_assert(cipher_block_size == 16);
    unsigned char out_buf[4096+16+16];

    int num_bytes_read, out_len;
    EVP_CIPHER_CTX* ctx;

    ctx = EVP_CIPHER_CTX_new();
    if (ctx == NULL) {
        fprintf(stderr, "ERROR: EVP_CIPHER_CTX_new failed. OpenSSL error: %s\n",
            ERR_error_string(ERR_get_error(), NULL));
        //cleanup(params, ifp, ofp, ERR_EVP_CTX_NEW);
    }

    /* Don't set key or IV right away; we want to check lengths */
    if (!EVP_CipherInit_ex(ctx, params->cipher_type, NULL, NULL, NULL, params->encrypt)) {
        fprintf(stderr, "ERROR: EVP_CipherInit_ex failed. OpenSSL error: %s\n",
            ERR_error_string(ERR_get_error(), NULL));
        //cleanup(params, ifp, ofp, ERR_EVP_CIPHER_INIT);
    }

    OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) == 16);
    OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == 16);

    /* Now we can set key and IV */
    if (!EVP_CipherInit_ex(ctx, NULL, NULL, params->key, params->iv, params->encrypt)) {
        fprintf(stderr, "ERROR: EVP_CipherInit_ex failed. OpenSSL error: %s\n",
            ERR_error_string(ERR_get_error(), NULL));
        EVP_CIPHER_CTX_cleanup(ctx);
        //cleanup(params, ifp, ofp, ERR_EVP_CIPHER_INIT);
    }

    while(1) 
    {
        // Read in data in blocks until EOF. Update the ciphering with each read.
        num_bytes_read = fread(in_buf, sizeof(unsigned char), BUFSIZE, ifp);
        if (ferror(ifp)) {
            fprintf(stderr, "ERROR: fread error: %s\n", strerror(errno));
            EVP_CIPHER_CTX_cleanup(ctx);
            //cleanup(params, ifp, ofp, errno);
        }
        if (!EVP_CipherUpdate(ctx, out_buf, &out_len, in_buf, num_bytes_read)) {
            fprintf(stderr, "ERROR: EVP_CipherUpdate failed. OpenSSL error: %s\n",
                ERR_error_string(ERR_get_error(), NULL));
            EVP_CIPHER_CTX_cleanup(ctx);
            //cleanup(params, ifp, ofp, ERR_EVP_CIPHER_UPDATE);
        }

        fwrite(params->iv, sizeof(unsigned char), EVP_CIPHER_CTX_iv_length(ctx), ofp);
        fflush(ofp);
        if (ferror(ofp)) {
            fprintf(stderr, "ERROR: fwrite error: %s\n", strerror(errno));
            EVP_CIPHER_CTX_cleanup(ctx);
            //cleanup(params, ifp, ofp, errno);
        }

        fwrite(out_buf, sizeof(unsigned char), out_len, ofp);
        fflush(ofp);
        if (ferror(ofp)) {
            fprintf(stderr, "ERROR: fwrite error: %s\n", strerror(errno));
            EVP_CIPHER_CTX_cleanup(ctx);
            //cleanup(params, ifp, ofp, errno);
        }

        /* Now cipher the final block and write it out to file */
        if (!EVP_CipherFinal(ctx, out_buf, &out_len)) {
            fprintf(stderr, "ERROR: EVP_CipherFinal_ex failed. OpenSSL error: %s\n",
                ERR_error_string(ERR_get_error(), NULL));
            EVP_CIPHER_CTX_cleanup(ctx);
            //cleanup(params, ifp, ofp, ERR_EVP_CIPHER_FINAL);
        }

        fwrite(out_buf, sizeof(unsigned char), out_len, ofp);
        fflush(ofp);

        if (ferror(ofp)) {
            fprintf(stderr, "ERROR: fwrite error: %s\n", strerror(errno));
            EVP_CIPHER_CTX_cleanup(ctx);
            //cleanup(params, ifp, ofp, errno);
        }
        if (num_bytes_read < BUFSIZE) {
            /* Reached End of file */
            break;
        }
        memset(out_buf, 204, sizeof(out_buf));
        memset(in_buf, 204, sizeof(in_buf));
    }    
    EVP_CIPHER_CTX_cleanup(ctx);
}

Здесь cipher_params_t является структурой:

typedef struct _cipher_params_t {
    unsigned char* key;
    unsigned char* iv;
    unsigned int encrypt;
    const EVP_CIPHER* cipher_type;
}cipher_params_t;

В main(), Мы устанавливаем key IV путем генерации случайных ключей. EVP_CIPHER установлено на EVP_aes_128_cbc(); Вот основная функция:

int main(int argc, char* argv[]) 
{
    FILE* f_input, * f_enc;


    cipher_params_t* params = (cipher_params_t*)malloc(sizeof(cipher_params_t));


    /* Key to use for encrpytion and decryption */
    unsigned char key[17] = "0123456789abcdef";

    /* Initialization Vector */
    unsigned char iv[17] = "AAAAAAAAAAAAAAAA";

    /* Generate cryptographically strong pseudo-random bytes for key and IV */
    //if (!RAND_bytes(key, sizeof(key)) || !RAND_bytes(iv, sizeof(iv))) {
    //    /* OpenSSL reports a failure, act accordingly */
    //    fprintf(stderr, "ERROR: RAND_bytes error: %s\n", strerror(errno));
    //    return errno;
    //}
    params->key = key;
    params->iv = iv;

    /* Indicate that we want to encrypt */
    params->encrypt = 1;

    /* Set the cipher type you want for encryption-decryption */
    params->cipher_type = EVP_aes_128_cbc();

    /* Open the input file for reading in binary ("rb" mode) */
    f_input = fopen("C:\\CMakeCache.txt", "rb");


    /* Open and truncate file to zero length or create ciphertext file for writing */
    f_enc = fopen("C:\\20161224_143119_DEC.txt", "wb");


    /* Encrypt the given file */
    file_encrypt_decrypt(params, f_input, f_enc);

    /* Encryption done, close the file descriptors */
    fclose(f_input);
    fclose(f_enc);

    /* Free the memory allocated to our structure */
    free(params);

    return 0;
}

Эта программа работает должным образом и генерирует зашифрованный файл, но когда я расшифровываю, используя JAVA, файл дешифруется, но с некоторыми ненужными символами. В java мы использовали AES/CBC/PKCS5Padding, и для расшифровки мы также делаем то же самое, из файлового буфера мы сначала читаем 4128 байтов, затем извлекаем 16 байтов (для IV), затем на оставшихся байтах выполняем cipher.doFinal(). Эта java программа также выполняет расшифровку файла без каких-либо ошибок, но выдает некоторые ненужные символы в расшифрованном файле.

Наблюдения (при копании проблемы):

In дешифрованный файл, после каждых 4096 байтов записывается несколько ненужных символов (ровно 16 байтов). Если мы реализуем шифрование в java, тогда файл расшифровывается в java без мусорных символов. То же самое, если я реализую расшифровку в c ++, то в выходном файле не будет мусорных символов.

Еще раз извините за такой длинный вопрос, я действительно не понимаю, что происходит? Какой процесс помещает ненужные символы в расшифрованный файл, который был зашифрован в c ++?

...