Разница между шифрованием командной строки openssl rc4 и файлом cpp, реализующим rc4

Я работаю над попыткой зашифровать текстовый файл через RC4 с помощью файла cpp, который я написал с заголовками openssl/rc4, а затем расшифровать через командную строку, чтобы показать, что моя реализация верна.

Моя команда терминала для файла находится ниже, а файл cpp находится ниже него вместе с командой компиляции терминала, которую я использовал для него.

Кажется, что в Интернете почти нет никакой информации об этом, за исключением некоторых расплывчатых видеороликов на YouTube, которые объясняют, как работает шифр RC4 (о котором я уже знаю). Я не могу найти ничего на справочных страницах, чтобы объяснить детали реализации openssl.

Любые указатели на то, почему мой файл cpp не расшифровывается до исходного содержимого, будут высоко оценены. Я рву на себе волосы, пытаясь понять это. Заранее спасибо.

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

шифрование командной строки:

openssl rc4-40 -k PASSword123 -in /home/chris/Desktop/test.txt -out /home/chris/Desktop/ssloutput.txt -p -nosalt

компиляция файла cpp:

g++ rc4.cpp -o rc4 -lssl -lcrypto

СРР-файл:

#include <openssl/rc4.h>
#include <string>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    int fd = open("/home/chris/Desktop/ssloutput.txt", O_RDWR);
    unsigned char keygen[12] = "PASSword123";
    RC4_KEY key;

    struct stat st;
    fstat(fd, &st);
    int size = st.st_size;

    unsigned char* fileIn;
    fileIn = (unsigned char*) calloc(size, sizeof(char));
    pread(fd, fileIn, size, 0);
    unsigned char *fileOut = (unsigned char*)malloc(size);

    RC4_set_key(&key, 16, keygen);
    RC4(&key, size, fileIn, fileOut);
    close(fd);

    int fd2 = open("/home/chris/Desktop/rc4output.txt", O_RDWR | O_CREAT);
    pwrite(fd2, fileOut, size, 0);
    close(fd2);

    free(fileIn);
    free(fileOut);

    return 0;
}

person Chris    schedule 11.04.2019    source источник
comment
Идентичны ли размеры зашифрованных и обычных файлов?   -  person Shawn    schedule 11.04.2019
comment
Да, они оба 116 байт. Не должно иметь значения, даже если бы их не было (хотя технически они всегда были бы с RC4). Алгоритм должен быть симметричным.   -  person Chris    schedule 11.04.2019
comment
Если вы используете rc4-40 в интерфейсе командной строки, наверняка вы должны передавать 5 в RC4_set_key() в качестве размера ключа? В противном случае, если вы хотите использовать 16-байтовые ключи, используйте режим rc4 в CLI.   -  person Alastair McCormack    schedule 11.04.2019
comment
Это правда, я должен был уловить это. Но независимо от того, когда я запускаю в CLI только rc4 вместо rc4-40, он должен совпадать с размером ключа = 16, но файлы по-прежнему не совпадают.   -  person Chris    schedule 11.04.2019
comment
В связи с этим, если keygen составляет 12 байтов (на самом деле 11, поскольку вы не должны считать конечный 0), почему вы говорите RC4_set_key(), что это 16? Это, вероятно, вызывает все виды прекрасного неопределенного поведения из-за доступа к массиву за пределами границ.   -  person Shawn    schedule 11.04.2019
comment
Потому что в одном из очень немногих источников информации, которые я смог найти, разрешенные размеры в команде CLI предположительно составляют 128, 64 или 40 бит. А функция RC4 должна дополнять клавишу до нужной длины.   -  person Chris    schedule 11.04.2019
comment
Вы лжете ему о размере ключа. Это просто напрашивается на неприятности и ничего не работает.   -  person Shawn    schedule 11.04.2019
comment
Ну, из всего, что я могу найти, видно, что он дополняет ввод. Даже генератор ключевого потока RC4 на высоком уровне дополняет ключ до нужной длины с помощью функции модуля. И большинство реализаций, которые я видел, не используют 40-, 64- или 128-битные ключи, несмотря на то, что это единственные доступные опции в CLI. Однако на всякий случай я изменил свой ключ на PASSword1234567, чтобы создать массив размером 16, и это все равно не расшифровало его должным образом. Может быть, это тип ввода для ключа? Насколько я понимаю, CLI делает шестнадцатеричный ключ из ввода, но не знает о функциях C/C++.   -  person Chris    schedule 11.04.2019
comment
Это 15-байтовый ключ... Если вы все еще указываете функции использовать 16-байтовый ключ, да, проблема все равно будет.   -  person Shawn    schedule 11.04.2019
comment
Я также пробовал unsigned char keygen[17] = PASSword12345678; Все еще не работает. Я пробовал этот ключ, передавая полный размер массива (17) в вызов функции rc4, а также пробовал его с передачей размера (16), чтобы игнорировать завершающий нулевой символ, но ни один из них не работал.   -  person Chris    schedule 11.04.2019
comment
Ооо, этот ТАК вопрос может быть действительно полезным: команда"> stackoverflow.com/questions/9329631/   -  person Shawn    schedule 11.04.2019
comment
Да, на самом деле это именно тот пост, на который я смотрел, но, не имея возможности сравнить с его реализацией C, я не уверен, где/если я ошибаюсь с вызовами функций...   -  person Chris    schedule 11.04.2019


