[Из песочницы] Использование библиотеки OpenSSL в проектах на C++
В своем первом топике я постараюсь подробно объяснить как начать использовать библиотеку OpenSSL. Сразу хочу отметить, что статья ориентирована на новичков, а так как я сам один из них, исходный код выполнен без проверок и не претендует на звание лучшего. Все действия выполнялись под Windows со средой разработки Visual Studio 2013.
Шаг 1. Установка необходимых компонентов и компиляция библиотеки
Для того, чтобы подружить библиотеку OpenSSL с Visual Studio нам потребуется:
- Архив с исходниками для компиляции библиотеки версии 1.0.1 (Скачать с оф. сайта)
- Perl для конфигурирования библиотеки (Скачать с оф. сайта)
- Среда разработки Visual Studio, а именно ее утилита «Командная строка разработчика»
После загрузки файлов устанавливаем Perl, распаковываем архив openssl-1.0.1a.tar.gz и его содержимое копируем в папку C:\openssl. Затем открываем командную строку Perl (command line) от имени администратора и пишем следующие команды:
cd c:\openssl
perl Configure VC-WIN32 --prefix=c:\Temp\openssl
ms\do_ms
Обратите внимание, что после выполнения второй команды последней строчкой в консоле должно быть Configured for VC-WIN32. Конфигурирование завершено, теперь приступим к компиляции. Находим в пуске в каталоге Visual Studio «Командная строка разработчика», запускаем от имени администратора и вводим команды:
cd c:\openssl
nmake -f ms\ntdll.mak
nmake -f ms\ntdll.mak install
На этом компиляция закончена. В папке C:\Temp\openssl\bin появились две библиотеки ssleay32.dll и libeay32.dll, а в папке C:\Temp\openssl\lib появились ssleay32.lib и libeay32.lib.
Шаг 2. Подключение библиотек и исправление ошибок в среде разработки
Для использования функций библиотеки нам потребуется подключить к проекту заголовочные файлы, но сначала их надо поместить в папку, где установлена Visual Studio. Для этого переходим в каталог …\Visual Studio\VC\include и копируем туда папку C:\Temp\openssl\include\openssl.
Теперь можно смело подключать заголовочные файлы (на примере rsa):
#include // Алгоритм RSA
#include // Для работы с файлами ключей
Также необходимо добавить в проект файлы с расширением .lib, которые мы получили ранее. Для этого откроем Обозреватель решений в Visual Studio, в дереве нашего проекта найдем папку Файлы ресурсов, щелкнем по ней правой клавишей → Добавить → Существующий элемент и выберем наши .lib файлы, находящиеся в C:\Temp\openssl\lib.
Вполне возможно, что при попытке собрать проект в окне вывода будут появляться ошибки такого рода:
Загружено "C:\Windows\SysWOW64\srvcli.dll". Невозможно найти или открыть PDB-файл.
Это лечится путем разрешения загрузки символов с серверов Microsoft. Переходим во вкладку Сервис → Параметры, слева в дереве выбираем Отладка → Символы, ставим галку напротив »Серверы символов Microsoft» и нажимаем ОК. Теперь при следующей компиляции проекта недостающие файлы загрузятся автоматически.
Также рекомендую переставить конфигурацию сборки программы в Release, только так мне удалось избавиться от некоторых ошибок.
Шаг 3. Описание некоторых функций библиотеки и пример кода
int RSA_size(const RSA *rsa) Вернет длину RSA модуля в байтах
RSA * RSA_generate_key(int bits, unsigned long e, void(*callback)(int, int, void*), void *cb_arg) генерирует пару ключей и возвращает в RSA структуру
int PEM_write_RSAPrivateKey(FILE *fp, RSA *x, const EVP_CIPHER *enc, unsigned char *kstr, int klen, pem_password_cb *cb, void *u) записывает закрытый ключ из структуры RSA в открытый файл
int PEM_write_RSAPublicKey(FILE *fp, const RSA *x) записывает открытый ключ из структуры RSA в открытый файл
RSA * PEM_read_RSAPublicKey(FILE *fp, RSA **x, pem_password_cb *cb, void *u) читает из файла открытый ключ и возвращает в RSA структуру
RSA * PEM_read_RSAPrivateKey(FILE *fp, RSA **x, pem_password_cb *cb, void *u) читает из файла закрытый ключ и возвращает в RSA структуру
int RSA_public_encrypt(int flen, const unsigned char *from, const unsigned char *to, RSA *rsa, int padding) шифрует строку "from" с помощью открытого ключа и возвращает в строку "to"
int RSA_private_decrypt(int flen, const unsigned char *from, const unsigned char *to, RSA *rsa, int padding) расшифровывает строку "from" с помощью закрытого ключа и возвращает в строку "to". Вернет "-1" если не удалось дешифровать.
Все необходимые компоненты, включая скомпилированный пример можно скачать с Яндекс.Диска.
Источники:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include
using namespace std;
void GenKeys(char secret[]);
void Enc();
void Dec(char secret[]);
void GenKeysMenu();
void EncryptMenu();
void DecryptMenu();
void main(){
setlocale(LC_ALL, "Russian");
char key;
StartMenu:
system("cls");
cout << "-------------- Шифрование RSA --------------" << endl << endl;
cout << " 1. Получение ключей" << endl;
cout << " 2. Зашифровать содержимое файла" << endl;
cout << " 3. Дешифровать содержимое файла" << endl << endl;
cout << "Ваш выбор: ";
cin >> key;
switch (key){
case '1': GenKeysMenu(); goto StartMenu;
case '2': EncryptMenu(); goto StartMenu;
case '3': DecryptMenu(); goto StartMenu;
default: goto StartMenu;
}
}
void GenKeys(char secret[]){
/* указатель на структуру для хранения ключей */
RSA * rsa = NULL;
unsigned long bits = 1024; /* длина ключа в битах */
FILE * privKey_file = NULL, *pubKey_file = NULL;
/* контекст алгоритма шифрования */
const EVP_CIPHER *cipher = NULL;
/*Создаем файлы ключей*/
privKey_file = fopen("\private.key", "wb");
pubKey_file = fopen("\public.key", "wb");
/* Генерируем ключи */
rsa = RSA_generate_key(bits, RSA_F4, NULL, NULL);
/* Формируем контекст алгоритма шифрования */
cipher = EVP_get_cipherbyname("bf-ofb");
/* Получаем из структуры rsa открытый и секретный ключи и сохраняем в файлах.
* Секретный ключ шифруем с помощью парольной фразы
*/
PEM_write_RSAPrivateKey(privKey_file, rsa, cipher, NULL, 0, NULL, secret);
PEM_write_RSAPublicKey(pubKey_file, rsa);
/* Освобождаем память, выделенную под структуру rsa */
RSA_free(rsa);
fclose(privKey_file);
fclose(pubKey_file);
cout << "Ключи сгенерированы и помещены в папку с исполняемым файлом" << endl;
}
void Encrypt(){
/* структура для хранения открытого ключа */
RSA * pubKey = NULL;
FILE * pubKey_file = NULL;
unsigned char *ctext, *ptext;
int inlen, outlen;
/* Считываем открытый ключ */
pubKey_file = fopen("\public.key", "rb");
pubKey = PEM_read_RSAPublicKey(pubKey_file, NULL, NULL, NULL);
fclose(pubKey_file);
/* Определяем длину ключа */
int key_size = RSA_size(pubKey);
ctext = (unsigned char *)malloc(key_size);
ptext = (unsigned char *)malloc(key_size);
OpenSSL_add_all_algorithms();
int out = _open("rsa.file", O_CREAT | O_TRUNC | O_RDWR, 0600);
int in = _open("in.txt", O_RDWR);
/* Шифруем содержимое входного файла */
while (1) {
inlen = _read(in, ptext, key_size - 11);
if (inlen <= 0) break;
outlen = RSA_public_encrypt(inlen, ptext, ctext, pubKey, RSA_PKCS1_PADDING);
if (outlen != RSA_size(pubKey)) exit(-1);
_write(out, ctext, outlen);
}
cout << "Содержимое файла in.txt было зашифровано и помещено в файл rsa.file" << endl;
}
void Decrypt(char secret[]){
RSA * privKey = NULL;
FILE * privKey_file;
unsigned char *ptext, *ctext;
int inlen, outlen;
/* Открываем ключевой файл и считываем секретный ключ */
OpenSSL_add_all_algorithms();
privKey_file = fopen("private.key", "rb");
privKey = PEM_read_RSAPrivateKey(privKey_file, NULL, NULL, secret);
/* Определяем размер ключа */
int key_size = RSA_size(privKey);
ptext = (unsigned char *)malloc(key_size);
ctext = (unsigned char *)malloc(key_size);
int out = _open("out.txt", O_CREAT | O_TRUNC | O_RDWR, 0600);
int in = _open("rsa.file", O_RDWR);
/* Дешифруем файл */
while (1) {
inlen = _read(in, ctext, key_size);
if (inlen <= 0) break;
outlen = RSA_private_decrypt(inlen, ctext, ptext, privKey, RSA_PKCS1_PADDING);
if (outlen < 0) exit(0);
_write(out, ptext, outlen);
}
cout << "Содержимое файла rsa.file было дешифровано и помещено в файл out.txt" << endl;
}
void GenKeysMenu(){
char secret[] = "";
system("cls");
cout << "-------------- Шифрование RSA --------------" << endl << endl;
cout << "Введите парольную фразу для закрытого ключа: ";
cin >> secret;
GenKeys(secret);
cout << "Нажмите любую кнопку для возврата в меню...";
_getch();
}
void EncryptMenu(){
system("cls");
cout << "-------------- Шифрование RSA --------------" << endl << endl;
Encrypt();
cout << "Нажмите любую кнопку для возврата в меню...";
_getch();
}
void DecryptMenu(){
char secret[] = "";
system("cls");
cout << "-------------- Шифрование RSA --------------" << endl << endl;
cout << "Введите парольную фразу для закрытого ключа: ";
cin >> secret;
Decrypt(secret);
cout << "Нажмите любую кнопку для возврата в меню...";
_getch();
}