Openssl C API "EVP_BytesToKey()" возвращает неправильный ключ и iv при использовании

Используя OpenSSL C API, я хочу расшифровать файл с помощью пароля, для этого мне нужно вручную получить ключ и iv из заданного пароля с помощью EVP_BytesToKey(), но функция выдает мне неправильный ключ и iv.

Я шифрую файл с помощью инструмента командной строки следующим образом:

openssl enc -nosalt -p -in sample.txt -out sample.txt.enc -e -aes256 -k PASSWORD
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
key=0BE64AE89DDD24E225434DE95D501711339BAEEE18F009BA9B4369AF27D30D60
iv =44A783DFFE1463B81E65064750797FA4

Вот мой код (большая часть из примеров переполнения стека, которые я видел):

#include <string>
#include <fstream>

#include <openssl/ssl.h>

#include <iostream>
#include <vector>
#include <locale>
#include <windows.h>


int main(){
 // Initializing OpenSLL
     const EVP_CIPHER *cipher;
    const EVP_MD *dgst = NULL;
    unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];

    // Setting password

    const char *password = "PASSWORD";
    const unsigned char *salt = NULL;
    int i;

    OpenSSL_add_all_algorithms();


    // Getting key and iv

    cipher = EVP_get_cipherbyname("aes-256-cbc");
    if(!cipher) { fprintf(stderr, "no such cipher\n"); return 1; }

    dgst=EVP_get_digestbyname("md5");
    if(!dgst) { fprintf(stderr, "no such digest\n"); return 1; }

    if(!EVP_BytesToKey(cipher, dgst, salt,
        (unsigned char *) password,
        strlen(password), 1, key, iv))
    {
        fprintf(stderr, "EVP_BytesToKey failed\n");
        return 1;
    }


    // Output key and iv to the screen

    printf("Key: "); for(i=0; i< 32; ++i) { printf("%02x", key[i]); } printf("\n");
    printf("IV: "); for(i=0; i< 16; ++i) { printf("%02x", iv[i]); } printf("\n");
}

При отладке программа выводит на экран ключ и iv, в них случайные байты?!?

Каким-то образом при печати ключа и iv на экране с «% 02x» отображаются допустимые шестнадцатеричные значения.

Вот что выводит моя программа:

Key: 319f4d26e3c536b5dd871bb2c52e3178dcbc5de3a413ea043012cf3506b6956e
IV: c23b1986151650adf58ba93c7a10f73f

Выход отличается от исходного ключа и iv. Я видел, как это делают другие люди, и у них это сработало.

Спасибо

3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
1
0
37
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Решение:

(read to the end for an improvement)

Начиная с openSSL 1.1, указание отсутствия дайджеста выводит дайджест SHA256 вместо MD5. Если вы запустите эту команду (обратите внимание на ключ -md):

openssl enc -nosalt -p -in sample.txt -out sample.txt.enc -e -aes256 -k PASSWORD -md md5

Вы получите следующий вывод:

iv =C23B1986151650ADF58BA93C7A10F73F

Это то же самое, что и ваша программа на C.

И наоборот, если вы переключите исходный код C на дайджест sha256:

dgst=EVP_get_digestbyname("sha256");

Вы получите тот же вывод, что и из командной строки.

Улучшение

Как вы могли заметить, openssl выдает предупреждение *** WARNING : deprecated key derivation used. В документации openssl есть объяснение такому предупреждению:

Newer applications should use a more modern algorithm such as PBKDF2 as defined in PKCS#5v2.1 and provided by PKCS5_PBKDF2_HMAC.

Затем мы можем получить ключ напрямую с помощью этого алгоритма как с исполняемым файлом openssl, используя переключатель iter 1, который автоматически использует -pbkdf2 Use password-based key derivation function 2 :

openssl enc -nosalt -p -in a.out -out a.out.enc -e -aes256 -k PASSWORD -md sha256 -iter 1

Получающий

key=75753C1B22D09354380EB2DA277B994738CA4C5AC09191E6285E9F99E04AA7A0
iv =7AE8C8B15B535D4197B5EC8E59F65809

И изменим нашу программу на C, чтобы получить ключ с тем же более безопасным алгоритмом:

#include "string.h"

#include "openssl/ssl.h"

int main(){
 // Initializing OpenSLL
     const EVP_CIPHER *cipher;
    const EVP_MD *dgst = NULL;
    unsigned char keyivpair[EVP_MAX_KEY_LENGTH+EVP_MAX_IV_LENGTH];
    unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];

    // Setting password

    const char *password = "PASSWORD";
    const unsigned char *salt = NULL;
    int i;

    OpenSSL_add_all_algorithms();


    // Getting key and iv

    cipher = EVP_get_cipherbyname("aes-256-cbc");
    if(!cipher) { fprintf(stderr, "no such cipher\n"); return 1; }

    dgst=EVP_get_digestbyname("sha256");
    if(!dgst) { fprintf(stderr, "no such digest\n"); return 1; }


    int iklen = EVP_CIPHER_key_length(cipher);
    int ivlen = EVP_CIPHER_iv_length(cipher);

    if( !PKCS5_PBKDF2_HMAC ((unsigned char *) password, 
                        strlen(password), 
                        salt, 
                        0, 
                        1, 
                        dgst, //sha256
                        iklen + ivlen,
                        keyivpair) )
    {
        fprintf(stderr, "PKCS5_PBKDF2_HMAC failed\n");
        return 1;       
    }
    
    memcpy(key, keyivpair, iklen);
    memcpy(iv, keyivpair + iklen, ivlen);

    // Output key and iv to the screen

    printf("Key: "); for(i=0; i< 32; ++i) { printf("%02x", key[i]); } printf("\n");
    printf("IV: "); for(i=0; i< 16; ++i) { printf("%02x", iv[i]); } printf("\n");

Что выводит:

Key: 75753c1b22d09354380eb2da277b994738ca4c5ac09191e6285e9f99e04aa7a0
IV: 7ae8c8b15b535d4197b5ec8e59f65809

Привет, спасибо за публикацию ответа, я очень ценю это :), мне нужно как-то получить iv, поэтому я не думаю, что мне понадобится улучшение, ты :(, возможно, я мог бы использовать другой алгоритм шифрования, который не принять внутривенно

user18909000 23.04.2022 16:31

Я никогда не знал, что SHA256 был по умолчанию!?

user18909000 23.04.2022 16:36

@ iber999 Копаясь, я нашел способ получить IV с более безопасным улучшенным решением. Я редактирую ответ прямо сейчас с полным кодом.

Fra93 23.04.2022 16:37

В конце ответа у iv есть три ```

user18909000 23.04.2022 16:48

Исправлена ​​опечатка @iber999

Fra93 23.04.2022 16:53

Другие вопросы по теме