Шифрование с использованием библиотек OpenSSL
By Vinayak Hegde
О чем эта статья
Линукс уже сделал свои первые уверенные шаги в корпоративном мире .
Одним из требований этого мира является защита данных.
И тут для сокрытия коммерческих данных от третьих лиц на помощь приходит криптование .
Open-source имеет репутацию софта с безопасным программированием .
Эта статья - еще один шаг в этом направлении .
Библиотека OpenSSL - это как раз то , что вам нужно для шифрования без погружения
в детали того , как реализован этот алгоритм .
Проблема в том , что документации по этому вопросу действительно мало .
Можно почитать исходники , поиграть с компиляцией и посмотреть , что выходит .
Имена функций в некотором смысле интуитивно помогают .
Еще один вариант - подписка на рассылку на сайте OpenSSL .
Командные утилиты OpenSSL также хорошо документированы и легки в использовании .
В этой статье я буду обьяснять , как использовать алгоритм blowfish для криптования с использованием
библиотек OpenSSL.
В раннюю эпоху криптографии алгоритмы , как и ключи , были секретны .
Но все меняется . Теперь алгоритмы общеизвестны . Примером является RSA-алгоритм ,
который широко известен . RSA - это а-симметричный алгоритм , использующий разные ключи
на входе и на выходе . Также использование RSA для криптования больших обьемов данных
неоправданно из-за больших вычислительных ресурсов .
Для криптования больших обьемов данных предпочтительны менее интенсивные вычислительные алгоритмы .
В этой статье мы используем алгоритм blowfish .
Blowfish - симметричный алгоритм , использующий один и тот же ключ для шифрования и дешифровки.
Blowfish был разработан знаменитым криптографом Bruce Schneier. Blowfish - это быстрый алгоритм .
Генерация ключей
Мы будем использовать 128-битный ключ.В программе он хранится как символьный массив .
Также мы сгенерируем 64-битный вектор(IV). Мы будем использовать режим Cipher Block Chaining (CBC) .
Функции blowfish будут использоваться не напрямую , а с помощью специального интерфейса .
Начальный вектор IV - (initialization vector) - это набор случайной информации , которая
используется на входе шифрования и обеспечивает работу всех его последующих стадий.
(blowfish использует 64-битные блоки для шифрования).
IV генерит условия для шифрования 1-го блока данных , из которого генерится 2-й блок данных , и т.д.
Случайные биты генерятся с помощью специального файла /dev/random , который обеспечивает
достаточно хорошую случайную последовательность чисел .
Для более детальной информации смотрите manpage.
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;
}
|
Функция для Encryption
Функция для шифровки имеет 2 параметра - файловый дескриптор входящего файла
и файл , куда будут записаны зашифрованные данные . Перед использованием буфера в памяти
его желательно обнулить . Это важно в случае повторного использования этого буфера .
В следующем примере входящие данные шифруются поблочно по 1 килобайту .
Последовательность шифрования следующая :
- Создание шифрованного контекста
- Инициализация контекста с помощью ключа и IV
- Вызов EVP_EncryptUpdate для криптования поблочно по 1k
- Вызов EVP_EncryptFinal для криптования оставшихся данных
- Вызов EVP_CIPHER_CTX_cleanup
Алгоритм Blowfish шифрует информацию поблочно по 64 бита . Иногда размер блока может быть
меньше , чем 64 бита . Это случается , когда размер данных не кратен 8 байтам (64 битам).
К нему добавляется данные и блок зашифровывается с помощью EVP_EncryptFinal.
Длина данных хранится в переменной в конце блока .
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;
}
|
Полный код
Минимальная интерактивная программа с реализацией вышеуказанных функций приведена здесь :
#include < openssl/blowfish.h>
#include < openssl/evp.h>
#include < fcntl.h>
#include < stdio.h>
#include < sys/stat.h>
#include < sys/types.h>
#define IP_SIZE 1024
#define OP_SIZE 1032
unsigned char key[16];
unsigned char iv[8];
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;
}
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;
}
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
main (int argc, char *argv[])
{
int flags1 = 0, flags2 = 0, outfd, infd, decfd;
mode_t mode;
char choice, temp;
int done = 0, n, olen;
bzero (&key, 16);
bzero (&iv, 8);
bzero (&mode, sizeof (mode));
flags1 = flags1 | O_RDONLY;
flags2 = flags2 | O_RDONLY;
flags2 = flags2 | O_WRONLY;
flags2 = flags2 | O_CREAT;
mode = mode | S_IRUSR;
mode = mode | S_IWUSR;
while (!done)
{
printf ("E - Encrypt a file\n");
printf ("D - Decrypt a file\n");
printf ("G - Generate a key\n");
printf ("Q - Quit\n");
choice = getchar ();
temp = getchar ();
switch (choice)
{
case 'e':
case 'E':
if ((infd = open (argv[1], flags1, mode)) == -1)
perror ("open input file error");
if ((outfd = open (argv[2], flags2, mode)) == -1)
perror ("open output file error");
encrypt (infd, outfd);
close (infd);
close (outfd);
break;
case 'd':
case 'D':
if ((outfd = open (argv[2], flags1, mode)) == -1)
perror ("open output file error");
if ((decfd = open (argv[3], flags2, mode)) == -1)
perror ("open output file error");
decrypt (outfd, decfd);
close (outfd);
fsync (decfd);
close (decfd);
break;
case 'g':
case 'G':
generate_key ();
break;
case 'Q':
case 'q':
done = 1;
break;
default:
printf ("ERROR: Unrecognized command. Try again.\n");
break;
}
}
return 0;
}
Компиляция программы :
# gcc -o blowfish sym_funcs.c -lcrypto
| |
Программе в качестве параметров нужно указать 3 внешних файла :
- Файл , который нужно зашифровать
- Файл , в который все будет зашифровано
- Файл , в котором все будет расшифровано
Не забудьте сгенерировать ключи перед шифрованием .
Пример приложения - Secure Instant Messenger
Рассмотрим программу instant messenger software (IM) , который взаимодействует с другим
IM. Может быть апробирован следующий метод :
- Каждый IM-клиент имеет свой private ключи.
- IM-клиент имеет также public keys , как и все остальные IM.
- Клиентом генерится т.н. сессионный ключ . Этот ключ используется для шифрования
данных между 2-мя клиентами .
- Этот сессионный ключ шифруется и обменивается между 2-мя клиентами с помощью public-ключа .
(RSA-алгоритм).
- Обмен шифрованными данными (с помощью Blowfish) происходит после "секретного рукопожатия".
Ресурсы
- OpenSSL Homepage
- The Blowfish Algorithm
- Handbook of Applied Cryptography
Copyright © 2003, Vinayak Hegde.
Copying license http://www.linuxgazette.com/copying.html
Published in Issue 87 of Linux Gazette, February 2003
|
|