Skip to content

Instantly share code, notes, and snippets.

@nosalvage
Created December 11, 2018 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nosalvage/8393118981fa9a149f49c75be639ce62 to your computer and use it in GitHub Desktop.
Save nosalvage/8393118981fa9a149f49c75be639ce62 to your computer and use it in GitHub Desktop.
/*************************************************************************
* Rutoken *
* Copyright (c) 2003-2018, CJSC Aktiv-Soft. All rights reserved. *
* Подробная информация: http://www.rutoken.ru *
*------------------------------------------------------------------------*
* Пример работы Рутокен с криптопровайдером КриптоПро CSP *
* с использованием интерфейса CryptoAPI на языке C *
*------------------------------------------------------------------------*
* Создание ключевого контейнера КриптоПро CSP на носителе Рутокен: *
* - проверка наличия криптопровайдера КриптоПро CSP в системе; *
* - инициализация криптопровайдера; *
* - перебор ключевых носителей; *
* - создание ключевого контейнера на Рутокен; *
* - генерация ключевой пары ГОСТ Р 34.10-2001 для подписи в контейнер *
* на Рутокен; *
* - генерация ключевой пары ГОСТ Р 34.10-2001 для обмена ключами *
* в контейнер на Рутокен; *
* - создание самоподписанного сертификата для ключевой пары для подписи;*
* - импорт созданного сертификата в контейнер на Рутокен; *
* - освобождение памяти, дескрипторов и контекстов. *
*------------------------------------------------------------------------*
* Пример является самодостаточным *
*************************************************************************/
#include "Includes/Common.h"
#import "RutokenManager.h"
#import <React/RCTLog.h>
/* Шаблон расширений сертификата */
CERT_EXTENSION rgExtension[] =
{
{szOID_KEY_USAGE, FALSE, {0, NULL} },
{szOID_ENHANCED_KEY_USAGE, FALSE, {0, NULL} },
};
/* Возможные значения szMedia структуры CRYPT_ENUMREADER_INFO_MEDIA, если не удалось получить уникальное имя устройства */
LPSTR possibleMediaType[] = { "NO_MEDIA", "INVALID_MEDIA", "IS_FKC", "NO_FKC", "NO_UNIQUE" };
@implementation RutokenManager
RCT_EXPORT_MODULE();
NSString* connectRutoken(NSString *hash){
LPWSTR szProvNameW = CRYPTOPRO_2001_PROV_A; // Имя криптопровайдера, заменить на CRYPTOPRO_FKN_2001_PROV_W для ФКН
LPWSTR fqContNameW = NULL; // Полное имя ключевого контейнера, с указанием устройства хранения
DWORD fqContNameLen; // Размер полного имени ключевого контейнера в байтах
CRYPT_ENUMREADER_INFO_MEDIA* readerInfo = NULL; // Указатель на структуру, содержащую информацию о ключевом носителе
LPWSTR readerNameW = NULL; // Имя устройства
LPSTR readerName; // Имя устройства в формате ANSI
LPSTR media; // Статус устройства
LPSTR mediaSubString = "rutoken"; // Подстрока, содержащаяся в статусе всех устройств Рутокен
DWORD dwProvType = PROV_GOST_2001_DH; // Тип криптопровайдера
HCRYPTPROV hProv = 0; // Дескриптор криптопровайдера
HCRYPTKEY hKey = 0; // Дескриптор ключа
PCCERT_CONTEXT pCertContext = NULL; // Указатель на контекст сертификата
CERT_NAME_BLOB SubjectIssuerBlob = { 0 }; // Структура для информации о субъекте сертификата
CRYPT_KEY_PROV_INFO KeyProvInfo = { 0 }; // Структура для информации о криптопровайдере
CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm = { 0 }; // Структура для информации об алгоритме подписи сертификата
CERT_EXTENSIONS Extensions = { 0 }; // Структура для информации о расширениях сертификата
CERT_ENHKEY_USAGE ExtUsage = { 0 }; // Структура для информации о расширенном назначении ключа
CRYPT_BIT_BLOB KeyUsage = { 0 }; // Структура для информации о назначении ключа
DWORD dwSize = 0; // Вспомогательная переменная для хранения длины буфера
DWORD keyTypes[] = { AT_KEYEXCHANGE, AT_SIGNATURE }; // Массив типов создаваемых ключей
DWORD keys = GetArraySize(keyTypes); // Размер массива типов создаваемых ключей
DWORD i = 0; // Вспомогательная переменная-счетчик
CSP_BOOL error = FALSE; // Переменная для сообщения об ошибке
DWORD searchFlags = CRYPT_FIRST | CRYPT_MEDIA; // Флаги для перебора ключевых носителей
for (;; ) {
/**********************************************************************
* Шаг 1. Проверка наличия выбранного криптопровайдера в системе *
**********************************************************************/
wprintf(L"Checking whether %s provider exists", szProvNameW);
if (!CryptAcquireContext(
&hProv, // Указатель на дескриптор криптопровайдера
NULL, // Имя ключевого контейнера
NULL, // Имя криптопровайдера
dwProvType, // Тип криптопровайдера
CRYPT_VERIFYCONTEXT)) { // Флаг операции, не требующей работы с контейнером
if (CSP_GetLastError() == NTE_KEYSET_NOT_DEF) {
wprintf(L" -> FAILED \nProvider has not been installed\n\n");
} else {
wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", CSP_GetLastError());
}
break;
}
wprintf(L" -> OK\n");
/******************************************************************************
* Шаг 2. Поиск ключевых носителей, доступных для использования в КриптоПро CSP*
******************************************************************************/
wprintf(L"Checking Rutoken devices\n");
wprintf(L"Checking size of buffer for device name");
if (!CryptGetProvParam(hProv,
PP_ENUMREADERS,
NULL,
&dwSize,
searchFlags)) {
wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", CSP_GetLastError());
break;
}
/*************************************************************************
* Выделение памяти для буфера *
*************************************************************************/
readerInfo = (CRYPT_ENUMREADER_INFO_MEDIA*)malloc(dwSize);
readerNameW = (LPWSTR)malloc(dwSize * sizeof(WCHAR));
if (!readerInfo || !readerNameW) {
wprintf(L" -> FAILED");
break;
}
memset(readerNameW, 0, dwSize * sizeof(WCHAR));
wprintf(L"WE ARE AFTER -> OK\n");
/*************************************************************************
* Перебор ключевых носителей *
*************************************************************************/
for (;; searchFlags = CRYPT_MEDIA) {
if (!CryptGetProvParam(hProv,
PP_ENUMREADERS,
(PBYTE)readerInfo,
&dwSize,
searchFlags)) {
wprintf(L"NOT FOUND\n");
if (CSP_GetLastError() == ERROR_NO_MORE_ITEMS) {
wprintf(L"Devices are not found\n");
} else {
wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", CSP_GetLastError());
}
error = TRUE;
break;
}
/***********************************************************************
* Получение имени ридера в формате ANSI строки. *
* Определение возможности использования носителя *
***********************************************************************/
readerName = readerInfo->szNickName + strlen(readerInfo->szNickName) + 1;
media = readerName + strlen(readerName) + 1;
error = FALSE;
for (i = 0; i < GetArraySize(possibleMediaType); ++i) {
if ((strlen(media) == strlen(possibleMediaType[i]) && !memcmp(media, possibleMediaType[i], strlen(media)))) {
error = TRUE;
break;
}
}
/***********************************************************************
* Проверка того, что совместимый с КриптоПро CSP носитель является *
* устройством Рутокен *
***********************************************************************/
if (error == FALSE) {
if (!strstr(media, mediaSubString)) {
error = TRUE;
}
}
if (error == FALSE) {
wprintf(L"Rutoken found\n");
/***********************************************************************
* Преобразование ANSI строки названия устройства в UTF-16 *
***********************************************************************/
wprintf(L"Mapping a reader name to an UTF-16 string");
if (!MultiByteToWideChar(CP_ACP,
0,
readerName,
-1,
readerNameW,
strlen(readerName) * sizeof(WCHAR))) {
error = TRUE;
wprintf(L" -> FAILED\n");
break;
} else {
wprintf(L" -> OK\n");
wprintf(L"Device: \"%s\"\n", readerNameW);
/***********************************************************************
* Продолжение работы с первым обнаруженным Рутокен *
***********************************************************************/
break;
}
}
}
if (error == TRUE) {
break;
}
/**********************************************************************
* Шаг 3. Создание ключевого контейнера с заданным именем на Рутокен *
**********************************************************************/
wprintf(L"Creating Fully Qualified Container Name");
fqContNameLen = sizeof(WCHAR) * (wcslen(L"\\\\.\\") + wcslen(readerNameW) + wcslen(L"\\") + wcslen(CONT_NAME_2001_W) + 1);
fqContNameW = (LPWSTR)malloc(fqContNameLen);
if (!fqContNameW) {
wprintf(L" -> FAILED\n");
break;
}
memset(fqContNameW, 0, fqContNameLen);
wcscat(fqContNameW, L"\\\\.\\");
wcscat(fqContNameW, readerNameW);
wcscat(fqContNameW, L"\\");
wcscat(fqContNameW, CONT_NAME_2001_W);
wprintf(L" -> OK\n");
wprintf(L"Creating key container with name \"%s\" on device \"%s\"", CONT_NAME_2001_W, readerNameW);
if (!CryptAcquireContextW(
&hProv, // Указатель на дескриптор криптопровайдера
fqContNameW, // Имя ключевого контейнера
szProvNameW, // Имя криптопровайдера
dwProvType, // Тип криптопровайдера
CRYPT_NEWKEYSET)) { // Флаги создания ключевого контейнера
if (CSP_GetLastError() == NTE_EXISTS) {
wprintf(L" -> FAILED\nDevice \"%s\" already has key container with name \"%s\"\n", readerNameW, CONT_NAME_2001_W);
/**********************************************************************
* Шаг 4. Инициализация криптопровайдера для работы с выбранным *
* контейнером *
**********************************************************************/
wprintf(L"Choosing key container with name \"%s\"\n", CONT_NAME_2001_W);
if (!CryptAcquireContextW(
&hProv, // Дескриптор криптопровайдера
fqContNameW, // Имя ключевого контейнера
szProvNameW, // Имя криптопровайдера
dwProvType, // Тип криптопровайдера
0)) {
wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", CSP_GetLastError());
break;
}
wprintf(L" -> OK\n");
} else {
wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", CSP_GetLastError());
break;
}
} else {
wprintf(L" -> OK\n");
wprintf(L"Container with name \"%s\" has been created on device \"%s\".\n", CONT_NAME_2001_W, readerNameW);
}
for (i = 0; i < keys; ++i) {
/**********************************************************************
* Шаг 5. Проверка наличия ключевых пар в контейнере и их генерация *
* в случае отсутствия (КриптоПро CSP не записывает контейнер на токен *
* без закрытого ключа) *
**********************************************************************/
wprintf(L"\nChecking exchange key existence");
if (!CryptGetUserKey(
hProv, // Дескриптор криптопровайдера
keyTypes[i], // Тип ключа
&hKey)) { // Дескриптор ключа
if (CSP_GetLastError() == NTE_NO_KEY) {
wprintf(L" -> OK, key does not exist\n");
wprintf(L" Generating GOST R 34.10-2001 %ls key", keyTypes[i] == AT_KEYEXCHANGE ? L"exchange" : L"signature");
if (!CryptGenKey(
hProv, // Дескриптор криптопровайдера
keyTypes[i], // Флаг назначения ключа
0, // Флаги
&hKey)) { // Дескриптор ключа
wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", CSP_GetLastError());
break;
}
wprintf(L" -> OK\n");
wprintf(L"%s key pair has been created.\n", keyTypes[i] == AT_KEYEXCHANGE ? L"Exchange" : L"Signature");
} else {
wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", CSP_GetLastError());
break;
}
} else {
wprintf(L" -> OK, key exists\n");
}
/**********************************************************************
* Шаг 6. Проверка наличия сертификата в контейнере для ключевой пары, *
* предназначенной для подписи *
**********************************************************************/
wprintf(L"\nChecking certificate existence for last key pair");
if (!CryptGetKeyParam(
hKey, // Дескриптор ключа
KP_CERTIFICATE, // Признак сертификата
NULL, // Указатель на буфер для получения сертификата
&dwSize, // Размер буфера
0)) {
if (CSP_GetLastError() == SCARD_E_NO_SUCH_CERTIFICATE) {
wprintf(L" -> OK, certificate does not exist\n");
} else {
wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", CSP_GetLastError());
error = TRUE;
goto destroy_key;
}
} else {
wprintf(L" -> OK, certificate exists\n");
}
/**********************************************************************
* Шаг 7. Создание самоподписанного сертификата для ключевой пары, *
* предназначенной для подписи *
**********************************************************************/
if (CSP_GetLastError() == SCARD_E_NO_SUCH_CERTIFICATE) {
wprintf(L"\nCreating certificate\n");
/**********************************************************************
* Шаг 7.1 Конвертирование имени субъекта сертификата *
**********************************************************************/
wprintf(L" Converting certificate subject name");
/**********************************************************************
* Получение размера буфера с закодированной структурой *
**********************************************************************/
if (!CertStrToNameW(
ENC_TYPE, // Тип кодирования сертификата
SUBJECT_NAME_2001_W, // Указатель на кодируемую строку X.500
CERT_X500_NAME_STR, // Тип кодируемой строки
NULL,
NULL, // Указатель на буфер для закодированной структуры
&SubjectIssuerBlob.cbData, // Указатель на размер буфера
NULL)) { // Указатель на расширенную информацию об ошибке
wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", CSP_GetLastError());
error = TRUE;
goto destroy_key;
}
/**********************************************************************
* Выделение памяти для буфера *
**********************************************************************/
SubjectIssuerBlob.pbData = (BYTE*)malloc(SubjectIssuerBlob.cbData * sizeof(BYTE));
if (!SubjectIssuerBlob.pbData) {
wprintf(L" -> FAILED, out of memory\n\n");
error = TRUE;
goto destroy_key;
}
/**********************************************************************
* Получение указателя на буфер с закодированной структурой *
**********************************************************************/
if (!CertStrToNameW(
ENC_TYPE, // Тип кодирования сертификата
SUBJECT_NAME_2001_W, // Указатель на кодируемую строку X.500
CERT_X500_NAME_STR, // Тип кодируемой строки
NULL,
SubjectIssuerBlob.pbData, // Указатель на буфер для закодированной структуры
&SubjectIssuerBlob.cbData, // Указатель на размер буфера
NULL)) { // Указатель на расширенную информацию об ошибке
wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", CSP_GetLastError());
error = TRUE;
goto free_subject_data;
}
wprintf(L" -> OK\n");
/**********************************************************************
* Шаг 7.2 Заполнение структуры с информацией о ключевом контейнере *
**********************************************************************/
KeyProvInfo.pwszContainerName = fqContNameW; // Имя контейнера
KeyProvInfo.pwszProvName = szProvNameW; // Имя криптопровайдера
KeyProvInfo.dwProvType = dwProvType; // Тип криптопровайдера
KeyProvInfo.dwFlags = 0; // Флаги
KeyProvInfo.cProvParam = 0; // Количество записей в массиве с расширенной информацией
KeyProvInfo.rgProvParam = NULL; // Указатель на массив с расширенной информацией о контейнере
KeyProvInfo.dwKeySpec = keyTypes[i]; // Тип ключа
/**********************************************************************
* Шаг 7.3 Заполнение структуры с информацией об алгоритме подписи *
**********************************************************************/
SignatureAlgorithm.pszObjId = OID_GOST3410_2001;
/**********************************************************************
* Шаг 7.4 Заполнение структуры с информацией о назначении ключа *
**********************************************************************/
wprintf(L" Encoding data about key usage");
KeyUsage.cbData = 1;
KeyUsage.pbData = &KEY_USAGE;
KeyUsage.cUnusedBits = 0;
if (!CryptEncodeObject(
ENC_TYPE, // Тип кодирования
rgExtension[0].pszObjId, // Указатель на OID кодируемой структуры
&KeyUsage, // Указатель на кодируемую структуру
NULL, // Указатель на буфер для закодированной структуры
&rgExtension[0].Value.cbData // Указатель на размер буфера
)) {
wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", CSP_GetLastError());
error = TRUE;
goto free_subject_data;
}
/**********************************************************************
* Выделение памяти для буфера *
**********************************************************************/
rgExtension[0].Value.pbData = (BYTE*)malloc(rgExtension[0].Value.cbData * sizeof(BYTE));
if (!rgExtension[0].Value.pbData) {
wprintf(L" -> FAILED, out of memory\n\n");
error = TRUE;
goto free_subject_data;
}
if (!CryptEncodeObject(
ENC_TYPE, // Тип кодирования
rgExtension[0].pszObjId, // Указатель на OID кодируемой структуры
&KeyUsage, // Указатель на кодируемую структуру
rgExtension[0].Value.pbData, // Указатель на буфер для закодированной структуры
&rgExtension[0].Value.cbData // Указатель на размер буфера
)) {
wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", CSP_GetLastError());
error = TRUE;
goto free_key_usage_data;
}
wprintf(L" -> OK\n");
/**********************************************************************
* Шаг 7.5 Заполнение структуры с информацией о расширенном *
* назначении ключа *
**********************************************************************/
wprintf(L" Encoding data about extended key usage");
ExtUsage.cUsageIdentifier = 1;
ExtUsage.rgpszUsageIdentifier = EXT_KEY_USAGE;
if (!CryptEncodeObject(
ENC_TYPE, // Тип кодирования
rgExtension[1].pszObjId, // Указатель на OID кодируемой структуры
&ExtUsage, // Указатель на кодируемое значение структуры
NULL, // Указатель на буфер для закодированной структуры
&rgExtension[1].Value.cbData // Указатель на размер буфера
)) {
wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", CSP_GetLastError());
error = TRUE;
goto free_key_usage_data;
}
/**********************************************************************
* Выделение памяти для буфера *
**********************************************************************/
rgExtension[1].Value.pbData = (BYTE*)malloc(rgExtension[1].Value.cbData * sizeof(BYTE));
if (!rgExtension[1].Value.pbData) {
wprintf(L" -> FAILED, out of memory\n\n");
error = TRUE;
goto free_key_usage_data;
}
if (!CryptEncodeObject(
ENC_TYPE, // Тип кодирования
rgExtension[1].pszObjId, // Указатель на OID кодируемой структуры
&ExtUsage, // Указатель на кодируемую структуру
rgExtension[1].Value.pbData, // Указатель на буфер для закодированной структуры
&rgExtension[1].Value.cbData // Указатель на размер буфера
)) {
wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", CSP_GetLastError());
error = TRUE;
goto free_enhanced_key_usage_data;
}
wprintf(L" -> OK\n");
/**********************************************************************
* Шаг 7.6 Заполнение структуры с расширениями сертификата *
**********************************************************************/
Extensions.cExtension = GetArraySize(rgExtension);
Extensions.rgExtension = rgExtension;
/**********************************************************************
* Шаг 7.7 Создание самоподписанного сертификата *
**********************************************************************/
/**********************************************************************
* Шаг 8. Импорт сертификата на токен *
**********************************************************************/
wprintf(L"Importing certificate to container on token");
if (!CryptSetKeyParam(
hKey, // Дескриптор ключа, соответствующий сертификату
KP_CERTIFICATE, // Признак сертификата
pCertContext->pbCertEncoded, // Указатель на буфер с сертификатом
0)) { // Флаги
wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", CSP_GetLastError());
error = TRUE;
goto free_cert_context;
}
wprintf(L" -> OK\n");
wprintf(L"\nCertificate has been created and imported.\n");
}
wprintf(L"\nContainer with name \"%s\" on device \"%s\" has all required objects.\n\n", CONT_NAME_2001_W, readerNameW);
/**********************************************************************
* Шаг 9. Освобождение памяти *
**********************************************************************/
/**********************************************************************
* Шаг 9.1 Освобождение контекста сертификата *
**********************************************************************/
free_cert_context:
if (pCertContext) {
CertFreeCertificateContext(pCertContext);
}
/**********************************************************************
* Шаг 9.2 Освобождение памяти *
**********************************************************************/
free_enhanced_key_usage_data:
free(rgExtension[1].Value.pbData);
free_key_usage_data:
free(rgExtension[0].Value.pbData);
free_subject_data:
free(SubjectIssuerBlob.pbData);
/**********************************************************************
* Шаг 9.3 Освобождение дескриптора ключа *
**********************************************************************/
destroy_key:
if (hKey) {
CryptDestroyKey(hKey);
}
if (error) {
break;
}
}
break;
}
/**********************************************************************
* Шаг 9.4 Освобождение памяти *
**********************************************************************/
if (fqContNameW) {
free(fqContNameW);
}
if (readerNameW) {
free(readerNameW);
}
if (readerInfo) {
free(readerInfo);
}
/**********************************************************************
* Шаг 9.5 Освобождение контекста криптопровайдера *
**********************************************************************/
if (hProv) {
CryptReleaseContext(hProv, 0);
}
if (CSP_GetLastError() == ERROR_SUCCESS || CSP_GetLastError() == NTE_EXISTS) {
wprintf(L"Test has been completed successfully.");
} else {
wprintf(L"Test has failed. Error number: 0x%0.8x.", CSP_GetLastError());
}
return 0;
}
RCT_EXPORT_METHOD(connectRutoken:(NSString *)hash:(RCTResponseSenderBlock)callback)
{
NSString *testString = connectRutoken(hash);
//NSLog(@"Importing CERT:%@", testString);
NSLog(@"Received signed hash:, %@", testString);
callback(@[@"Returned from connect RUTOKEN"]);
}
RCT_EXPORT_METHOD(getCertAlgorithm:(RCTResponseSenderBlock)callback)
{
//NSString *testString = getAlgorithmFromCert();
//NSLog(@"Importing CERT:%@", testString);
//NSLog(@"Received hash:, %@!", hash);
//callback(@[testString]);
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment