Приложение, использующее пакет GPG ( https://ru.wikipedia.org/wiki/GnuPG ) и имеющее следующие функции:
Генерация ключей. Экспорт, импорт, подпись ключей, удаление ключей.
Шифрование. Расшифровка.
Для передачи сообщений использовать любой доступный канал связи.
Использование синонимов вместо идентификаторов ключей.
История
Изначально приложение задумывалось для переписки во vkontakte через vk Api(программный интерфейс для доступа к VK используя своё собственное приложение, а не браузер или официальное мобильное приложение) из под учетной записи пользователя. Но эти товарищи заблокировали возможность чтения своих сообщений(по умолчанию). Получить доступ можно, но для этого нужно пройти через разные унижения и этот вариант отпал.
А для отправки сообщений через vk Api запретов по умолчанию нет, но тогда выглядело бы это однобоко(отправляем автоматически, а читаем вручную) : пишем сообщение, шифруем, отправляем, получаем сообщение от другого пользователя в vk, вручную копируем в приложение, расшифровываем.
В vk есть ещё один нюанс. Там любят, когда ваша переписка ведётся в незашифрованном виде, чтобы, если будет нужно, прочитать её (просто так до вас, конечно, дела нет(до критического количества подписчиков, кажется, 100 тысяч ), но при каких-либо технических работах(внедрение нового фильтра) случайный выбор падёт на вас).
Шифровать можно не всё подряд, а только самое ценное(без фанатизма).
- - - - - - - - - - - - -
Установка
- - 1 вариант - -
Установка не требуется. Нужно распаковать архив(полностью) download GnuPGunpacked.zip , а не просто запустить приложение из архива.
Имена каталогов(куда вы распакуете архив) могут содержать любые символы, но если что-то не работает, то удалите русские символы и пробелы.
Там в каталоге \GnuPG\bin есть приложение encrypt-decrypt_365.exe. Запускаете. Всё.
В архиве есть и 64-х и 32-х разрядная версии:
- - 2 вариант - -
Нужно запустить download gpg4win3110.zip (или скачать в другом месте)
Но запустить нужно с ключом /S (описание есть в .pdf, вложенном в архив) :
пример: gpg4win.exe /S /D=D:\Programme\Gpg4win
И тогда вы придёте к варианту № 1. Скорируйте приложение encrypt-decrypt_365.exe из архива в пункте 1 в каталог \GnuPG\bin.
Особенности запуска
Запуск приложения encrypt-decrypt_365.exe должен быть под тем же пользователем, под которым создавались ключи.
Ключи создаются и хранятся в каталоге пользователя типа этого C:\Users\User\AppData\Roaming\gnupg\ (вместо User - ваш пользователь) .
И при удалении приложения не забудьте что-нибудь сделать с этим каталогом.
То есть если вы запустите приложение вначале под Администратором, создадите ключи, а затем запустите под другим(своим) пользователем, то тех ключей вы не увидите.
Т.о. можно создавать разные группы ключей для разных пользователей.
Короткая инструкция:
1. сгенерировать ключи
2. передать свой открытый ключ другому пользователю (экспорт)
3. получить(импорт) от другого пользователя его открытый ключ. (+ Сверить отпечатки ключей. Подписать ключ)
4. Шифруем/расшифровываем сообщения.
- - - - - - - - - - - - -
Инструкция :
Создание новой пары ключей(приватного и публичного)
Кнопка 'generate key'. Нужно пройти диалог. Пример того, что нужно ввести, есть на картинке. Обведено оранжевым цветом.
Также появится окно с заголовком Pinentry, где нужно ввести(два раза) парольную фразу для защиты создаваемого приватного ключа.
(есть небольшие глюки с отображением шрифтов, поэтому вместо нормального текста - знаки вопроса)
После завершения диалога в списке появится созданный ключ (его идентификатор). Знак '+' слева от id ключа означает, что есть приватный + публичный ключи. (знак '-' будет означать, что есть только публичный ключ)
Публичным ключом сообщения шифруются. Приватным ключом - расшифровываются.
Удаление приватного ключа.
Если нужно удалить ключи, но вначале удаляется приватный, затем - публичный. Нужно выбрать ключ в списке. Нажать 'delete secret key'. В появившемся диалоге подтвердить удаление.
После удаления приватного ключа знак '+' слева от id этого ключа (в списке) изменится на '-'(остался только публичный ключ).
Удаление публичного ключа.
Выбрать ключ в списке. Нажать 'delete public key'. В появившемся диалоге подтвердить удаление.
Использование синонимов вместо идентификаторов ключей.
Так проще распознавать, с кем вы ведёте переписку. В списке ваших ключей вы видите идентификаторы ( Ivan7 (no) <ivan777@mail.ru> ) , которые могут ничего не значить и трудно запоминаться.
Тогда вы можете назначить этому id осмысленное для вас имя.
Нажимаете кнопку 'Syn1'. Верхнее текстовое поле заполняется информацией о ваших ключах(включая синонимы, если вы ранее уже их указывали). Вначале - идентификатор, затем отпечаток ключа и после отпечатка через пробел укаываете синоним. Если вы указываете синонимы только для некоторых ключей, то остальные строки можно не удалять.
Для сохранения настройки нажмите кнопку 'Syn2'. ( В каталоге \GnuPG\bin будет создан файл synonyms.txt, который можно удалить для прекращения использования этой функции.)
Списки ключей двух пользователей. У Пользователя 1 есть 1 ключ, у Пользователя 2 есть 3 ключа.
Экспорт открытого ключа.
Пользователь 1 экспортирует открытый ключ. нужно выбрать ключ в списке и нажать 'Export public key'. Ключ выведется в верхнее текстовое поле. Его можно передать другому лицу любым способом.
Импорт открытого ключа.
Пользователь 2 загружает к себе открытый ключ, полученный от Пользователя 1. Нужно скопировать ключ в верхнее текстовое поле и нажать 'Import public key'. Список ключей обновится и там будет новый ключ. Слева от него будет знак '-' - это означает, что у вас есть только открытый ключ. (открытые ключи можно передавать кому угодно, закрытые ключи хранятся только у вас)
(Перед импортом) При получении открытого ключа проконтролируйте, что отсутствуют искажения в начале и окончании текстового представления ключа. Например, при передаче ключа через VK бывает, что пара дефисов "сливается" в один символ(уже не дефис) и ключ не импортируется. Везде по 5 дефисов.
Шифрование сообщения.
Вы получили и импортировали ключ, полученный от другого пользователя. Выберите его в списке, напишите сообщение в верхнем текстовом поле и нажмите кнопку 'enctypt'. В нижнем текстовом поле появится зашифрованное сообщение. Его отправляете владельцу этого ключа. У него есть приватный ключ, которым он расшифрует это сообщение.
На картинке вы видите чёрное окно с дополнительным вопросом. Ответьте положительно. Этот дополнительный вопрос появился потому, что вы ещё не подписали импортированный ключ своим приватным ключом. Чужой ключ не обязательно подписывать, но тогда этот дополнительный вопрос будет каждый раз появляться. Полписывание ключа - в следующем пункте.
(также на картинке есть отпечатки этого ключа)
Подпись импортированного ключа.
Для подписи ключа нажмите кнопку 'Sign public key', но
Прежде, чем подписать импортированный ключ, нужно сверить отпечаток загруженного ключа с оригинальным отпечатком. Нужно это для того, чтобы убедится, что полученный вами ключ никто не подменил в процессе передачи. Сверить отпечатки можно по другому каналу связи (телефонный звонок владельцу ключа). Для вывода отпечатков ваших ключей - нажмите кнопку 'Syn1'. Или вернитесь к предыдущему пункту - там тоже выводится нужный отпечаток. Или можете запустить подпись ключа( 'Sign public key' ) - там тоже выводится отпечаток первичного ключа. После сверки ответьте на все вопросы положительно.
Если отпечатки не совпадают - удалите этот ключ ( 'delete public key' ).
Расшифровка сообщения.
Вставьте зашифрованное сообщение в нижнее текстовое поле. Выберите нужный(свой) ключ в списке. Нажмите 'decrypt'. В верхнем текстовом поле появится расшифрованное сообщение.
В процессе переписки может быть неудобно каждый раз менять ключи при шифровании(выбирать открытый ключ собеседника) и расшифровке(выбирать свой ключ). Поэтому вы может выбрать в списке открытый ключ собеседника, а при расшифровке приложение переберёт последовательно ключи, найдёт нужный и расшифрует сообщение(то есть в итоге будет использовать не выбранный в списке ключ, подходящий для расшифровки. Цена - чуть медленнее.).
Что это такое?
Это функция, которой лучше не пользоваться - Хранение пароля от приватного ключа в файле. Сейчас так: когда вы начинаете сеанс работы с этим приложением, то при первом обращении к функции, задействующей приватный ключ (Шифрование, подпись ключа) появится окно для ввода парольной фразы, которую вы задали при создании ключа. После ввода пароля он сохраняется и не запрашивается до конца сеанса(завершение работы операционной системы или можете, открыв Диспетчер задач, найти в фоновых процессах и завершить приложение GnuPG's private key daemon) Будет так: если вы создадите в каталоге \GnuPG\bin файл passphrase.txt со строкой-парольной фразой, то окно для ввода парольной фразы не появится - будет использоваться первая строка из этого файла. Если пароль ошибочен, то вы не получите сообщения об этом. И ещё, парольные фразы у вас могут быть разные для разных ключей, а в файле указывается только одна фраза.
Если вы создадите этот файл и перезапустите приложение, то символы дефисов '- - - -' изменятся на '********'.
исходный код приложения:
// протестировано с gpg 2.2.17//компиляция://cd "C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0\mingw64\bin"////x86_64-w64-mingw32-gcc.exe encrypt-decrypt_365.c -l:libgdi32.a -o encrypt-decrypt_365.exe -mwindows////x86_64-w64-mingw32-gcc.exe encrypt-decrypt_365.c -l:libgdi32.a -o encrypt-decrypt_365.exe -mwindows -mconsole////cd "C:\Program Files (x86)\C-Free 5\mingw\bin"//mingw32-gcc.exe encrypt-decrypt_365.c -l:libgdi32.a -o encrypt-decrypt_365_32bit.exe -mwindows#include <windows.h>// заголовочный файл, содержащий WINAPI#include <stdio.h>#include <commctrl.h>#include <string.h>LRESULTCALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);LRESULT APIENTRY EditSubclassProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);LRESULT APIENTRY Edit2SubclassProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);int encodeToBase64(char* inF,char* outB64F);int copyTextToFile(HWND id,char* fname);int decodeBase64(char* inF,char* outT);int copyTextToControl(HWND id,char* fname );int deleteFiles(char* f1,char* f2,char* f3);void getField(int nfield,constchar* source,char* target ,int maxLen);//TCHAR mainMessage[] = "Kakoi-to text!"; // строка с сообщениемHWND editctl, editctl2, hWnd_button2, hWnd_buttonDecrypt, hWnd_expPubKey, hWnd_signPubKey, hWnd_impPubKey,
hWnd_ComboBox_pub, hWnd_Text1, hWnd_synonym1, hWnd_synonym2, hWnd_genKey, hWnd_delPrivateKey, hWnd_delPublicKey;HWND hMainWnd;// дескриптор будущего окнаchar sizeCL[60];WORD sx, sy;WNDPROC wpOrigEditProc, wpOrigEdit2Proc;int slen;char* passw =NULL;char* synR,* synRc;#define fprML 40#define uidML 150char str[400];// [fprML + uidML + 40];struct infoKey {char fpr[fprML+1];char uid[uidML+1];char synonym[100];int withoutPrivate;};int numPubKeys =0, currSel =-1;struct infoKey * arrInfoKey =NULL;int fillKeyList(int* numPubKeys,size_t sz,struct infoKey ** arrInfoKey);void updKeyList(int* numPubKeys,size_t sz,struct infoKey ** arrInfoKey,char* str);void loadPassPhrase(char** passw);/* 64-bit */ longlong id_button2 =10001;longlong id_buttonDecrypt =10003;longlong IDC_EDIT =10002;longlong id_expPubKey =10004;longlong id_impPubKey =10005;longlong id_signPubKey =10006;longlong id_ComboBox_pub =10007;longlong id_synonym1 =10008;longlong id_synonym2 =10009;longlong id_genKey =10010;longlong id_delPrivateKey =10011;longlong id_delPublicKey =10012;/* // 32-bitint id_button2 = 10001;int id_buttonDecrypt = 10003;int IDC_EDIT = 10002;int id_expPubKey = 10004;int id_impPubKey = 10005;int id_signPubKey = 10006;int id_ComboBox_pub = 10007;int id_synonym1 = 10008;int id_synonym2 = 10009;int id_genKey = 10010;int id_delPrivateKey = 10011;int id_delPublicKey = 10012;*/// Управляющая функция:intWINAPIWinMain(HINSTANCE hInst,// дескриптор экземпляра приложенияHINSTANCE hPrevInst,// не используемLPSTR lpCmdLine,// не используемint nCmdShow)// режим отображения окна{TCHAR szClassName[]="Mein Kampf";// строка с именем классаMSG msg;// создаём экземпляр структуры MSG для обработки сообщенийWNDCLASSEX wc;// создааём экземпляр, для обращения к членам класса WNDCLASSEX
wc.cbSize =sizeof(wc);// размер структуры (в байтах)
wc.style = CS_HREDRAW | CS_VREDRAW;// стиль класса окна
wc.lpfnWndProc = WndProc;// указатель на пользовательскую функцию
wc.lpszMenuName =NULL;// указатель на имя меню (у нас его нет)
wc.lpszClassName = szClassName;// указатель на имя класса
wc.cbWndExtra =0;// число освобождаемых байтов в конце структуры
wc.cbClsExtra =0;// число освобождаемых байтов при создании экземпляра приложения
wc.hIcon =LoadIcon(NULL, IDI_WINLOGO);// декриптор пиктограммы
wc.hIconSm =LoadIcon(NULL, IDI_WINLOGO);// дескриптор маленькой пиктограммы (в трэе)
wc.hCursor =LoadCursor(NULL, IDC_ARROW);// дескриптор курсора
wc.hbrBackground =(HBRUSH)GetStockObject(WHITE_BRUSH);// дескриптор кисти для закраски фона окна
wc.hInstance = hInst;// указатель на строку, содержащую имя меню, применяемого для классаif(!RegisterClassEx(&wc)){// в случае отсутствия регистрации класса:MessageBox(NULL,"Ne poluchilos zaregistrirovat class!","Err_Err", MB_OK);return1;// возвращаем, следовательно, выходим из WinMain}// Функция, создающая окно:
hMainWnd =CreateWindow(
szClassName,// имя класса"encrypt-decrypt",// имя окна (то что сверху)WS_OVERLAPPEDWINDOW,// | WS_VSCROLL, // режимы отображения окна
CW_USEDEFAULT,// позиция окна по оси х0,// позиция окна по оси у (раз дефолт в х, то писать не нужно)
CW_USEDEFAULT,// ширина окна0,// высота окна (раз дефолт в ширине, то писать не нужно)NULL,// дескриптор родительского окнаNULL,// дескриптор меню
hInst,// дескриптор экземпляра приложенияNULL);// ничего не передааём из WndProcif(!hMainWnd){// в случае некорректного создания окна (неверные параметры и тп):MessageBox(NULL,"Window is not created!","Err_Err2", MB_OK);return0;}//////////////button2////////////////////////////////////Создаем кнопку2
hWnd_button2 =CreateWindow(TEXT("button"),TEXT("encrypt"),WS_CHILD|WS_VISIBLE| BS_PUSHBUTTON,10,30,130,30, hMainWnd,(HMENU)id_button2, hInst,NULL);//////////////Decrypt button////////////////////////////////////Создаем кнопку Decrypt
hWnd_buttonDecrypt =CreateWindow(TEXT("button"),TEXT("decrypt"),WS_CHILD|WS_VISIBLE| BS_PUSHBUTTON,400,30,130,30, hMainWnd,(HMENU)id_buttonDecrypt, hInst,NULL);//Создаем кнопку для экспорта открытого ключа
hWnd_expPubKey =CreateWindow(TEXT("button"),TEXT("Export public key"),WS_CHILD|WS_VISIBLE| BS_PUSHBUTTON,370,10,130,20, hMainWnd,(HMENU)id_expPubKey, hInst,NULL);//Создаем кнопку для инпорта открытого ключа
hWnd_impPubKey =CreateWindow(TEXT("button"),TEXT("Import public key"),WS_CHILD|WS_VISIBLE| BS_PUSHBUTTON,370,35,130,20, hMainWnd,(HMENU)id_impPubKey, hInst,NULL);//Создаем кнопку для подписания открытого ключа
hWnd_signPubKey =CreateWindow(TEXT("button"),TEXT("Sign public key"),WS_CHILD|WS_VISIBLE| BS_PUSHBUTTON,510,30,110,30, hMainWnd,(HMENU)id_signPubKey, hInst,NULL);// кнопка отображения текущего списка id + отпечаток + синоним (для редактирования)
hWnd_synonym1 =CreateWindow(TEXT("button"),TEXT("Syn1"),WS_CHILD|WS_VISIBLE| BS_PUSHBUTTON,300,10,50,20, hMainWnd,(HMENU)id_synonym1, hInst,NULL);// кнопка для записи отредактированного списка
hWnd_synonym2 =CreateWindow(TEXT("button"),TEXT("Syn2"),WS_CHILD|WS_VISIBLE| BS_PUSHBUTTON,300,36,50,20, hMainWnd,(HMENU)id_synonym2, hInst,NULL);// кнопка для запуска генерации ключей
hWnd_genKey =CreateWindow(TEXT("button"),TEXT("generate key"),WS_CHILD|WS_VISIBLE| BS_PUSHBUTTON,150,3,130,19, hMainWnd,(HMENU)id_genKey, hInst,NULL);// кнопка для запуска удаления приватного ключа
hWnd_delPrivateKey =CreateWindow(TEXT("button"),TEXT("delete secret key"),WS_CHILD|WS_VISIBLE| BS_PUSHBUTTON,150,25,130,19, hMainWnd,(HMENU)id_delPrivateKey, hInst,NULL);// кнопка для запуска публичного ключа
hWnd_delPublicKey =CreateWindow(TEXT("button"),TEXT("delete public key"),WS_CHILD|WS_VISIBLE| BS_PUSHBUTTON,150,47,130,19, hMainWnd,(HMENU)id_delPublicKey, hInst,NULL);////////////// EDIT 1/////////////////////////////////////////
editctl =CreateWindow("EDIT","",WS_CHILD|WS_VISIBLE|WS_BORDER|WS_VSCROLL|WS_HSCROLL|
ES_LEFT | ES_AUTOHSCROLL | ES_WANTRETURN | ES_MULTILINE | ES_AUTOVSCROLL | ES_NOHIDESEL,10,70,100,100, hMainWnd,(HMENU)IDC_EDIT, hInst,0);//////////////// установка своей оконной процедуры для Edit// Retrieve the handle to the edit control. //hwndEdit = GetDlgItem(hWnd, IDC_EDIT); // возвращает то же значение, которое уже есть в editctl (в WndProc)// Subclass the edit control. //wpOrigEditProc = (WNDPROC)SetLastError(0);// GWL_WNDPROC = -4 //(long long), not (long)
wpOrigEditProc =(WNDPROC)SetWindowLongPtr(editctl,(int)-4,(LONG_PTR)EditSubclassProc);if(0== wpOrigEditProc){//dw = GetLastError(); //sprintf(sizeCL, "errorCode = %d", dw);//MessageBox(hWnd, sizeCL, TEXT(""), 0); // Code: 1400. Недопустимый дескриптор окнаMessageBox(hMainWnd,"Error of a reset WNDPROC for Edit1.",TEXT(""),0);}////////////// EDIT 2/////////////////////////////////////////
editctl2 =CreateWindow("EDIT","",WS_CHILD|WS_VISIBLE|WS_BORDER|WS_VSCROLL|WS_HSCROLL|
ES_LEFT | ES_AUTOHSCROLL | ES_WANTRETURN | ES_MULTILINE | ES_AUTOVSCROLL | ES_NOHIDESEL,10,200,100,50, hMainWnd,(HMENU)IDC_EDIT, hInst,0);//////////////// установка своей оконной процедуры для EditSetLastError(0);// GWL_WNDPROC = -4 //(long long), not (long)
wpOrigEdit2Proc =(WNDPROC)SetWindowLongPtr(editctl2,(int)-4,(LONG_PTR)Edit2SubclassProc);if(0== wpOrigEdit2Proc)MessageBox(hMainWnd,"Error of a reset WNDPROC for Edit2.",TEXT(""),0);///////////////// COMBOBOX //////////////////////////////////////
hWnd_ComboBox_pub =CreateWindow("combobox",NULL,WS_CHILD|WS_VISIBLE|WS_VSCROLL| CBS_AUTOHSCROLL | CBS_DROPDOWNLIST | CBS_DISABLENOSCROLL,640,30,400,210, hMainWnd,(HMENU) id_ComboBox_pub, hInst,NULL);
updKeyList(&numPubKeys,sizeof(struct infoKey),&arrInfoKey, str);////////////////// STATIC /////////////////////////////
hWnd_Text1 =CreateWindow("STATIC","- - - -",WS_VISIBLE|WS_CHILD| SS_CENTER,150,30,60,30,
hMainWnd,NULL, hInst,NULL);
loadPassPhrase(&passw);ShowWindow(hMainWnd, nCmdShow);// отображаем окноUpdateWindow(hMainWnd);// обновляем окноwhile(GetMessage(&msg,NULL,0,0)){// извлекаем сообщения из очереди, посылаемые фу-циями, ОСTranslateMessage(&msg);// интерпретируем сообщенияDispatchMessage(&msg);// передааём сообщения обратно ОС}return msg.wParam;// возвращаем код выхода из приложения}LRESULTCALLBACK WndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam){//HDC hDC; // создааём дескриптор ориентации текста на экране//PAINTSTRUCT ps; // структура, сод-щая информацию о клиентской области (размеры, цвет и тп)//RECT rect; // стр-ра, определяющая размер клиентской области//COLORREF colorText = RGB(255, 0, 0); // задааём цвет текстаint resB64;staticint i;switch(uMsg){case WM_CREATE:// при этом событии(WM_CREATE) остальных элементов ещё не сущестует и следовательно все их дескрипторы = 0break;///////////////////////////////////////////case WM_INITDIALOG:;//This is an empty statement.break;case WM_COMMAND:if(LOWORD(wParam)== id_button2)// encrypt{//+++1. Копирование текста из editctl в файл//+++2. запуск gpg. Шифрование сообщения//+++3. кодирование полученного шифрованого текста в Base64//+++4. копирование текста base64 в editctl2//+++5. удаляем созданные файлы//6. добавить кнопку для удаления текста в editctl.if( currSel ==-1){MessageBox(hMainWnd,"Select one public key.",TEXT(""),0);break;}if( copyTextToFile( editctl,"out.txt")==1)break;/* 2. запуск gpg. Шифрование сообщения system() ожидает завершения (по умолчанию) запуск основного приложения должен быть под тем же пользователем , под которым создавались ключи так как они лежат в каталоге пользователя типа этого C:\Users\User\AppData\Roaming\gnupg\ */// можно добавить --throw-keyids - Do not put the recipient key IDs into encrypted messagessprintf(str,"gpg.exe --yes --output outEnc.txt --recipient %s --encrypt out.txt", arrInfoKey[currSel].fpr);system(str);// 3.кодирование полученного шифрованого текста в Base64
resB64 = encodeToBase64("outEnc.txt","outB64.txt");if(resB64 ==1){MessageBox(hWnd,"Cannot open incoming file.",TEXT(""),0);break;}elseif(resB64 ==2){MessageBox(hWnd,"Cannot open outcoming file.",TEXT(""),0);break;}// 4. копирование текста base64 из файла в editctl2if( copyTextToControl( editctl2,"outB64.txt")!=0)break;//5. удаляем созданные файлыif( deleteFiles("out.txt","outEnc.txt","outB64.txt")==1)MessageBox(hWnd,"Error removing of one or some files.",TEXT(""),0);}// encrypt-button click END// decrypt-button clickelseif(LOWORD(wParam)== id_buttonDecrypt){//+++1. Копирование зашифрованного текста из editctl2 в файл//+++2. декодирование текста. Снимаю Base64//+++3. запуск gpg. Расшифровка сообщения//+++4. копирование расшифрованного текста из файла в editctl//+++5. удаляем созданные файлы//6. добавить кнопку для удаления текста в editctl2.if( copyTextToFile( editctl2,"in.txt")==1)break;// 2.декодирование текста. Снимаю Base64
resB64 = decodeBase64("in.txt","inEnc.txt");if(resB64 ==1){MessageBox(hWnd,"Cannot open incoming file.",TEXT(""),0);break;}elseif(resB64 ==2){MessageBox(hWnd,"Cannot open outcoming file.",TEXT(""),0);break;}//3. запуск gpg. Расшифровка сообщения//Парольная фраза для разблокировки приватного ключа вводится один раз и//хранится до конца сеанса.// при выполнении команды --decrypt запускается GnuPG's private key daemon// --recipient и --default-key здесь не актуальны// если "gpg.exe --pinentry-mode loopback --passphrase pw" пароль неправильный, то// команда не выполняется и ничего не выведется.// Если при шифровании используется --throw-keyids, то при расшифровке перебираются приватные ключи,// находится нужный, запрашивается пароль.// заметил. У меня было два ключа (две пары) с полностью одинаковы id и паролем.// Зашифровал сообщение одним ключом, указав Отпечаток (отпечатки то разные).// При расшифровке ввел пароль.// Зашифровал сообщение вторым ключом. А при расшифровке пароль не потребовался.// Но ключ-то другой (id такой же). (?)if( passw !=NULL){sprintf(str,"gpg.exe --pinentry-mode loopback --passphrase \"%s\" --yes --output inDec.txt --decrypt inEnc.txt", passw);system(str);}elsesystem("gpg.exe --yes --output inDec.txt --decrypt inEnc.txt");//4. копирование расшифрованного текста из файла в editctlif( copyTextToControl( editctl,"inDec.txt")!=0)break;//5. удаляем созданные файлыif( deleteFiles("in.txt","inEnc.txt","inDec.txt")==1)MessageBox(hWnd,"Error removing of one or some files.",TEXT(""),0);}// decrypt-button click END// export-button click STARTelseif(LOWORD(wParam)== id_expPubKey){// export public key//1. вывод ключа в файл//2. копирование ключа из файла в editctl//3. удаление файла//1. вывод ключа// ключ выводится в файл в ASCII формате.if( currSel ==-1){MessageBox(hMainWnd,"Select one public key.",TEXT(""),0);break;}sprintf(str,"gpg.exe --armor --output pubKey.txt --yes --export %s", arrInfoKey[currSel].fpr);system(str);//2. копирование ключа из файла в editctlint r1;if((r1 = copyTextToControl( editctl,"pubKey.txt"))!=0){if( r1 ==1)MessageBox(hWnd,"Is you using correct email(id)?",TEXT(""),0);break;}//3. удаление файлаif( deleteFiles("pubKey.txt",NULL,NULL)==1)MessageBox(hWnd,"Error removing of one or some files.",TEXT(""),0);}elseif(LOWORD(wParam)== id_impPubKey){// Import public key// при импорте ключа добавляется также суб ключ// +++1. Копирую ключ из верхнего текстового поля в файл// +++2. запуск коменды импорта// +++3. удалить файлif( copyTextToFile( editctl,"pubKeyI.txt")==1)break;system("gpg.exe --import pubKeyI.txt");if( deleteFiles("pubKeyI.txt",NULL,NULL)==1)MessageBox(hWnd,"Error removing the file.",TEXT(""),0);
updKeyList(&numPubKeys,sizeof(struct infoKey),&arrInfoKey, str);}elseif(LOWORD(wParam)== id_signPubKey){// Sign public key// не подписанным ключом можно шифровать сообщения,// но каждый раз gpg будет задавать дополнительный вопрос.//+++1. Запуск последовательности операций "sign trust save"" для подписи ключа,// установки значения доверия владельца, и сохранение.// будет диалог со вводом парольной фразы (выбор приватного ключа по умолчанию)//system("gpg.exe --yes --edit-key id sign");if( currSel ==-1){MessageBox(hMainWnd,"Select one public key.",TEXT(""),0);break;}// без показа окна pinentry. Но парольная фраза для конкретного ключа и будет// использоваться для того ключа, который выбран по умолчанию.//system("gpg.exe --pinentry-mode loopback --passphrase \"rE6ds18R\" --yes --edit-key keyId sign trust save");//для этой команды можно использовать --default-key . //Тогда подписываться импортированный ключ будет указанным приватным ключом.//gpg использует ключ по умолчанию, откроется окно Pinentry для ввода парольной фразы или//если пароль прочитан из файла, то он будет//использован без открытия Pinentry.if( passw !=NULL){sprintf(str,"gpg.exe --pinentry-mode loopback --passphrase \"%s\" --yes --edit-key %s sign trust save",
passw, arrInfoKey[currSel].fpr);}elsesprintf(str,"gpg.exe --yes --edit-key %s sign trust save", arrInfoKey[currSel].fpr);system(str);// при этом текстовый диалог останется.//y//5//y}//редактирование списка синонимовelseif(LOWORD(wParam)== id_synonym1 ){if( arrInfoKey ==NULL)break;
slen =0;for(i =0; i < numPubKeys; i++){
slen +=strlen(arrInfoKey[i].fpr);
slen +=strlen(arrInfoKey[i].uid);
slen +=strlen(arrInfoKey[i].synonym);}
slen += numPubKeys *6;// 2 - <>; 2 - 1310; 2 - ' '
synR =(char*)malloc( slen +1);if( synR ==NULL){MessageBox(hMainWnd,"err of malloc()",TEXT(""),0);break;}
synR[slen]='\0';
synRc = synR;for(i =0; i < numPubKeys; i++){sprintf(str,"<%s> %s%s\xD\xA", arrInfoKey[i].uid, arrInfoKey[i].fpr, arrInfoKey[i].synonym);
slen =strlen(str);strncpy(synRc, str, slen);
synRc += slen;}SetWindowText(editctl, synR);free(synR);
synR =NULL;
synRc =NULL;}// сохранение списка синонимовelseif(LOWORD(wParam)== id_synonym2 ){if( copyTextToFile( editctl,"synonyms.txt")==1)break;SetWindowText(editctl,"");
updKeyList(&numPubKeys,sizeof(struct infoKey),&arrInfoKey, str);}// запуск генерирования ключейelseif(LOWORD(wParam)== id_genKey ){system("gpg.exe --full-generate-key");
updKeyList(&numPubKeys,sizeof(struct infoKey),&arrInfoKey, str);}// запуск удаления приватного ключаelseif(LOWORD(wParam)== id_delPrivateKey ){if( currSel ==-1){MessageBox(hMainWnd,"Select one public key.",TEXT(""),0);break;}sprintf(str,"gpg.exe --delete-secret-key %s", arrInfoKey[currSel].fpr);system(str);
updKeyList(&numPubKeys,sizeof(struct infoKey),&arrInfoKey, str);}// запуск удаления публичного ключаelseif(LOWORD(wParam)== id_delPublicKey ){if( currSel ==-1){MessageBox(hMainWnd,"Select one public key.",TEXT(""),0);break;}sprintf(str,"gpg.exe --delete-key %s", arrInfoKey[currSel].fpr);system(str);
updKeyList(&numPubKeys,sizeof(struct infoKey),&arrInfoKey, str);}// изменение выбранного элемента в comboboxelseif(LOWORD(wParam)== id_ComboBox_pub ){if(HIWORD(wParam)==CBN_SELCHANGE){if(( currSel =SendMessage(hWnd_ComboBox_pub,CB_GETCURSEL,0,0))== CB_ERR )
currSel =-1;}}break;case WM_SIZE:// Sent to a window after its size has changed
sx =LOWORD(lParam);//ширина клиентской области
sy =HIWORD(lParam);//высота//sprintf(sizeCL, "size. w = %d, h = %d", sx, sy);//MessageBox(hWnd, sizeCL, TEXT(""), 0);SetWindowPos(editctl,(HWND)NULL,10,70, sx-20, sy/2-80,0);SetWindowPos(editctl2,(HWND)NULL,10, sy/2+40, sx-20, sy/2-50,0);SetWindowPos(hWnd_buttonDecrypt,(HWND)NULL,10, sy/2,130,30,0);SetWindowPos(hWnd_Text1,(HWND)NULL,150, sy/2,60,30,0);break;/*case WM_PAINT: // если нужно нарисовать, то: hDC = BeginPaint(hWnd, &ps); // инициализируем контекст устройства GetClientRect(hWnd, &rect); // получаем ширину и высоту области для рисования SetTextColor(hDC, colorText); // устанавливаем цвет контекстного устройства DrawText(hDC, mainMessage, -1, &rect, DT_SINGLELINE|DT_CENTER|DT_VCENTER); // рисуем текст EndPaint(hWnd, &ps); // заканчиваем рисовать break; */case WM_DESTROY:// если окно закрылось, то:
SetWindowLongPtr(editctl,(int)-4,(LONG_PTR)wpOrigEditProc);// GWL_WNDPROC = -4
SetWindowLongPtr(editctl2,(int)-4,(LONG_PTR)wpOrigEdit2Proc);// GWL_WNDPROC = -4if( arrInfoKey !=NULL)free(arrInfoKey);if( passw ==NULL)free(passw);PostQuitMessage(0);// отправляем WinMain() сообщение WM_QUITbreak;default:returnDefWindowProc(hWnd, uMsg, wParam, lParam);// если закрыли окно}return0;// возвращаем значение}// Edit's WinProcLRESULT APIENTRY EditSubclassProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam){switch(uMsg){case WM_CREATE:break;///////////////////////////////////////////case WM_KEYDOWN:if(GetKeyState(VK_CONTROL)&0x8000&& wParam =='A'){SendMessage(hwnd,EM_SETSEL,0,-1);//MessageBox(hwnd, "test 11111.", TEXT(""), 0);}break;case WM_COMMAND:break;}returnCallWindowProc(wpOrigEditProc, hwnd, uMsg, wParam, lParam);}// Edit2's WinProcLRESULT APIENTRY Edit2SubclassProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam){switch(uMsg){case WM_CREATE:break;///////////////////////////////////////////case WM_KEYDOWN:if(GetKeyState(VK_CONTROL)&0x8000&& wParam =='A')SendMessage(hwnd,EM_SETSEL,0,-1);//MessageBox(hwnd, "test 11111.", TEXT(""), 0);break;case WM_COMMAND:break;}returnCallWindowProc(wpOrigEdit2Proc, hwnd, uMsg, wParam, lParam);}int encodeToBase64(char* inF,char* outB64F){staticchar b64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";int i;char line[100];char lineRes[133];char* p;FILE* in =fopen(inF,"rb");int numread;if(in ==NULL)return1;FILE* out =fopen(outB64F,"wb");if(out ==NULL)return2;while((numread =fread(line,sizeof(char),99, in))!=0){
p = lineRes;for(i =0; numread > i; i = i +3){if((numread - i)>2){*p++= b64[(line[i]>>2)&0x3F];// & 0x3F*p++= b64[((line[i]&0x3)<<4)|((int)(line[i +1]&0xF0)>>4)];*p++= b64[((line[i +1]&0xF)<<2)|((int)(line[i +2]&0xC0)>>6)];// & 0xC0*p++= b64[line[i +2]&0x3F];}elseif((numread - i)==2){*p++= b64[(line[i]>>2)&0x3F];// & 0x3F*p++= b64[((line[i]&0x3)<<4)|((int)(line[i +1]&0xF0)>>4)];*p++= b64[(line[i +1]&0xF)<<2];// на 2 тоже сдвигаем(там всегда 00)*p++='=';}else{// = 1*p++= b64[(line[i]>>2)&0x3F];*p++= b64[((line[i]&0x3)<<4)];*p++='=';*p++='=';}}fwrite(lineRes,sizeof(char), p - lineRes, out);}fclose(out);fclose(in);return0;}int decodeBase64(char* inF,char* outT){char b64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";char errS[]=" conversion error!";int i, err =0, end =0;char line[100];char lineRes[75];char* p,* fch;FILE* in =fopen(inF,"rb");if(in ==NULL)return1;FILE* out =fopen(outT,"wb");int numread;if(out ==NULL)return2;while((numread =fread(line,sizeof(char),100, in))!=0){
p = lineRes;for(i =0; numread > i; i = i +4){
fch =strchr(b64,(int)line[i]);if(fch !=NULL){*p =(char)(fch - b64)<<2;}else{
err =1;break;}
fch =strchr(b64,(int)line[i +1]);if(fch !=NULL){*p =*p |(((char)(fch - b64)&0x30)>>4);*(p+1)=((char)(fch - b64)<<4);}else{
err =1;break;}
fch =strchr(b64,(int)line[i +2]);if(fch !=NULL){*(p +1)=*(p +1)|((char)(fch - b64)>>2);*(p +2)=(char)(fch - b64)<<6;}else{if((char)line[i +2]=='='){
p = p +1;
end =1;break;}else{
err =1;break;}}
fch =strchr(b64,(int)line[i +3]);if(fch !=NULL){*(p +2)=*(p +2)|(char)(fch - b64);}else{if((char)line[i +3]=='='){
p = p +2;
end =1;break;}else{
err =1;break;}}
p = p +3;}if(err ==0){fwrite(lineRes,sizeof(char), p - lineRes, out);if(end ==1)break;}else{if((int)(p - lineRes)>0)fwrite(lineRes,sizeof(char), p - lineRes, out);fwrite(errS,sizeof(char),18, out);break;}}fclose(in);fclose(out);return0;}int copyTextToFile(HWND id,char* fname){int lenFact, len1;FILE* out;char* txt;
len1 =GetWindowTextLength(id);//символов без \0if(len1 ==0){MessageBox(hMainWnd,"Edit control is empty.",TEXT(""),0);return1;}
txt =malloc((len1 +1)*sizeof(char));GetWindowText(id, txt, len1+1);//sizeof(txt)); // +1 - для \0//MessageBox(hMainWnd, txt, TEXT(""), 0); //показывает сообщение, копируемое в файл// GetWindowText даёт текст с \13\10 . если открыть файл в текстовом режиме ("w"), то // при записи символ '\10' заменяется функциями Си на \13\10 и превращается в \13\13\10
out =fopen(fname,"wb");if(out ==NULL){MessageBox(hMainWnd,"Error opening file.",TEXT(""),0);return1;}
lenFact =fwrite(txt, len1,1, out);// вывод исходного сообщения в файлfclose(out);free(txt);
txt =NULL;if(lenFact ==0){MessageBox(hMainWnd,"Data were not wrote to file.",TEXT(""),0);//break;return1;}return0;}int copyTextToControl(HWND id,char* fname ){FILE* out;long sz;char* txt;int lenFact;// размер получившегося файла
out =fopen(fname,"rb");if(out ==NULL){MessageBox(hMainWnd,"Error opening file.",TEXT(""),0);return1;}fseek(out,0L, SEEK_END);
sz =ftell(out);rewind(out);if(-1L== sz ){MessageBox(hMainWnd,"Error getting size of file.",TEXT(""),0);fclose(out);return2;}//чтение данных
txt =malloc((sz +1)*sizeof(char));if((lenFact =fread(txt, sz,1, out))==0){MessageBox(hMainWnd,"Error reading file.",TEXT(""),0);fclose(out);free(txt);return3;}
txt[sz]='\0';fclose(out);SetWindowText(id, txt);// typedef const char* LPCSTR;free(txt);
txt =NULL;return0;}int deleteFiles(char* f1,char* f2,char* f3){int fdel =0;if( f1 !=NULL)if(remove(f1)!=0){
fdel =1;}if( f2 !=NULL)if(remove(f2)!=0){
fdel =1;}if( f3 !=NULL)if(remove(f3)!=0){
fdel =1;}return fdel;}// --keyid-format long не обязательно// --list-public-keys - выводит информацию в удобном для человека виде// --with-colons --list-public-keys - выводит ту же инфу для парсинга. // но есть отличие здесь выводится отпечаток ещё и для субключа// ФОРМАТ вывода --with-colons ТУТ https://github.com/gpg/gnupg/blob/master/doc/DETAILS// https://wiki.debian.org/Subkeys//заполнение массива информацией о ключах по списку публичных ключей. Дополнение по списку приватных. Синонимint fillKeyList(int* numPubKeys,size_t sz,struct infoKey ** arrInfoKey){FILE* fKeys;char line[250];int pubSec =0, currN =-1, x =0;//освобождение памяти и очистка списка перед заполнениемif(*arrInfoKey !=NULL){free(*arrInfoKey);*arrInfoKey =NULL;SendMessage(hWnd_ComboBox_pub,CB_RESETCONTENT,0,0);}system("gpg.exe --with-colons --list-public-keys > listPub.txt");//заполнение массива информацией о ключах по списку публичных ключей.
fKeys =fopen("listPub.txt","r");if(fKeys ==NULL){MessageBox(hMainWnd,"Error opening file listPub.txt.",TEXT(""),0);return1;}*numPubKeys =0;while(fgets(line,250, fKeys)!=NULL){if(strncmp(line,"pub",3)!=0)continue;(*numPubKeys)++;}if(*numPubKeys ==0){fclose(fKeys);return0;}rewind(fKeys);*arrInfoKey =(struct infoKey *)malloc(*numPubKeys * sz );if( arrInfoKey ==NULL){fclose(fKeys);MessageBox(hMainWnd,"err of malloc()",TEXT(""),0);return1;}while(fgets(line,250, fKeys)!=NULL){if(pubSec ==0){if(strncmp(line,"pub",3)==0){
pubSec =1;
currN++;}}elseif( pubSec ==1){if(strncmp(line,"fpr",3)==0){
pubSec =2;
getField(10, line,(*arrInfoKey)[currN].fpr , fprML);(*arrInfoKey)[currN].withoutPrivate =1;(*arrInfoKey)[currN].synonym[0]='\0';}}else{// pubSec == 2if(strncmp(line,"uid",3)==0){
pubSec =0;
getField(10, line,(*arrInfoKey)[currN].uid , uidML);}}}fclose(fKeys);system("gpg.exe --with-colons --list-secret-keys > listPriv.txt");//Дополнение по списку приватных. Если в списке приватных ключей нет отпечатка публичного ключа, то//считаю, что этот ключ был импортирован. Помечаю его символом +.
fKeys =fopen("listPriv.txt","r");if(fKeys ==NULL){MessageBox(hMainWnd,"Error opening file listPriv.txt.",TEXT(""),0);return1;}
pubSec =0;while(fgets(line,250, fKeys)!=NULL){if(pubSec ==0){if(strncmp(line,"sec",3)==0)
pubSec =1;}else{// if ( pubSec == 1)if(strncmp(line,"fpr",3)==0){
pubSec =0;for( currN =0; currN <*numPubKeys; currN++){if((*arrInfoKey)[currN].withoutPrivate ==0)continue;if(strstr(line,(*arrInfoKey)[currN].fpr)!=NULL){(*arrInfoKey)[currN].withoutPrivate =0;
x++;break;}}if( x ==*numPubKeys )//когда все отпечатки найдены, а приватных ключей ещё многоbreak;}}}fclose(fKeys);if( deleteFiles("listPub.txt","listPriv.txt",NULL)==1)MessageBox(hMainWnd,"Error removing of one or some files.",TEXT(""),0);//добавляем синонимы
fKeys =fopen("synonyms.txt","r");if(fKeys ==NULL)return0;while(fgets(line,250, fKeys)!=NULL){
currN =0;for(x =0; x <250; x++){if( line[x]=='\n'){
line[x]='\0';break;}if( currN <=2&& line[x]=='>'){//ищу конец id
currN++;
pubSec = x;}}if(currN <2)continue;
pubSec +=2;// начало fingerprintfor( currN =0; currN <*numPubKeys; currN++){if(strncmp(line + pubSec,(*arrInfoKey)[currN].fpr, fprML)==0){strcpy((*arrInfoKey)[currN].synonym, line + pubSec + fprML +1);break;}}}fclose(fKeys);return0;}void getField(int nfield,constchar* source,char* target ,int maxLen){int i, cn =0, start =-1, end =0, lfact;for( i =0; i <strlen(source); i++){if( source[i]==':')
cn++;if( start ==-1&& cn == nfield-1)//fix me
start = i+1;if( cn == nfield ){
end = i;break;}}if(start ==-1)// nfield == 1
start =0;
lfact = end - start;if( lfact > maxLen ){MessageBox(hMainWnd,"The actual length of the key field is greater than maxLen.",TEXT("The string was cut off."),0);
lfact = maxLen;}strncpy(target, source + start, lfact);
target[lfact]='\0';}void updKeyList(int* numPubKeys,size_t sz,struct infoKey ** arrInfoKey,char* str){int i;
fillKeyList(numPubKeys, sz, arrInfoKey);// заполнение массива
currSel =-1;if(*arrInfoKey !=NULL){for(i =0; i <*numPubKeys; i++){//заполнение combobox из этого массива//sprintf(str, "%c %s - %s", ( (*arrInfoKey)[i].withoutPrivate ) ? '-' : '+',// (*arrInfoKey)[i].uid, (*arrInfoKey)[i].fpr );sprintf(str,"%c%s",((*arrInfoKey)[i].withoutPrivate )?'-':'+',((*arrInfoKey)[i].synonym[0]=='\0')?(*arrInfoKey)[i].uid :(*arrInfoKey)[i].synonym );//if ( SendMessage(hWnd_ComboBox_pub, CB_ADDSTRING, 0, (int ) str ) == CB_ERR ) // 32-bitif(SendMessage(hWnd_ComboBox_pub,CB_ADDSTRING,0,(longlong) str )== CB_ERR )//64-bitMessageBox(hMainWnd,"err CB_ERR CB_ADDSTRING",TEXT(""),0);}if(*numPubKeys ==1)
currSel =SendMessage(hWnd_ComboBox_pub,CB_SETCURSEL,0,0);}else{MessageBox(hMainWnd,"There is not available keys.",TEXT(""),0);}}void loadPassPhrase(char** passw){FILE* fp;char line[151];int i =0;
fp =fopen("passphrase.txt","r");if(fp ==NULL){return;}if(fgets(line,151, fp)==NULL){fclose(fp);return;}//delete \n if it is in the line.while( line[i]!='\0'){if( line[i]=='\n'){
line[i]='\0';// optionallybreak;}
i++;}*passw =(char*)malloc( i +1);strncpy(*passw, line, i);(*passw)[i]='\0';fclose(fp);SetWindowText(hWnd_Text1,"********");}// материалы// https://docs.microsoft.com/en-us/windows/win32/controls/edit-control-styles// Window Class Styles https://docs.microsoft.com/en-us/windows/win32/winmsg/window-class-styles// Window Styles https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles// EM_SETSEL message https://docs.microsoft.com/en-us/windows/win32/controls/em-setsel// вариант https://stackoverflow.com/questions/10127054/select-all-text-in-edit-contol-by-clicking-ctrla// https://docs.microsoft.com/ru-ru/windows/win32/winmsg/using-window-procedures// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlonga// https://unix.stackexchange.com/questions/60213/gpg-asks-for-password-even-with-passphrase// https://www.gnupg.org/gph/en/manual/x56.html// http://www.cyberforum.ru/win-api/thread630959.html - создание меню// http://www.cyberforum.ru/win-api/thread1844562.html - подключение меню из ресурсов// https://habr.com/ru/post/358182/
Комментарии