Ответы (1)


Итак, вот версия вашего кода с добавлением большого количества проверок ошибок, исправлением ошибок, очисткой странных вещей (использование O_RDWR с open(), когда вы только читаете или пишете? pread()? pwrite()?) и использованием EVP_BytesToKey() как вариант -k для openssl rc4 использования (это был ключевой (хех) фактор):

#include <fcntl.h>
#include <openssl/evp.h>
#include <openssl/rc4.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int main() {
  int fd = open("ssloutput.txt", O_RDONLY);
  if (fd < 0) {
    perror("open ssloutput.txt");
    return 1;
  }

  struct stat st;
  if (fstat(fd, &st) < 0) {
    perror("fstat");
    return 1;
  }
  size_t size = st.st_size;

  unsigned char *fileIn = calloc(size, 1);
  if (!fileIn) {
    perror("calloc");
    return 1;
  }
  if (read(fd, fileIn, size) != (ssize_t)size) {
    perror("read");
    return 1;
  }
  close(fd);

  unsigned char *fileOut = malloc(size);
  if (!fileOut) {
    perror("malloc");
    return 1;
  }

  // Prepare the key according to the same rules as openssl rc4 -k foo
  char keygen[] = "PASSword123";
  RC4_KEY key;
  unsigned char computed_key[16];
  if (EVP_BytesToKey(EVP_rc4(), EVP_sha256(), NULL,
                     (const unsigned char *)keygen, strlen(keygen), 1,
                     computed_key, NULL) != 16) {

    fputs("Error calculating rc4 key!\n", stderr);
    return 1;
  }
  // Should match the one printed out by openssl rc4 -p
  fputs("key=", stdout);
  for (size_t n = 0; n < sizeof computed_key; n += 1) {
    printf("%02hhx", computed_key[n]);
  }
  putchar('\n');

  RC4_set_key(&key, sizeof computed_key, computed_key);
  RC4(&key, size, fileIn, fileOut);

  int fd2 = open("rc4output.txt", O_WRONLY | O_TRUNC | O_CREAT, 0644);
  if (fd2 < 0) {
    perror("open rc4output.txt");
    return 1;
  }
  if (write(fd2, fileOut, size) != (ssize_t)size) {
    perror("write");
    return 1;
  }
  close(fd2);

  free(fileIn);
  free(fileOut);

  return 0;
}

Демонстрация:

$ cat input.txt
the quick brown dog jumped over the lazy red fox.
$ gcc -o myrc4 -O -Wall -Wextra myrc4.c -lcrypto
$ openssl rc4 -k PASSword123 -md sha256 -p -nosalt -in input.txt -out ssloutput.txt
key=B554C1D224D8EF1738ED4EE238317463
$ ./myrc4
key=B554C1D224D8EF1738ED4EE238317463
$ cat rc4output.txt
the quick brown dog jumped over the lazy red fox.
person Shawn    schedule 11.04.2019
comment
Большое спасибо, это сработало отлично (хотя это файл C++, поэтому мне пришлось преобразовать вызовы malloc и calloc в (Unisgned char*). Итак, вы говорите, что проблема заключалась в том, чтобы не включать EVP_Bytes в ключ? Где вы нашли информацию, что это было необходимо?Я рад за исправление кода, но я хотел бы понять, как вы туда попали.Я бы подумал, что функциональность должна быть встроена в библиотеки lssl и libcrypto ? - person Chris; 11.04.2019
comment
Если это должен был быть C++, то почему он вообще использовал malloc(), C-приведения, C-заголовки и т. д.? Похоже на C, я предполагаю, что C. Не заметил тег. В любом случае, мне понадобился EVP_BytesToKey() из того вопроса SO, на который я ссылался в комментарии. Тогда нужно было просто прочитать документацию по этой функции. Этот вопрос продемонстрировал, как использовать -K, чтобы предоставить openssl точные байты для использования в качестве ключа, поэтому я пошел другим путем, продемонстрировав, как работать с -k. - person Shawn; 11.04.2019
comment
Ух ты, я не могу поверить, что я замазал это предложение. Он специально упомянул функцию, лол. В любом случае, спасибо за помощь. Очень признателен. - person Chris; 11.04.2019
comment
Однако документация OpenSSL определенно оставляет желать лучшего. - person Shawn; 11.04.2019