Автор: Vinayak Hegde
Перевод: Игорь Яровинский
Одной из главных потребностей современного информационного общества является защита используемой информации. Шифрование дает возможность не допустить утечку и попадание важных данных в руки злоумышленника.
Библиотеки 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Кб каждый.
Общую модель работы подпрограммы можно описать следующим образом:
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 |
При запуске откомпилированной программы в качестве параметров ей нужно передать имена трёх файлов:
Для создания безопасного чата нужно следовать такой схеме:
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