Created
December 11, 2018 14:16
-
-
Save nosalvage/8393118981fa9a149f49c75be639ce62 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/************************************************************************* | |
* 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