Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org

Шифрование с использованием библиотек 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 килобайту . Последовательность шифрования следующая :

  1. Создание шифрованного контекста
  2. Инициализация контекста с помощью ключа и IV
  3. Вызов EVP_EncryptUpdate для криптования поблочно по 1k
  4. Вызов EVP_EncryptFinal для криптования оставшихся данных
  5. Вызов 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 внешних файла :

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

Пример приложения - Secure Instant Messenger

Рассмотрим программу instant messenger software (IM) , который взаимодействует с другим IM. Может быть апробирован следующий метод :

  1. Каждый IM-клиент имеет свой private ключи.
  2. IM-клиент имеет также public keys , как и все остальные IM.
  3. Клиентом генерится т.н. сессионный ключ . Этот ключ используется для шифрования данных между 2-мя клиентами .
  4. Этот сессионный ключ шифруется и обменивается между 2-мя клиентами с помощью public-ключа . (RSA-алгоритм).
  5. Обмен шифрованными данными (с помощью Blowfish) происходит после "секретного рукопожатия".

Ресурсы

  1. OpenSSL Homepage
  2. The Blowfish Algorithm
  3. 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
Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье