Шифрование с использованием библиотек OpenSSL

Автор: (C) Vinayak Hegde
Перевод: (C) Игорь Яровинский


Вступление.

Одной из главных потребностей современного информационного общества является защита используемой информации. Шифрование дает возможность не допустить утечку и попадание важных данных в руки злоумышленника.

Библиотеки OpenSSL используются для решения этой проблемы. Но, увы, многие алгоритмы, позволяющие кодировать информацию, недостаточно документированы. Можно читать исходные коды и следить за тем, что происходит при выполнении этих программ. Этому помогают имена функций, которые на интуитивном уровне дают понять, что именно делает та или иная подпрограмма. Кроме этого, можно внести свой адрес в список рассылок на сайте OpenSSL и следить за новостями проекта таким образом.

Дополнительная информация.

Во времена появления криптографии все алгоритмы шифрования были засекречены. Но теперь эта ситуация меняется. Сейчас любой желающий может найти информацию о том, как работает тот или иной алгоритм. Лучшим примером этого может быть RSA - метод, при котором для шифрования и дешифрования используются разные ключи. Не смотря на многие преимущества RSA над другими алгоритмами, его не рекомендуют использовать для кодирования больших объемов данных, так как на это уходит очень много времени.

Для шифрования больших объемов информации используются другие алгоритмы. Об одном из них, а именно об алгоритме "блоуфиш", и пойдёт речь в этой статье. Этот алгоритм является симметричным, то есть при шифровании и дешифровании используется один и тот же ключ. Он был создан знаменитым криптографом Брюсом Шнеером (Bruce Schneier).

Генерирование ключа.

В качестве примера мы будем использовать 128-и разрядный ключ и хранить его в виде массива, а также 64-х разрядный вектор инициализации. Вектор инициализации содержит информацию, сгенерированную случайным образом, и используется для шифрования первого блока данных, после чего полученный блок будет ключом для шифрования следующего блока, и так далее.

Генерация вектора осуществляется при помощи специального файла /dev/random. [Не забывайте, что не последнюю роль в "жизни" этого файла играет сервис random. Кстати, вы можете получить доступ к генератору случайных чисел и из шелла -- за это отвечает перменная $RANDOM. Это очень удобно, если вам нужно создать подкатлог или файл с уникальным имененм. Прим.ред.]

int
generate_key ()
{
    int i, j, fd;
    if ((fd = open ("/dev/random", O_RDONLY)) == -1)
        perror ("open error");

    if ((read (fd, key, 16)) == -1)
        perror ("read key error");

    if ((read (fd, iv, 8)) == -1)
        perror ("read iv error");

    printf("128 bit key:\n");
    for (i = 0; i < 16; i++)
        printf ("%d \t", key[i]);
    printf ("\n ------ \n");

    printf("Initialization vector\n");
    for (i = 0; i < 8; i++)
        printf ("%d \t", iv[i]);

    printf ("\n ------ \n");
    close (fd);
    return 0;
}

Подпрограмма для шифрования.

Подпрограмме передаются два параметра: первый - дескриптор файла с исходной информацией, второй - дескриптор файла, в который будет заноситься зашифрованная информация. В приведенной ниже подпрограмме, информация кодируется блоками по 1Кб каждый.

Общую модель работы подпрограммы можно описать следующим образом:

  1. Создание содержимого шифра
  2. Инициализация содержимого шифра с использованием данных из ключа и вектора инициализации.
  3. Вызов EVP_EncryptUpdate для шифрования блоков информации.
  4. Вызов EVP_EncryptFinal для шифрования "оставшихся" данных.
  5. Вызов EVP_CIPHER_CTX_cleanup для очистки памяти от ненужной информации.
int
encrypt (int infd, int outfd)
{
    unsigned char outbuf[OP_SIZE];
    int olen, tlen, n;
    char inbuff[IP_SIZE];
    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init (& ctx);
    EVP_EncryptInit (& ctx, EVP_bf_cbc (), key, iv);

    for (;;)
      {
          bzero (& inbuff, IP_SIZE);

          if ((n = read (infd, inbuff, IP_SIZE)) == -1)
            {
                perror ("read error");
                break;
            }
          else if (n == 0)
              break;

          if (EVP_EncryptUpdate (& ctx, outbuf, & olen, inbuff, n) != 1)
            {
                printf ("error in encrypt update\n");
                return 0;
            }

          if (EVP_EncryptFinal (& ctx, outbuf + olen, & tlen) != 1)
            {
                printf ("error in encrypt final\n");
                return 0;
            }
          olen += tlen;
          if ((n = write (outfd, outbuf, olen)) == -1)
              perror ("write error");
      }
    EVP_CIPHER_CTX_cleanup (& ctx);
    return 1;
}

Подпрограмма для дешифрования.

В основу дешифрования положена модель работы подпрограммы для шифрования. Следующий код показывает, как реализована эта подпрограмма.


int
decrypt (int infd, int outfd)
{
    unsigned char outbuf[IP_SIZE];
    int olen, tlen, n;
    char inbuff[OP_SIZE];
    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init (& ctx);
    EVP_DecryptInit (& ctx, EVP_bf_cbc (), key, iv);

    for (;;)
      {
          bzero (& inbuff, OP_SIZE);
          if ((n = read (infd, inbuff, OP_SIZE)) == -1)
            {
                perror ("read error");
                break;
            }
          else if (n == 0)
              break;

          bzero (& outbuf, IP_SIZE);

          if (EVP_DecryptUpdate (& ctx, outbuf, & olen, inbuff, n) != 1)
            {
                printf ("error in decrypt update\n");
                return 0;
            }

          if (EVP_DecryptFinal (& ctx, outbuf + olen, & tlen) != 1)
            {
                printf ("error in decrypt final\n");
                return 0;
            }
          olen += tlen;
          if ((n = write (outfd, outbuf, olen)) == -1)
              perror ("write error");
      }

    EVP_CIPHER_CTX_cleanup (& ctx);
    return 1;
}

Простую программу, в которой используются вышеприведенные функции можно взять здесь. Для компиляции выполните команду:

# gcc -o blowfish sym_funcs.c -lcrypto

При запуске откомпилированной программы в качестве параметров ей нужно передать имена трёх файлов:

  1. Файл, в котором содержится информация для шифрования
  2. Файл, в который будет заноситься зашифрованная информация
  3. Файл, в который будет заноситься расшифрованная информация

Пример: безопасный чат

Для создания безопасного чата нужно следовать такой схеме:

  1. Каждая программа-клиент имеет свой открытый и секретный ключ.
  2. Программа-клиент "знает" открытые ключи других клиентов, с которыми планируется связь.
  3. Генерируется сеансовый ключ программой-клиентом, которая инициирует подключение. Этот ключ будет использован для кодирования сообщений между двумя клиентами.
  4. Сеансовый ключ кодируется с использованием открытого ключа и передается другим (другому) клиентам.
  5. После такого "секретного рукопожатия" и начинается обмен данными.

Ссылки:

  1. Домашняя страничка OpenSSL
  2. Алгоритм "Блоуфиш"
  3. Учебник практической криптографии

Vinayak Hegde работает в Aparna Web services и занимается конфигурированием Linux-систем с удалённой зарузкой. В круг интересов входит организация и работа сетей, языки программирования. Компьютерами начал интересоваться из-за необходимости в дополнительной информации, а Linux предоставил доступ к исходным кодам многих программ.


Copyright ї 2003, Vinayak Hegde. Copying license http://www.linuxgazette.com/copying.html
Published in Issue 87 of Linux Gazette, February 2003


Команда переводчиков:
Владимир Меренков, Александр Михайлов, Иван Песин, Сергей Скороходов, Александр Саввин, Роман Шумихин, Александр Куприн, Андрей Киселев, Игорь Яровинский, Юрий Прушинский

Со всеми предложениями, идеями и комментариями обращайтесь к Александру Куприну ([email protected]). Убедительная просьба: указывайте сразу, не возражаете ли Вы против публикации Ваших отзывов в рассылке.

Сайт рассылки: http://gazette.linux.ru.net
Эту статью можно взять здесь: http://gazette.linux.ru.net/lg87/vinayak.html
Архивы выпусков находятся здесь: http://gazette.linux.ru.net/archive/