Дерево страниц
Перейти к концу метаданных
Переход к началу метаданных


Введение

Функциональность устройств Рутокен охватывает широкий спектр задач обеспечения информационной безопасности. Устройства Рутокен могут быть применены для строгой двухфакторной аутентификации, защиты электронной переписки, установления защищенных соединений, безопасного проведения финансовых транзакций и криптографической защиты информации.

Рутокен S

Рутокен S – это носитель ключевой информации и устройство для авторизации в компьютерных системах и защиты персональных данных с реализацией отечественного стандарта шифрования "на борту".

Через интерфейс PKCS#11 доступны следующие возможности Рутокен S:

  • создание, запись, чтение, изменение, удаление двоичных файлов,
  • генерация и импорт ключей шифрования ГОСТ 28147-89, 
  • шифрование данных по алгоритму ГОСТ 28147-89 в режимах простой замены, гаммирования и гаммирования с обратной связью, 
  • вычисление имитовставки по алгоритму ГОСТ 28147-89 длиной 32 бит, 
  • генерация последовательности случайных чисел длиной 256 бит.

Рутокен Lite

Рутокен Lite – это ключевой носитель для безопасного хранения ключей шифрования и электронной подписи, паролей и других данных во встроенной защищенной памяти устройства. 

Через интерфейс PKCS#11 доступны следующие возможности Рутокен Lite:

  • создание, запись, чтение, изменение, удаление двоичных файлов.

Рутокен ЭЦП

Рутокен ЭЦП – электронный идентификатор с аппаратной реализацией отечественных и зарубежных стандартов электронной подписи, шифрования и хеширования.

Через интерфейс PKCS#11 доступны следующие возможности Рутокен ЭЦП:

  • алгоритмы ГОСТ Р 34.10-2012 и ГОСТ Р 34.10-2001: генерация ключевых пар с проверкой качества, импорт ключевых пар, формирование и проверка электронной подписи,
  • алгоритмы ГОСТ Р 34.11-2012 и ГОСТ Р 34.11-94: вычисление значения хеш-функции данных, в том числе с возможностью последующего формирования электронной подписи внутри устройства,
  • алгоритм ГОСТ 28147-89: генерация и импорт ключей шифрования, шифрование данных в режимах простой замены, гаммирования и гаммирования с обратной связью, вычисление и проверка криптографической контрольной суммы данных (имитовставки).
  • выработка сессионных ключей (ключей парной связи): по схемам VKO GOST R 34.10-2012 (RFC 7836), VKO GOST R 34.10-2001 (RFC 4357), расшифрование по схеме EC El-Gamal.
  • алгоритм RSA: поддержка ключей размером до 2048 бит, генерация ключевых пар с настраиваемой проверкой качества, импорт ключевых пар, формирование электронной подписи.
  • генерация последовательности случайных чисел требуемой длины.

Рутокен PINPad

Рутокен PINPad представляет собой решение класса TrustScreen, которое совмещает в себе все возможности электронного идентификатора Рутокен ЭЦП с экраном Touchscreen, позволяя тем самым вывести на новый уровень безопасность типичные для PKI-инфраструктуры пользовательские задачи:

  • визуализацию документа перед созданием электронной подписи с возможностью дополнительного предъявления PIN-кода перед созданием подписи;
  • визуализацию запросов на сертификат в формате PKCS#10 с возможностью дополнительного предъявления PIN-кода перед созданием подписи;
  • pащиты от подмены хеш-функции при подписи PKCS#7 с атрибутами;
  • журналирования операций.

Кроме того, Рутокен PINPad поддерживает отечественные и зарубежные криптографические алгоритмы в полном объеме:

  • алгоритмы ГОСТ Р 34.10-2012 и ГОСТ Р 34.10-2001: генерация ключевых пар с проверкой качества, импорт ключевых пар, формирование и проверка электронной подписи;
  • алгоритмы ГОСТ Р 34.11-2012 и ГОСТ Р 34.11-94: вычисление значения хеш-функции данных, в том числе с возможностью последующего формирования электронной подписи внутри устройства;
  • алгоритм ГОСТ 28147-89: генерация и импорт ключей шифрования, шифрование данных в режимах простой замены, гаммирования и гаммирования с обратной связью, вычисление и проверка криптографической контрольной суммы данных (имитовставки).
  • выработка сессионных ключей (ключей парной связи): по  по схемам VKO GOST R 34.10-2012 (RFC 7836), VKO GOST R 34.10-2001 (RFC 4357), расшифрование по схеме EC El-Gamal.
  • алгоритм RSA: поддержка ключей размером до 2048 бит, генерация ключевых пар с настраиваемой проверкой качества, импорт ключевых пар, формирование электронной подписи.
  • генерация последовательности случайных чисел требуемой длины.

Рутокен PINPad ведет защищенный журнал операций, который может быть подписан алгоритмом ГОСТ на специальной ключевой паре, не удаляемой при форматировании устройства и однозначно идентифицирующей используемое устройство.

Начало работы

Подключение библиотеки

Для работы с устройствами Рутокен через программный интерфейс PKCS#11 приложение должно предварительно загрузить библиотеку, содержащую реализацию функций и механизмов стандарта PKCS#11.

Рутокен SDK предоставляет две библиотеки rtPKCS11 и rtPKCS11ECP, подробнее об особенностях выбора и использования которых можно ознакомиться в разделе Использование библиотек rtPKCS11 и rtPKCS11ECP. Основная разница заключается в том, что российские алгоритмы доступны в библиотеке rtPKCS11ECP, а зарубежные – в rtPKCS11.

Кроме функций стандартного интерфейса PKCS#11 библиотеки экспортируют функции расширения, которые могут быть удобны при использовании специфической функциональности устройств Рутокен.

После загрузки библиотеки нужно получить адрес экспортируемой библиотекой функции C_GetFunctionList() и вызвать ее для получения списка функций PKCS#11. Теперь все готово для работы с библиотекой.

Для работы с функциями расширения необходимо получить адрес функции CK_C_EX_GetFunctionListExtended() и вызвать ее для получения списка функций расширения PKCS#11.

После работы с библиотекой ее нужно выгрузить из памяти.

Загрузка библиотеки и списка функций PKCS#11
/* Имя библиотеки PKCS#11 */
#ifdef _WIN32
/* Библиотека для Рутокен S, Рутокен Lite и Рутокен ЭЦП, поддерживает только алгоритмы RSA */
	#define PKCS11_LIBRARY_NAME         "rtPKCS11.dll" 
/* Библиотека для Рутокен Lite, Рутокен ЭЦП, Рутокен PINPad, поддерживает алгоритмы ГОСТ и RSA */
	#define PKCS11ECP_LIBRARY_NAME      "rtPKCS11ECP.dll"
#endif 
#ifdef __unix__
/* Библиотека для Рутокен Lite, Рутокен ЭЦП, Рутокен PINPad, поддерживает алгоритмы ГОСТ и RSA */
	#define PKCS11_LIBRARY_NAME         "librtpkcs11ecp.so"
	#define PKCS11ECP_LIBRARY_NAME      "librtpkcs11ecp.so"
#endif 	
#ifdef __APPLE__
/* Библиотека для Рутокен Lite, Рутокен ЭЦП, Рутокен PINPad, поддерживает алгоритмы ГОСТ и RSA */
	#define PKCS11_LIBRARY_NAME         "librtpkcs11ecp.dylib"
	#define PKCS11ECP_LIBRARY_NAME      "librtpkcs11ecp.dylib"
#endif 	

HMODULE hModule = NULL_PTR;                                     // Хэндл загруженной библиотеки PKCS#11
CK_FUNCTION_LIST_PTR pFunctionList = NULL_PTR;                  // Указатель на список функций PKCS#11, хранящийся в структуре CK_FUNCTION_LIST
CK_C_GetFunctionList pfGetFunctionList = NULL_PTR;              // Указатель на функцию C_GetFunctionList
CK_RV rv = CKR_OK;                                              // Вспомогательная переменная для хранения кода возврата
 
while (TRUE)
{
	/* Загрузить библиотеку */
	printf("Loading library %s", PKCS11ECP_LIBRARY_NAME);
	hModule = LoadLibrary(PKCS11ECP_LIBRARY_NAME);
	if (hModule == NULL_PTR)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Получить адрес функции запроса структуры с указателями на функции */	
	printf("Getting GetFunctionList function");
	pfGetFunctionList = (CK_C_GetFunctionList)GetProcAddress(hModule, "C_GetFunctionList");
	if (pfGetFunctionList == NULL_PTR)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Получить структуру с указателями на функции */
	printf("Getting function list");
	rv = pfGetFunctionList(&pFunctionList);
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	...
	break;
}
 
/* Выгрузить библиотеку из памяти */
if (hModule)
{
	printf("Unloading library");
	if (FreeLibrary(hModule) != TRUE)
		printf(" -> Failed\n");
	else
		printf(" -> OK\n");
	hModule = NULL_PTR;
}

Инициализация и деинициализация библиотеки

После загрузки библиотеки ее нужно инициализировать вызовом функции C_Initialize(). Параметр NULL при вызове данной функции означает, что функции библиотеки не будут вызываться из нескольких потоков, в противном случае в параметре должен быть передан указатель на структуру типа CK_INITIALIZE_ARGS.

Для завершения работы с библиотекой ее нужно деинициализировать вызовом функции C_Finalize().

Инициализация библиотеки
...
 
/* Инициализировать библиотеку */
printf("Initializing library");
rv = pFunctionList->C_Initialize(NULL_PTR);
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");

...
 
/* Деинициализировать библиотеку */
if (pFunctionList)
{
	printf("Finalizing library");
	rvTemp = pFunctionList->C_Finalize(NULL_PTR);
	if (rvTemp != CKR_OK)
		printf(" -> Failed\n");
	else
		printf(" -> OK\n");
	pFunctionList = NULL_PTR;
}

Определение подключенных устройств

Доступ к каждому подключенному устройству осуществляется с помощью идентификатора слота, к которому оно подключено. Для получения списка всех слотов предназначена функция C_GetSlotList(). Значение первого параметра указывает, должен ли список включать слоты только с подключенным токенами (CK_TRUE) или все слоты (CK_FALSE). 

Получение списка токенов
CK_SLOT_ID_PTR aSlots = NULL_PTR;           // Указатель на массив идентификаторов слотов
CK_ULONG ulSlotCount = 0;                   // Количество идентификаторов слотов в массиве
 
while(TRUE)
{
...
	/* Получить количество слотов c подключенными токенами */
	printf(" Getting number of connected slots");
	rv = pFunctionList->C_GetSlotList(CK_TRUE, NULL_PTR, &ulSlotCount);
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	aSlots = (CK_SLOT_ID*)malloc(ulSlotCount * sizeof(CK_SLOT_ID));
	if (aSlots == NULL)
	{
		printf("Memory allocation for aSlots failed! \n");
		break;
	}
	memset(aSlots, 0, (ulSlotCount * sizeof(CK_SLOT_ID)));

	/* Получить список слотов c подключенными токенами */
	printf(" Getting list of connected slots");
	rv = pFunctionList->C_GetSlotList(CK_TRUE,	aSlots, &ulSlotCount);
	if (rv != CKR_OK)
	{
		printf(" -> Failed %X\n", (int)rv);
		break;
	}
	printf(" -> OK\n");

	printf(" Slots available: 0x%8.8X\n", (int)ulSlotCount);
	...
	break;
}
 
if (aSlots)
{
	free(aSlots);
	aSlots = NULL_PTR;
}

Для получения актуальной информации о состоянии конкретного слота вызывается функция C_GetSlotInfo(), в которую передается идентификатор слота. Ее вызов запускает обновление информации обо всех слотах. Если токен извлечь из разъема и затем снова вставить в тот же самый разъем, то он может подключиться к любому свободному слоту, а не обязательно к тому же самому.

Мониторинг событий в слоте

Для мониторинга событий извлечения и подключения токенов для всех слотов используется функция C_WaitForSlotEvent(), запущенная в отдельном потоке.

При вызове C_WaitForSlotEvent() с флагом CKF_DONT_BLOCK функция возвращает код CKR_NO_EVENT при отсутствии событий или код CKR_OK при его наличии (вместе с идентификатором соответствующего событию слота).

При вызове C_WaitForSlotEvent() с флагом 0 выполнение функции блокируется до возникновения события и функция возвращает код CKR_OK и номер соответствующего слота.

Мониторинг событий в слотах
 /* Количество потоков, одновременно ожидающих события в каком-либо слоте*/
#define MONITORING_THREADS_NUMBER    1

/* Структура данных, содержащая параметры работы для функции ожидания событий в слотах */
typedef struct _MONITORING_THREADS_PARAMS
{
	CK_FUNCTION_LIST_PTR m_pFunctionList;
	CK_FLAGS m_flags;
	DWORD m_dwThread_Number;
} MONITORING_THREADS_PARAMS, * PMONITORING_THREADS_PARAMS;

/* Запустить поток, ожидающий событие в слоте.
   До наступления события выполнение потока заблокировано */
void Monitoring_Slots(IN void* param)  // Указатель на структуру данных типа MONITORING_THREADS_PARAMS с параметрами для запуска потоков 
{
	CK_FUNCTION_LIST_PTR pFunctionList = NULL_PTR;        // Указатель на список функций PKCS#11, хранящийся в структуре CK_FUNCTION_LIST
	CK_SLOT_ID slotID = 0xFFFFFFFF;                       // Идентификатор слота, в котором произошло событие
	CK_SLOT_INFO slotInfo;                                // Структура данных типа CK_SLOT_INFO с информацией о слоте
	CK_FLAGS ckFlags = 0;                                 // Вспомогательная переменная для хранения флагов, передаваемых в функцию C_ cWaitForSlotEvent
	DWORD dwThreadNumber = 0;                             // Вспомогательная переменная для хранения порядкового номера запущенного потока
	CK_RV rv = CKR_OK;                                    // Вспомогательная переменная для хранения кода возврата
	
	/* Получить из структуры данных типа MONITORING_THREADS_PARAMS параметры для дальнейшей работы*/
	PMONITORING_THREADS_PARAMS pMonitoring_Threads_Param = (PMONITORING_THREADS_PARAMS)param;
	pFunctionList = pMonitoring_Threads_Param->m_pFunctionList;
	ckFlags = pMonitoring_Threads_Param->m_flags;
	dwThreadNumber = pMonitoring_Threads_Param->m_dwThread_Number;

	while (TRUE)
	{
		/* Ожидать событие в некотором слоте, режим работы функции C_WaitForSlotEvent зависит от значения флагов ckFlags */
		slotID = 0xFFFFFFFF;
		rv = pFunctionList->C_WaitForSlotEvent(ckFlags,  // 0 для блокирования потока и CKF_DONT_BLOCK для неблокирования
		                                       &slotID,	 // Идентификатор слота, в котором произошло событие
		                                       NULL_PTR);
		if (rv == CKR_CRYPTOKI_NOT_INITIALIZED)
		{
			printf("Work with PKCS#11 has been finished.\n");
			break;
		}
		if (rv == CKR_NO_EVENT)
		{
			printf(" -> Failed \n"
			       "No more slot events...\n");
			break;
		}
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			break;
		}
		memset(&slotInfo, 0,  sizeof(CK_SLOT_INFO));

		/* Получить информацию о слоте */
		rv = pFunctionList->C_GetSlotInfo(slotID,
		                                  &slotInfo);
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			break;
		}

		/* Распечатать информацию о номере потока и событии в слоте */
		printf("\n Monitoring thread: 0x%8.8x \n", (int)dwThreadNumber);
		printf("  Slot ID:          0x%8.8x \n", (int)slotID);
		if (slotInfo.flags & CKF_TOKEN_PRESENT)
			printf("  Token has been attached!\n");
		else
			printf("  Token has been detached!\n");
	}
	printf("Exiting from thread: 0x%8.8x \n\n", (int)dwThreadNumber);
}
 
int main(int argc, char* argv[])
{
	DWORD i = 0;                                             // Вспомогательная переменная. Счетчик цикла
	...

	while (TRUE)
	{
		...
		printf("\nPlease attach or detach Rutoken and press Enter...\n");
		getchar();
		i = 1;
		while (TRUE)
		{
			printf("Events counter: 0x%8.8x \n", (int)i);
			/* Получить все события в слотах, не блокируя поток */
			printf("C_WaitForSlotEvent");
			rv = pFunctionList->C_WaitForSlotEvent(CKF_DONT_BLOCK, 	// Не блокировать поток
			                                       &slotID,			// Идентификатор слота, в котором произошло событие
			                                       NULL_PTR);		// Зарезервировано, должно быть NULL_PTR
			if (rv == CKR_NO_EVENT)
			{
				printf(" -> OK\n");
				printf("No more slots events.\n");
				break;
			}
			if (rv != CKR_OK)
			{
				printf(" -> Failed\n");
				break;
			}
			printf(" -> OK\n");
		}

		if ((rv != CKR_NO_EVENT) && (rv != CKR_OK))
			break;

		/* Запустить поток, ожидающих событие в каком-либо слоте.
		   До наступления события выполнение запущенного потока заблокировано. 
           Первое событие разблокирует выполнение ожидающего потока */
		while (TRUE)
		{
			MONITORING_THREADS_PARAMS aThreads_With_Blocking[MONITORING_THREADS_NUMBER];
			uintptr_t aThreads[MONITORING_THREADS_NUMBER];
			for (i = 0;
			     i < MONITORING_THREADS_NUMBER;
			     i++)
			{
				printf("Starting monitoring thread number 0x%8.8X \n", (int)i);
				memset(&aThreads_With_Blocking[i],
				       0,
				       sizeof(MONITORING_THREADS_PARAMS));
				aThreads_With_Blocking[i].m_pFunctionList = pFunctionList;
				aThreads_With_Blocking[i].m_flags = 0;
				aThreads_With_Blocking[i].m_dwThread_Number = i;
				aThreads[i] = CreateProc(&aThreads[i], NULL_PTR, &Monitoring_Slots, &aThreads_With_Blocking[i]);
			}
			printf("\n\nPlease attach or detach Rutoken or press Enter to exit.\n");
			getchar();
			break;
		}
		break;
	}	
}

Определение типа устройств

Стандарт PKCS#11 предлагает определять тип устройства по параметрам, возвращаемым функциями C_GetSlotInfo() и C_GetTokenInfo(). Функция C_GetSlotInfo() возвращает структуру типа CK_SLOT_INFO, содержащую в поле slotDescription имя считывателя  и в поле manufacturerID производителя устройства (Aktiv Co.). Функция C_GetTokenInfo() возвращает структуру типа CK_TOKEN_INFO, содержащую в поле model наименование модели устройства. Имя считывателя или наименование устройства обычно позволяет однозначно идентифицировать тип подключенного устройства. В таблице приведены значения для каждого из устройств Рутокен.

Модель РутокенЗначение  slotDescription структуры CK_SLOT_INFOЗначение  model структуры CK_TOKEN_INFO
Рутокен SAktiv Co. ruToken 0Rutoken S 64K, где 64K - размер памяти
Рутокен LiteAktiv Rutoken lite 0Rutoken lite
Рутокен ЭЦПAktiv Rutoken ECP 0Rutoken ECP
Рутокен PINPadAktiv Rutoken PINPad 2 0Rutoken PINPad 2
Рутокен WebAktiv Rutoken Web 0Rutoken Web

Более удобным способом получить информацию о подключенном к слоту токене можно с помощью функции расширения C_EX_GetTokenInfoExtended(), которая возвращает расширенные данные в виде структуры типа CK_TOKEN_INFO_EXTENDED. Поля ulTokenClass и ulTokenType содержат информацию о классе и типе устройства соответственно и могут быть использованы для их определения. 

Определение класса токена
CK_C_EX_GetFunctionListExtended pfGetFunctionListEx = NULL_PTR; 	// Указатель на функцию C_EX_GetFunctionListExtended
CK_FUNCTION_LIST_EXTENDED_PTR 	pFunctionListEx = NULL_PTR;       	// Указатель на список функций расширения PKCS#11, хранящийся в структуре CK_FUNCTION_LIST_EXTENDED
CK_TOKEN_INFO_EXTENDED			tokenInfoEx;                        // Структура данных типа CK_TOKEN_INFO_EXTENDED с информацией о токене
 
while(TRUE)
{
	...
 
	printf("Determining token type");
 
	/* Получить адрес функции запроса структуры с указателями на функции расширения */
	pfGetFunctionListEx = (CK_C_EX_GetFunctionListExtended)GetProcAddress(hModule,
				                                                          "C_EX_GetFunctionListExtended");
	if (pfGetFunctionListEx == NULL_PTR)
	{
		printf(" -> Failed\n");
		break;
	}
 
	/* Получить структуру с указателями на функции расширения */
	rv = pfGetFunctionListEx(&pFunctionListEx);
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	memset(&tokenInfoEx,
	   		0,
	  		sizeof(CK_TOKEN_INFO_EXTENDED));
	tokenInfoEx.ulSizeofThisStructure = sizeof(CK_TOKEN_INFO_EXTENDED);
	/* Получить расширенную информацию о подключенном токене */
	rv = pFunctionListEx->C_EX_GetTokenInfoExtended(aSlots[0],		// Идентификатор слота, к которому подключен токен
			                                   		&tokenInfoEx);	// Буфер для помещения информации о токене
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	/* Определить класс токена */
	switch (tokenInfoEx.ulTokenClass)
	{
		case TOKEN_CLASS_S: 
			printf(": Rutoken / Rutoken S\n");
		case TOKEN_CLASS_ECP:
			printf(": Rutoken ECP\n");
		case TOKEN_CLASS_LITE:
			printf(": Rutoken Lite\n");
		case  TOKEN_CLASS_WEB:
			printf(": Rutoken WEB\n");
		case TOKEN_TYPE_RUTOKEN_PINPAD_FAMILY:
			printf(": Rutoken PINPad\n");
		default:
			printf(": undefined\n");
	}

	break;
}

Открытие и закрытие сессии

Большинство функций PKCS#11 требует наличие открытой сессии между токеном и приложением.

Для открытия сессии используется функция C_OpenSession(), для закрытия сессии – C_CloseSession(), для закрытия всех открытых сессий – C_CloseAllSessions().

Сессия может быть открыта только для чтения объектов на токене или для чтения и записи (флаг CKF_RW_SESSION). После открытия сессии приложение получает доступ к публичным объектам на токене. Для доступа к приватным объектом пользователь должен получить доступ Пользователя или Администратора функций C_Login().

При закрытии сессии все сессионные объекты уничтожаются, даже если приложение использует эти объекты в других сессиях. 

Мониторинг событий в слотах
CK_SESSION_HANDLE hSession = NULL_PTR;       // Хэндл открытой сессии

...
 
/* Открыть RW сессию в первом доступном слоте */
printf("Opening Session");
rv = pFunctionList->C_OpenSession(aSlots[0], 								// Идентификатор слота
								  CKF_SERIAL_SESSION | CKF_RW_SESSION,		// Флаги сессии
								  NULL_PTR,
								  NULL_PTR,
								  &hSession);								// Хэндл сессии
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");
 
...
 
/* Закрыть все открытые сессии в слоте */
printf("C_CloseAllSession");
rv = pFunctionList->C_CloseAllSessions(aSlots[0]);
if (rvTemp != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");
hSession = NULL_PTR;

Получение и сброс прав доступа

В PKCS#11 доступны две глобальные роли: CKU_USER – пользователь Рутокен, CKU_SO – администратор Рутокен. Помимо них, устройства Рутокен ЭЦП и Рутокен PINPad имеют локальные роли, про которые можно ознакомиться в разделе Установка и смена локального PIN-кода.

Особенности поведения Рутокен PINPad при включенной политике "Логин и смена PIN-кода Пользователя"

Устройство Рутокен PINPad может быть настроено таким образом, что для команд аутентификации и смены PIN-кода Пользователя будет требоваться ввод PIN-кода только с экрана устройства. В таком случае PIN-код, переданный в функцию C_Login()/C_SetPIN(), будет проигнорирован, а вместо него в функцию будет передан введенный с экрана устройства PIN-код. PIN-код Пользователя в таком случае должен содержать только ASCII цифры.

По умолчанию политика "Логин и смена PIN-кода Пользователя могут быть выполнены только с экрана PINPad" отключена. Настройка политики выполняется с предъявлением специального пароля администрирования PINPad через APDU-команду или утилиту ConfUtil, которая выдается по запросу партнерам-разработчикам. Кроме того, форматирование токена функцией C_EX_InitToken() также сбрасывает бит, отвечающий на требование ввода PIN-кода с экрана.

Для аутентификации предварительно необходимо открыть сессию. 

Мониторинг событий в слотах
/* DEMO PIN-код Пользователя Рутокен */
CK_UTF8CHAR      USER_PIN[]      = {'1', '2', '3', '4', '5', '6', '7', '8'};
 
...
 
/* Выполнить аутентификацию Пользователя */
printf("Logging in");
rv = pFunctionList->C_Login(hSession,				// Хэндл сессии
							CKU_USER,				// Тип пользователя
							USER_PIN,				// PIN-код пользователя
							sizeof(USER_PIN));		// Длина PIN-кода
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");
 
...
 
/*Сбросить права доступа */
printf("Logging out");
rv = pFunctionList->C_Logout(hSession);
if ((rv == CKR_OK) || (rv == CKR_USER_NOT_LOGGED_IN))
	printf(" -> OK\n");
else
	printf(" -> Failed\n");

Управление объектами на токене

Общие атрибуты объектов 

Стандарт PKCS#11 различает несколько классов объектов. Объекты содержат набор атрибутов, каждый из которых имеет только одно определенное значение. 

Атрибут CKA_TOKEN определяет, будет ли созданный объект храниться только в рамках текущей сессии (значение CK_FALSE), или будет сохранен в памяти Рутокен (значение CK_TRUE). Сессионный объект будет уничтожен автоматически при закрытии сессии, а сохраненный на токене доступен вплоть до физического удаления объекта.

Атрибут CKA_PRIVATE определяет доступность объекта. Пользователь не имеет доступа к приватному объекту (значение CK_TRUE) до тех пор, пока не выполнит аутентификацию на токене. Публичный же объект доступен без аутентификации (значение CK_FALSE). 

Атрибут CKA_MODIFIABLE отвечает за возможность изменения атрибутов объекта после его создания. По умолчанию атрибут принимает значение CK_TRUE, при котором редактирование атрибутов объекта становится возможным. Значение CK_FALSE означает, что созданный объект будет доступен «только для чтения» и значения атрибутов объекта после его создания не могут быть изменены.

Сгенерированные устройствами Рутокен закрытые и секретные ключевые объекты в целях безопасности является неизвлекаемыми. Это означает, что значение ключа (значение атрибута CKA_VALUE) невозможно получить через функцию C_GetAttributeValue(). Все криптографические операции с такими ключами производятся внутри устройства без передачи значения ключа наружу.

Рутокен PINPad имеет специфические атрибуты ключевых объектов, подробнее информация доступна в разделе Атрибуты ключевых объектов Рутокен PINPad.

Создание объекта на токене

Для создания объектов на токене предназначена функция C_CreateObject(). В нее передается предварительно созданный шаблон с атрибутами создаваемого объекта, размер шаблона и хэндл открытой сессии с правами Пользователя. Функция возвращает хэндл созданного с указанными атрибутами объекта.

Устройства Рутокен, сертифицированные ФСБ, не поддерживают создание (импорт) ключей функцией C_CreateObject по алгоритмам ГОСТ 28147-89, ГОСТ 34.10-2001 и ГОСТ 34.10-2012 в долговременную память (с флагом CKA_TOKEN = TRUE).

Создание объекта типа CKO_DATA на токене
CK_OBJECT_CLASS ocData = CKO_DATA;
CK_UTF8CHAR 	label[] = “Data object sample”;
CK_UTF8CHAR 	application[] = “Rutoken Control Panel”;
CK_BYTE 		data[] = “Sample data”;
CK_BBOOL 		true = CK_TRUE;


CK_ATTRIBUTE attrDataTmpl[] = {
  {CKA_CLASS, &ocData, sizeof(ocData)},
  {CKA_LABEL, label, sizeof(label)-1},
  {CKA_VALUE, data, sizeof(data)},
  {CKA_APPLICATION, application, sizeof(application)-1},
  {CKA_TOKEN, &true, sizeof(true )}
};
 
CK_OBJECT_HANDLE hData; // Хэндл созданного объекта типа CKO_DATA
 
...
 
printf("Create object");
rv = pFunctionList->C_CreateObject( hSession, 				// Открытая с правами Пользователя сессия
									attrDataTmpl, 			// Шаблон с атрибутами создаваемого объекта
									arraysize(attrDataTmpl),// Размер шаблона
									&hData);				// Возвращаемый хэндл созданного объекта
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");	

Импорт объектов на токен 

Для импорта объектов также используется функция C_CreateObject(). В нее передается предварительно созданный шаблон с атрибутами импортируемого объекта (в том числе и значением CKA_VALUE), размер шаблона и хэндл открытой сессии с правами Пользователя. Функция возвращает хэндл созданного с указанными атрибутами объекта.

Устройства Рутокен, сертифицированные ФСБ, не поддерживают создание (импорт) ключей функцией C_CreateObject по алгоритмам ГОСТ 28147-89, ГОСТ 34.10-2001 и ГОСТ 34.10-2012 в долговременную память (с флагом CKA_TOKEN = TRUE).

Импорт сертификата
/* Значение сертификата ключа подписи */
CK_BYTE cbCertificate[] = { 0x30, 0x82, 0x03, 0x36, 0x30, 0x82, 0x02, 0xe5,
							0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0a, 0x24,
							0xa5, 0x24, 0x63, 0x00, 0x02, 0x00, 0x01, 0xa7,
							0x15, 0x30, 0x08, 0x06, 0x06, 0x2a, 0x85, 0x03,
							0x02, 0x02, 0x03, 0x30, 0x65, 0x31, 0x20, 0x30,
							0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
							0x0d, 0x01, 0x09, 0x01, 0x16, 0x11, 0x69, 0x6e,
							0x66, 0x6f, 0x40, 0x63, 0x72, 0x79, 0x70, 0x74,
							0x6f, 0x70, 0x72, 0x6f, 0x2e, 0x72, 0x75, 0x31,
							0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
							0x13, 0x02, 0x52, 0x55, 0x31, 0x13, 0x30, 0x11,
							0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x43,
							0x52, 0x59, 0x50, 0x54, 0x4f, 0x2d, 0x50, 0x52,
							0x4f, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
							0x04, 0x03, 0x13, 0x16, 0x54, 0x65, 0x73, 0x74,
							0x20, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x20,
							0x43, 0x52, 0x59, 0x50, 0x54, 0x4f, 0x2d, 0x50,
							0x52, 0x4f, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31,
							0x31, 0x31, 0x32, 0x32, 0x31, 0x30, 0x31, 0x33,
							0x34, 0x32, 0x5a, 0x17, 0x0d, 0x31, 0x34, 0x31,
							0x30, 0x30, 0x34, 0x30, 0x37, 0x30, 0x39, 0x34,
							0x31, 0x5a, 0x30, 0x65, 0x31, 0x10, 0x30, 0x0e,
							0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x07, 0x49,
							0x76, 0x61, 0x6e, 0x6f, 0x66, 0x66, 0x31, 0x0b,
							0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
							0x02, 0x52, 0x55, 0x31, 0x14, 0x30, 0x12, 0x06,
							0x03, 0x55, 0x04, 0x05, 0x13, 0x0b, 0x31, 0x32,
							0x33, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x31,
							0x32, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x09, 0x2a,
							0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01,
							0x16, 0x0e, 0x69, 0x76, 0x61, 0x6e, 0x6f, 0x76,
							0x40, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x72, 0x75,
							0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04,
							0x08, 0x13, 0x06, 0x4d, 0x6f, 0x73, 0x63, 0x6f,
							0x77, 0x30, 0x63, 0x30, 0x1c, 0x06, 0x06, 0x2a,
							0x85, 0x03, 0x02, 0x02, 0x13, 0x30, 0x12, 0x06,
							0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01,
							0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e,
							0x01, 0x03, 0x43, 0x00, 0x04, 0x40, 0xfb, 0x3c,
							0xdc, 0x59, 0xc3, 0x9c, 0x4a, 0x43, 0x89, 0x87,
							0xc7, 0xd7, 0xfe, 0x50, 0x19, 0xb3, 0x0c, 0x8b,
							0x76, 0x97, 0xa9, 0xdf, 0xb7, 0xca, 0x2c, 0x6c,
							0x3b, 0xa9, 0x13, 0xf4, 0xe0, 0x69, 0x02, 0x59,
							0x92, 0x47, 0x21, 0x1a, 0xef, 0x90, 0x61, 0x91,
							0x40, 0x30, 0xdd, 0x7c, 0xb0, 0x4f, 0x64, 0x5f,
							0x24, 0x9a, 0xf1, 0xd6, 0x0f, 0xa9, 0xf0, 0x86,
							0xd9, 0x35, 0x2b, 0x3e, 0xf2, 0xf3, 0xa3, 0x82,
							0x01, 0x73, 0x30, 0x82, 0x01, 0x6f, 0x30, 0x0b,
							0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03,
							0x02, 0x04, 0xf0, 0x30, 0x26, 0x06, 0x03, 0x55,
							0x1d, 0x25, 0x04, 0x1f, 0x30, 0x1d, 0x06, 0x07,
							0x2a, 0x85, 0x03, 0x02, 0x02, 0x22, 0x06, 0x06,
							0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03,
							0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
							0x07, 0x03, 0x04, 0x30, 0x1d, 0x06, 0x03, 0x55,
							0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x8a, 0x24,
							0x35, 0x74, 0x6b, 0xf7, 0x91, 0x17, 0x92, 0xb2,
							0xcf, 0x8f, 0x63, 0x87, 0xb7, 0x69, 0x06, 0xe1,
							0x71, 0xf2, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
							0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x6d,
							0x8f, 0x5e, 0x05, 0xd9, 0x5f, 0xac, 0x91, 0x17,
							0x94, 0x1e, 0x95, 0x9a, 0x05, 0x30, 0x38, 0x37,
							0x7a, 0x10, 0x2a, 0x30, 0x55, 0x06, 0x03, 0x55,
							0x1d, 0x1f, 0x04, 0x4e, 0x30, 0x4c, 0x30, 0x4a,
							0xa0, 0x48, 0xa0, 0x46, 0x86, 0x44, 0x68, 0x74,
							0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
							0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x70,
							0x72, 0x6f, 0x2e, 0x72, 0x75, 0x2f, 0x43, 0x65,
							0x72, 0x74, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c,
							0x2f, 0x54, 0x65, 0x73, 0x74, 0x25, 0x32, 0x30,
							0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x25, 0x32,
							0x30, 0x43, 0x52, 0x59, 0x50, 0x54, 0x4f, 0x2d,
							0x50, 0x52, 0x4f, 0x28, 0x32, 0x29, 0x2e, 0x63,
							0x72, 0x6c, 0x30, 0x81, 0xa0, 0x06, 0x08, 0x2b,
							0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
							0x81, 0x93, 0x30, 0x81, 0x90, 0x30, 0x33, 0x06,
							0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
							0x01, 0x86, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a,
							0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x63, 0x72,
							0x79, 0x70, 0x74, 0x6f, 0x70, 0x72, 0x6f, 0x2e,
							0x72, 0x75, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x6e,
							0x63, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73,
							0x72, 0x66, 0x30, 0x59, 0x06, 0x08, 0x2b, 0x06,
							0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x4d,
							0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
							0x77, 0x77, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74,
							0x6f, 0x70, 0x72, 0x6f, 0x2e, 0x72, 0x75, 0x2f,
							0x43, 0x65, 0x72, 0x74, 0x45, 0x6e, 0x72, 0x6f,
							0x6c, 0x6c, 0x2f, 0x70, 0x6b, 0x69, 0x2d, 0x73,
							0x69, 0x74, 0x65, 0x5f, 0x54, 0x65, 0x73, 0x74,
							0x25, 0x32, 0x30, 0x43, 0x65, 0x6e, 0x74, 0x65,
							0x72, 0x25, 0x32, 0x30, 0x43, 0x52, 0x59, 0x50,
							0x54, 0x4f, 0x2d, 0x50, 0x52, 0x4f, 0x28, 0x32,
							0x29, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x08, 0x06,
							0x06, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x03, 0x03,
							0x41, 0x00, 0x2b, 0xd2, 0xfe, 0x64, 0x54, 0x3a,
							0xe1, 0xf6, 0x89, 0x75, 0xfe, 0xbb, 0xa6, 0x29,
							0xed, 0x0b, 0x92, 0xc0, 0xa4, 0x84, 0x15, 0x59,
							0x23, 0x12, 0x08, 0xbb, 0xd3, 0xab, 0x8e, 0x2e,
							0x75, 0xb9, 0xbf, 0x9e, 0xd1, 0x9d, 0x1e, 0xf9,
							0x6a, 0x24, 0xed, 0xb8, 0x58, 0x15, 0x1f, 0x03,
							0x11, 0xfa, 0xd3, 0x85, 0xf1, 0x34, 0x96, 0xac,
							0x20, 0x8e, 0xdd, 0xad, 0x4e, 0xae, 0x55, 0x3e,
							0x8d, 0xd1, 0xff,
};

/* Шаблон для импорта сертификата ключа подписи */
CK_ATTRIBUTE CertTmpl[] =
{
	{ CKA_CLASS, &ocCert, sizeof(ocCert)},							// Объект сертификата
	{ CKA_ID, &KeyPairIDGOST1, sizeof(KeyPairIDGOST1) - 1},         // Идентификатор сертификата
	{ CKA_TOKEN, &bTrue, sizeof(bTrue)},                            // Сертификат является объектом токена
	{ CKA_PRIVATE, &bFalse, sizeof(bFalse)},                        // Сертификат доступен без авторизации на токене
	{ CKA_VALUE, &cbCertificate, sizeof(cbCertificate)}				// Значение сертификата
};

CK_OBJECT_HANDLE 	hCert;								 // Хэндл сертификата
 
printf("Import certificate");
rv = pFunctionList->C_CreateObject( hSession, 			// Открытая с правами Пользователя сессия
									CertTmpl, 			// Шаблон с атрибутами создаваемого объекта
									arraysize(CertTmpl),// Размер шаблона
									&hCert);			// Возвращаемый хэндл созданного объекта
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");	

Поиск объектов на токене

Для поиска объектов на токене предназначены три функции C_FindObjectsInit()C_FindObjects() и  C_FindObjectsFinal(). Сначала операция поиска инициализируется функцией C_FindObjectsInit(), в которую передается указатель шаблон атрибутов для поиска объекта и его размер, затем функция C_FindObjects() выполняет поиск объектов согласно заданным атрибутам и возвращает хэндлы всех найденных объектов в массиве. C_FindObjectsFinal() завершает процедуру поиска.

Поиск объектов на токене
/* Шаблон для поиска симметричного ключа ГОСТ 28147-89 */
CK_ATTRIBUTE attrGOST28147SecKey[] =
{
	{ CKA_ID, &SecKeyID, sizeof(SecKeyID) - 1}       // Критерий поиска - идентификатор ключа
};
 
CK_OBJECT_HANDLE_PTR 	phObject = NULL_PTR;         // Указатель на массив хэндлов объектов, соответствующих критериям поиска
CK_ULONG 				ulObjectCount = 0;           // Количество хэндлов объектов в массиве
CK_RV 					rvTemp = CKR_OK;             // Вспомогательная переменная для хранения кода возврата

while (TRUE)
{
	...

	while(TRUE)
	{
		/* Инициализировать операцию поиска */
		printf("C_FindObjectsInit");
		rv = pFunctionList->C_FindObjectsInit(hSession,							// Хэндл сессии, открытой с правами Пользователя
		                                      attrGOST28147SecKey,				// Шаблон для поиска с атрибутами объекта
		                                      arraysize(attrGOST28147SecKey));	// Количество строк в шаблоне
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			break;
		}
		printf(" -> OK\n");
	
		/* Найти все объекты, соответствующие критериям поиска */
		printf("C_FindObjects");
		// Считаем, что максимальное количество объектов не превышает 100
		phObject = (CK_OBJECT_HANDLE*)malloc(100 * sizeof(CK_OBJECT_HANDLE));
		if (phObject == NULL)
		{
			printf("Memory allocation for aSlots failed! \n");
			break;
		}
		memset(phObject,
		       0,
		       (100 * sizeof(CK_OBJECT_HANDLE)));
		rv = pFunctionList->C_FindObjects(hSession,			// Хэндл открытой сессии
		                                  phObject,			// Указатель на массив хэндлов найденных объектов
		                                  100,				// Максимальное количество хэндлов найденных объектов
		                                  pulObjectCount);	// Фактическое количество найденных объектов
		if (rv != CKR_OK)
			printf(" -> Failed\n");
		else
			printf(" -> OK\n");
		break;
	}

	/* Деинициализировать операцию поиска */
	printf("C_FindObjectsFinal");
	rvTemp = pFunctionList->C_FindObjectsFinal(hSession);	// Хэндл открытой сессии
	if (rvTemp != CKR_OK)
		printf(" -> Failed\n");
	else
		printf(" -> OK\n");
	if (rv == CKR_OK)
		printf("Search has been completed.\n"
		       "Objects found: %d \n",
		       (int)ulObjectCount);
	else
	{
		printf("Search failed!\n");
		if (phObject)
		{
			free(phObject);
			phObject = NULL_PTR;
			ulObjectCount = 0;
		}
	}
	break;
}

Чтение и изменение объектов

Для чтения атрибутов созданного объекта используется функция C_GetAttributeValue(). Если объекты или его атрибуты не являются защищенными от записи (например, значение атрибута CKA_MODIFIABLE равно CK_TRUE), то значения атрибутов могут быть изменены функцией C_SetAttributeValue().

Сгенерированные устройствами Рутокен закрытые и секретные ключевые объекты в целях безопасности является неизвлекаемыми. Это означает, что значение ключа (значение атрибута CKA_VALUE) невозможно получить через функцию C_GetAttributeValue(). Все криптографические операции с такими ключами производятся внутри устройства без извлечения ключа и передачи его значения наружу.

Чтение и изменение атрибутов объекта
CK_BYTE 		new_data[] = “Sample Rutoken data”;

CK_ATTRIBUTE attrDataReadTmpl[] = {CKA_VALUE, NULL_PTR, NULL};
CK_ATTRIBUTE attrDataEditTmpl[] = {CKA_VALUE, new_data, sizeof(new_data)};


while(true)
{
	...
 
	/* Изменить значение объекта */
	printf("Setting object value");
	rv = pFunctionList->C_SetAttributeValue(hSession,			// Хэндл открытой с правами Пользователя сессии 
		                                    hData,				// Хэндл объекта ключа
		                                    &attrDataEditTmpl,	// Шаблон изменения значения атрибута
		                                    1);					// Количество атрибутов в шаблоне
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Получить размер буфера для хранения значения атрибута CKA_VALUE*/
	printf("Getting object value size");
	rv = pFunctionList->C_GetAttributeValue(hSession,			// Хэндл открытой с правами Пользователя сессии 
		                                    hData,				// Хэндл объекта ключа
		                                    &attrDataReadTmpl,	// Шаблон получения значения атрибута
		                                    1);					// Количество атрибутов в шаблоне
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Выделить необходимое количество памяти для значения атрибута */
	attrDataReadTmpl.pValue = (CK_BYTE*)malloc(attrDataReadTmpl.ulValueLen);
	if (attrDataReadTmpl.pValue == NULL)
	{
		printf("Memory allocation for attrDataReadTmpl failed! \n");
		break;
	}
	memset(attrDataReadTmpl.pValue,
	       0,
	       (attrDataReadTmpl.ulValueLen * sizeof(CK_BYTE)));
 
	/* Получить значение объекта */
	printf("Getting object value");
	rv = pFunctionList->C_GetAttributeValue(hSession,			// Хэндл открытой с правами Пользователя сессии 
		                                    hData,				// Хэндл объекта ключа
		                                    &attrDataReadTmpl,	// Шаблон получения значения атрибута
		                                    1);					// Количество атрибутов в шаблоне
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Распечатать буфер со значением объекта */
	printf("Data is:\n");
	for (i = 0;
	     i < attrDataReadTmpl.ulValueLen;
	     i++)
		printf("%s", attrDataReadTmpl.pValue[i]);
	break;
}
 
if (attrDataReadTmpl.pValue)
{
	free(attrDataReadTmpl.pValue);
	attrDataReadTmpl.pValue = NULL_PTR;
	attrDataReadTmpl.ulValueLen= 0;
}
 

Удаление объектов на токене

Для удаления объекта на токене необходимо знать его хэндл, который следует передать в функцию C_DestroyObject() (предварительно должна быть открыта сессия с правами Пользователя). Если хэндл объекта неизвестен, то по любому известному атрибуту объекта можно получить его хэндл через группу функций  C_FindObjectsInit()C_FindObjects() и C_FindObjectsFinal()

Удаление объектов на токене
/* Удалить все найденные объекты */
for (i = 0;
	 i < ulObjectCount;
	 i++)
{
	printf(" C_DestroyObject %d", (int)(i + 1));
	rv = pFunctionList->C_DestroyObject(hSession,		// Открытая с правами Пользователя сессия
	                                    phObject[i]);	// Массив хэндлов объектов для удаления
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
}	
if (ulObjectCount != 0)
	printf("Destruction objects has been completed successfully.\n");
free(phObject);
phObject = NULL_PTR;
ulObjectCount = 0;

Генерация ключевой пары

Атрибуты ключевых объектов

Все поддерживаемые устройствами Рутокен атрибуты объектов представлены в разделе Объекты PKCS #11.

Атрибуты ключевых объектов Рутокен PINPad 

Рутокен PINPad имеет два специфических атрибута закрытого ключа, которые влияют на поведение устройства при осуществлении криптографических операций с использованием такого ключа: CKA_VENDOR_KEY_CONFIRM_OP и CKA_VENDOR_KEY_PIN_ENTER. Оба атрибута присваиваются закрытому ключу при генерации ключевой пары соответственно указанным в шаблоне значениям и поэтому не могут быть изменены после генерации.

Если ключ был создан с флагом визуализации CKA_VENDOR_KEY_CONFIRM_OP, то данные, которые подписываются с помощью такого ключа, перед подписью будут показаны на экране устройства и для их подписи потребуется подтверждение пользователя в виде нажатия специальной кнопки на экране устройства. 

Если ключ был создан с флагом повышенной защиты CKA_VENDOR_KEY_PIN_ENTER, то для подписи таким ключом перед операцией потребуется ввести локальный PIN-код Пользователя с идентификатором 001F на экране устройства. Такой PIN-код должен содержать только цифры ASCII и по умолчанию имеет значение "12345678".

Особенности поведения Рутокен PINPad при включенной политике "Отключить кэширование PIN2"

Устройство Рутокен PINPad по умолчанию настроено таким образом, что для подписи ключом с атрибутом CKA_VENDOR_KEY_PIN_ENTER ввод локального PIN-кода (PIN2) с экрана устройства будет требоваться только один раз, при подписи первого документа. Далее этот PIN-код кэшируется и запоминается в памяти Рутокен PINPad, и при подписывании последующих документов выдает окно подтверждения/отказа от подписи вместо окна ввода PIN-кода. Кэш сбрасывается по истечении 15 минут или после выполнения функции C_Logout().

По умолчанию политика "Отключить кэширование PIN2" отключена. Настройка политики выполняется с предъявлением специального пароля администрирования PINPad через APDU-команду или утилиту ConfUtil, которая выдается по запросу партнерам-разработчикам.

Поддерживаемые типы ключей

Устройства Рутокен поддерживают следующие типы ключей асимметричной криптографии (CK_KEY_TYPE

  • CKK_GOSTR3410 для ключей ГОСТ Р 34.10-2001 и  ГОСТ Р 34.10-2012 (256 бит),

  • CKK_GOSTR3410_512 для ключей ГОСТ Р 34.10-2012 (512 бит),
  • CKK_RSA для ключей RSA.

Примеры шаблонов ключей ГОСТ Р 34.10-2001 

Ниже представлены примеры шаблонов закрытого и открытого ключа ГОСТ Р 34.10-2001 с пояснениями.

Шаблон закрытого ключа ГОСТ Р 34.10-2001
CK_OBJECT_CLASS	ocPrivKey	 	= CKO_PRIVATE_KEY;
CK_UTF8CHAR 	PrivKeyLabel[]	= {"GOST Private Key"}; 
CK_BYTE 		KeyPairID[] 	= {"GOST keypair"}; 
CK_KEY_TYPE 	KeyType 		= CKK_GOSTR3410;
CK_BBOOL 		bTrue 			= CK_TRUE; 
CK_BYTE		GOST3410params[]	= { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01 }; // Параметры алгоритма ГОСТ Р 34.10-2001  
 
CK_ATTRIBUTE GOST34_10_2001PrivateKey[] = { 
	{ CKA_CLASS, &ocPrivKey, sizeof(ocPrivKey)}, 					// Объект закрытого ключа 
	{ CKA_LABEL, &PrivKeyLabel, sizeof(PrivKeyLabel) - 1}, 			// Метка ключа 
	{ CKA_ID, &KeyPairID, sizeof(KeyPairID) - 1}, 					// Идентификатор ключевой пары #1 (должен совпадать у открытого и закрытого ключей) 
	{ CKA_KEY_TYPE, &KeyType, sizeof(KeyType)}, 					// Тип ключа 
	{ CKA_TOKEN, &bTrue, sizeof(bTrue)},							// Ключ является объектом токена 
	{ CKA_PRIVATE, &bTrue, sizeof(bTrue)}, 							// Ключ доступен только после авторизации на токене 
	{ CKA_DERIVE, &bTrue, sizeof(bTrue)}, 							// Ключ поддерживает деривацию (из него могут быть получены другие ключи) 
	{ CKA_VENDOR_KEY_CONFIRM_OP, &bTrue, sizeof(bTrue) }, 			// Операция подписи требует подтверждения на PINPad (только для Рутокен PINPad)
	{ CKA_VENDOR_KEY_PIN_ENTER, &bTrue, sizeof(bTrue) }, 			// Операция подписи требует ввода PIN-кода на PINPad (только для Рутокен PINPad)
	{ CKA_GOSTR3410_PARAMS, GOST3410params, sizeof(GOST3410params)} // Параметры алгоритма 
};
Шаблон открытого ключа ГОСТ Р 34.10-2001
CK_OBJECT_CLASS ocPubKey 		= CKO_PUBLIC_KEY; 
CK_UTF8CHAR 	PubKeyLabel[] 	= {"GOST Public Key"}; 
CK_BYTE 		KeyPairID[] 	= {"GOST keypair"}; 
CK_KEY_TYPE 	KeyType 		= CKK_GOSTR3410; 
CK_BBOOL 		bTrue 			= CK_TRUE; 
CK_BBOOL 		bFalse 			= CK_FALSE; 
/* Набор параметров КриптоПро A алгоритма ГОСТ Р 34.10-2001 */
CK_BYTE 	GOST3410params[]	= { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01 }; // Параметры алгоритма ГОСТ Р 34.10-2001 
CK_BYTE 	GOST3411params[]	= { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01 }; // Параметры алгоритма ГОСТ Р 34.11-1994   
 
CK_ATTRIBUTE GOST34_10_2001PublicKey[] = {
	{ CKA_CLASS, &ocPubKey, sizeof(ocPubKey)}, 						// Объект открытого ключа 
	{ CKA_LABEL, &PubKeyLabel, sizeof(PubKeyLabel)-1}, 				// Метка ключа 
	{ CKA_ID, &KeyPairID, sizeof(KeyPairID)-1}, 					// Идентификатор ключевой пары 
	{ CKA_KEY_TYPE, &KeyType, sizeof(KeyType)}, 					// Тип ключа 
	{ CKA_TOKEN, &bTrue, sizeof(bTrue)}, 							// Ключ является объектом токена 
	{ CKA_PRIVATE, &bFalse, sizeof(bFalse)}, 						// Ключ доступен без авторизации на токене 
	{ CKA_DERIVE, &bTrue, sizeof(bTrue)}, 							// Ключ поддерживает деривацию (из него могут быть получены другие ключи)
	{ CKA_GOSTR3410_PARAMS, GOST3410params, sizeof(GOST3410params)},// Параметры алгоритма 
	{ CKA_GOSTR3411_PARAMS, GOST3411params, sizeof(GOST3411params)} // Параметры алгоритма 
};

Примеры шаблонов ключей ГОСТ Р 34.10-2012

Ниже представлены примеры шаблонов закрытого и открытого ключа ГОСТ Р 34.10-2012 с пояснениями.

Шаблон закрытого ключа ГОСТ Р 34.10-2012
CK_OBJECT_CLASS	ocPrivKey	 			= CKO_PRIVATE_KEY;
CK_UTF8CHAR 	PrivKeyLabel[]			= {"GOST Private Key"}; 
CK_BYTE 		KeyPairID[] 			= {"GOST keypair"}; 
CK_KEY_TYPE 	keyTypeGostR3410		= CKK_GOSTR3410;		// Для закрытого ключа длиной 256 бит
CK_KEY_TYPE 	keyTypeGostR3410_512 	= CKK_GOSTR3410_512;	// Для закрытого ключа длиной 512 бит
CK_BBOOL 		bTrue 					= CK_TRUE; 

/* Набор параметров КриптоПро A алгоритма ГОСТ Р 34.10-2001 */
CK_BYTE 		paramsGostR3410[] 		= {0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01};  
 
/* Набор параметров КриптоПро A алгоритма ГОСТ Р 34.10-2012(512) */
CK_BYTE 		paramsGostR3410_512[] 	= {0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x01, 0x02, 0x01}; 
 
 
CK_ATTRIBUTE GOST34_10_2012_256_PrivateKey[] = { 					// Шаблон для ключа длиной 256 бит
	{ CKA_CLASS, &ocPrivKey, sizeof(ocPrivKey)}, 					// Объект закрытого ключа 
	{ CKA_LABEL, &PrivKeyLabel, sizeof(PrivKeyLabel) - 1}, 			// Метка ключа 
	{ CKA_ID, &KeyPairID, sizeof(KeyPairID) - 1}, 					// Идентификатор ключевой пары #1 (должен совпадать у открытого и закрытого ключей) 
	{ CKA_KEY_TYPE, &keyTypeGostR3410, sizeof(keyTypeGostR3410)}, 	// Тип ключа 
	{ CKA_TOKEN, &bTrue, sizeof(bTrue)},							// Ключ является объектом токена 
	{ CKA_PRIVATE, &bTrue, sizeof(bTrue)}, 							// Ключ доступен только после авторизации на токене 
	{ CKA_DERIVE, &bTrue, sizeof(bTrue)}, 							// Ключ поддерживает деривацию (из него могут быть получены другие ключи) 
	{ CKA_VENDOR_KEY_CONFIRM_OP, &bTrue, sizeof(bTrue) }, 			// Операция подписи требует подтверждения на PINPad (только для Рутокен PINPad)
	{ CKA_VENDOR_KEY_PIN_ENTER, &bTrue, sizeof(bTrue) }, 			// Операция подписи требует ввода PIN-кода на PINPad (только для Рутокен PINPad)
	{ CKA_GOSTR3410_PARAMS, parametersGostR3410, sizeof(parametersGostR3410)} // Параметры алгоритма 
};
 
CK_ATTRIBUTE GOST34_10_2012_512_PrivateKey[] = { 					// Шаблон для ключа длиной 512 бит
	{ CKA_CLASS, &ocPrivKey, sizeof(ocPrivKey)}, 					// Объект закрытого ключа 
	{ CKA_LABEL, &PrivKeyLabel, sizeof(PrivKeyLabel) - 1}, 			// Метка ключа 
	{ CKA_ID, &KeyPairID, sizeof(KeyPairID) - 1}, 					// Идентификатор ключевой пары #1 (должен совпадать у открытого и закрытого ключей) 
	{ CKA_KEY_TYPE, &keyTypeGostR3410_512, sizeof(keyTypeGostR3410)_512},   // Тип ключа 
	{ CKA_TOKEN, &bTrue, sizeof(bTrue)},							// Ключ является объектом токена 
	{ CKA_PRIVATE, &bTrue, sizeof(bTrue)}, 							// Ключ доступен только после авторизации на токене 
	{ CKA_DERIVE, &bTrue, sizeof(bTrue)}, 							// Ключ поддерживает деривацию (из него могут быть получены другие ключи) 
	{ CKA_VENDOR_KEY_CONFIRM_OP, &bTrue, sizeof(bTrue) }, 			// Операция подписи требует подтверждения на PINPad (только для Рутокен PINPad)
	{ CKA_VENDOR_KEY_PIN_ENTER, &bTrue, sizeof(bTrue) }, 			// Операция подписи требует ввода PIN-кода на PINPad (только для Рутокен PINPad)
	{ CKA_GOSTR3410_PARAMS, paramsGostR3410_512, sizeof(paramGostR3410_512)} // Параметры алгоритма 
};
Шаблон открытого ключа ГОСТ Р 34.10-2012
CK_OBJECT_CLASS ocPubKey 				= CKO_PUBLIC_KEY; 
CK_UTF8CHAR 	PubKeyLabel[] 			= {"GOST Public Key"}; 
CK_BYTE 		KeyPairID[] 			= {"GOST keypair"}; 
CCK_KEY_TYPE 	keyTypeGostR3410		= CKK_GOSTR3410;		// Для закрытого ключа длиной 256 бит
CK_KEY_TYPE 	keyTypeGostR3410_512 	= CKK_GOSTR3410_512;	// Для закрытого ключа длиной 512 бит
CK_BBOOL 		bTrue 					= CK_TRUE; 
CK_BBOOL 		bFalse 					= CK_FALSE; 
/* Набор параметров КриптоПро A алгоритма ГОСТ Р 34.10-2001 */
CK_BYTE			paramsGostR3410[] 		= { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01 };

/* Набор параметров КриптоПро A алгоритма ГОСТ Р 34.10-2012(512) */
CK_BYTE 		paramsGostR3410_512[] 	= { 0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x01, 0x02, 0x01 };

/* Набор параметров КриптоПро алгоритма ГОСТ Р 34.11-2012(256) */
CK_BYTE 		paramsGostR3411_256[] 	= { 0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02 };

/* Набор параметров КриптоПро алгоритма ГОСТ Р 34.11-2012(512) */
CK_BYTE 		paramsGostR3411_512[] 	= { 0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03 }; 
 
 
CK_ATTRIBUTE GOST34_10_2012_256_PublicKey[] = {						// Шаблон для открытого ключа, соответствуюзего закрытому длиной 256 бит
	{ CKA_CLASS, &ocPubKey, sizeof(ocPubKey)}, 						// Объект открытого ключа 
	{ CKA_LABEL, &PubKeyLabel, sizeof(PubKeyLabel)-1}, 				// Метка ключа 
	{ CKA_ID, &KeyPairID, sizeof(KeyPairID)-1}, 					// Идентификатор ключевой пары 
	{ CKA_KEY_TYPE, &keyTypeGostR3410, sizeof(keyTypeGostR3410)}, 	// Тип ключа 
	{ CKA_TOKEN, &bTrue, sizeof(bTrue)}, 							// Ключ является объектом токена 
	{ CKA_PRIVATE, &bFalse, sizeof(bFalse)}, 						// Ключ доступен без авторизации на токене 
	{ CKA_DERIVE, &bTrue, sizeof(bTrue)}, 							// Ключ поддерживает деривацию (из него могут быть получены другие ключи)
	{ CKA_GOSTR3410_PARAMS, paramsGostR3410, sizeof(paramsGostR3410)},			// Параметры алгоритма 
	{ CKA_GOSTR3411_PARAMS, paramsGostR3411_256, sizeof(paramsGostR3411_256)} 	// Параметры алгоритма 
};
 
CK_ATTRIBUTE GOST34_10_2012_512_PublicKey[] = {						// Шаблон для открытого ключа, соответствуюзего закрытому длиной 512бит
	{ CKA_CLASS, &ocPubKey, sizeof(ocPubKey)}, 						// Объект открытого ключа 
	{ CKA_LABEL, &PubKeyLabel, sizeof(PubKeyLabel)-1}, 				// Метка ключа 
	{ CKA_ID, &KeyPairID, sizeof(KeyPairID)-1}, 					// Идентификатор ключевой пары 
	{ CKA_KEY_TYPE, &keyTypeGostR3410_512, sizeof(keyTypeGostR3410_512)}, 	    // Тип ключа 
	{ CKA_TOKEN, &bTrue, sizeof(bTrue)}, 							// Ключ является объектом токена 
	{ CKA_PRIVATE, &bFalse, sizeof(bFalse)}, 						// Ключ доступен без авторизации на токене 
	{ CKA_DERIVE, &bTrue, sizeof(bTrue)}, 							// Ключ поддерживает деривацию (из него могут быть получены другие ключи)
	{ CKA_GOSTR3410_PARAMS, paramsGostR3410_512, sizeof(paramsGostR3410_512)},	// Параметры алгоритма 
	{ CKA_GOSTR3411_PARAMS, paramsGostR3411_512, sizeof(paramsGostR3411_512)} 	// Параметры алгоритма 
};

Поддерживаемые механизмы генерации ключей

Устройства Рутокен поддерживают следующие механизмы генерации ключевой пары:

  • CKM_GOSTR3410_KEY_PAIR_GEN для генерации ключевой пары ГОСТ Р 34.10.2001  и  ГОСТ Р 34.10.2012 с длиной ключа 256 бит,
  • CKM_GOSTR3410_512_KEY_PAIR_GEN для генерации ключевой пары ГОСТ Р 34.10.2012 с длиной ключа 512 бит,
  • CKM_RSA_PKCS_KEY_PAIR_GEN для генерации ключевой пары RSA.

Пример генерации ключевой пары

Предварительно должна быть открыта сессия чтения/записи с авторизацией с правами пользователя Рутокен. 

Генерация ключевой пары ГОСТ Р 34.10-2001
/* Вычисление размера массива */
#define 		 arraysize(a)   (sizeof(a)/sizeof(a[0]))
 
CK_MECHANISM     KeyGenMech	 = {CKM_GOSTR3410_KEY_PAIR_GEN, NULL_PTR, 0}; // Генерация ключа ГОСТ Р 34.10-2001
 
CK_OBJECT_HANDLE hPublicKey	 = NULL_PTR;    // Хэндл открытого ключа
CK_OBJECT_HANDLE hPrivateKey = NULL_PTR;    // Хэндл закрытого ключа

... 
printf("Generating key pair");
rv = pFunctionList->C_GenerateKeyPair(hSession,                             // Хэндл открытой сессии
			                          &KeyGenMech,                          // Используемый механизм генерации ключевой пары 
			                          GOST34_10_2001PublicKey,              // Шаблон открытого ключа 
			                          arraysize(GOST34_10_2001PublicKey),   // Размер шаблона открытого ключа
			                          GOST34_10_2001PrivateKey,             // Шаблон закрытого ключа 
			                          arraysize(GOST34_10_2001PrivateKey),  // Размер шаблона закрытого ключа
			                          &hPublicKey,                      	// Хэндл открытого ключа
			                          &hPrivateKey);                    	// Хэндл закрытого ключа
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");
Генерация ключевой пары ГОСТ Р 34.10-2012
/* Вычисление размера массива */
#define 		 arraysize(a)   (sizeof(a)/sizeof(a[0]))
 
CK_MECHANISM     gostR3410KeyPairGenMech 		= {CKM_GOSTR3410_KEY_PAIR_GEN, NULL_PTR, 0}; 		// Механизм генерации ключевой пары ГОСТ Р 34.10-2001
CK_MECHANISM 	 gostR3410_512KeyPairGenMech	= {CKM_GOSTR3410_512_KEY_PAIR_GEN, NULL_PTR, 0};  	// Механизм генерации ключевой пары ГОСТ Р 34.10-2012(512) */

 
CK_OBJECT_HANDLE hPublicKey_256	 = NULL_PTR;    // Хэндл открытого ключа
CK_OBJECT_HANDLE hPrivateKey_256 = NULL_PTR;    // Хэндл закрытого ключа
CK_OBJECT_HANDLE hPublicKey_512	 = NULL_PTR;    // Хэндл открытого ключа
CK_OBJECT_HANDLE hPrivateKey_512 = NULL_PTR;    // Хэндл закрытого ключа
... 
printf("Generating 256 bit key pair");
rv = pFunctionList->C_GenerateKeyPair(hSession,									 // Хэндл открытой сессии
			                          &gostR3410KeyPairGenMech,                  // Используемый механизм генерации ключевой пары 
				                      GOST34_10_2012_256_PublicKey,              // Шаблон открытого ключа
			                          arraysize(GOST34_10_2012_256_PublicKey),   // Размер шаблона открытого ключа
			                          GOST34_10_2012_256_PrivateKey,             // Шаблон закрытого ключа 
			                          arraysize(GOST34_10_2012_256_PrivateKey),  // Размер шаблона закрытого ключа
			                          &hPublicKey_256,                  		 // Хэндл открытого ключа
			                          &hPrivateKey_256);                    	 // Хэндл закрытого ключа
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");
 
printf("Generating 512 bit key pair");
rv = pFunctionList->C_GenerateKeyPair(hSession,                             	 // Хэндл открытой сессии
			                          &gostR3410_512KeyPairGenMech,              // Используемый механизм генерации ключевой пары 
				                      GOST34_10_2012_512_PublicKey,              // Шаблон открытого ключа
			                          arraysize(GOST34_10_2012_512_PublicKey),   // Размер шаблона открытого ключа
			                          GOST34_10_2012_512_PrivateKey,             // Шаблон закрытого ключа 
			                          arraysize(GOST34_10_2012_512_PrivateKey),  // Размер шаблона закрытого ключа
			                          &hPublicKey_512,                  		 // Хэндл открытого ключа
			                          &hPrivateKey_512);                    	 // Хэндл закрытого ключа
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");


Генерация секретного ключа

Атрибуты ключевых объектов

Все поддерживаемые устройствами Рутокен атрибуты объектов представлены в разделе Объекты секретных ключей

Поддерживаемые типы ключей

Устройства Рутокен поддерживают следующие типы секретных ключей (CK_KEY_TYPE

  • CKK_GENERIC_SECRET для абстрактных ключей произвольной длины,

  • CKK_GOST28147 для ключей ГОСТ 28147-89.

Примеры шаблона секретного ключа

Шаблон секретного ключа ГОСТ 28147-89
CK_OBJECT_CLASS ocSecKey 		= CKO_SECRET_KEY; 
CK_UTF8CHAR 	SecKeyLabel[] 	= {"GOST Secret Key"}; 
CK_BYTE 		SecKeyID[] 		= {"GOST Secret Key"}; 
CK_KEY_TYPE 	KeyType 		= CKK_GOST28147; 
CK_BBOOL 		bTrue 			= CK_TRUE; 
CK_BBOOL 		bFalse 			= CK_FALSE; 
 
/* Набор параметров КриптоПро A алгоритма ГОСТ 28147-89 */
CK_BYTE 	GOST28147params[]	= { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01 }; 
  
CK_ATTRIBUTE attrGOST28147_89SecKey[] =
{
	{ CKA_CLASS, &ocSeckey, sizeof(ocSeckey)},              // Объект секретного ключа ГОСТ 28147-89
	{ CKA_LABEL, &SecKeyLabel, sizeof(SecKeyLabel) - 1},    // Метка ключа
	{ CKA_ID, &SecKeyID, sizeof(SecKeyID) - 1},             // Идентификатор ключа
	{ CKA_KEY_TYPE, &KeyType, sizeof(KeyType)},       		// Тип ключа
	{ CKA_ENCRYPT, &bTrue, sizeof(bTrue)},                  // Ключ предназначен для зашифрования
	{ CKA_DECRYPT, &bTrue, sizeof(bTrue)},                  // Ключ предназначен для расшифрования
	{ CKA_TOKEN, &bTrue, sizeof(bTrue)},                    // Ключ является объектом токена
	{ CKA_PRIVATE, &bFalse, sizeof(bFalse)},                // Ключ доступен без авторизации на токене
	{ CKA_GOST28147_PARAMS, GOST28147params, sizeof(GOST28147params)} // Параметры алгоритма 
};

Поддерживаемые механизмы генерации ключей

Устройства Рутокен поддерживают следующие механизмы генерации секретного ключа:

  • CKM_GOST28147_KEY_GEN для генерации секретного ключа ГОСТ 28147-89 (библиотекой rtPKCS11ECP),
  • CKM_GOST_KEY_GEN для генерации секретного ключа ГОСТ 28147-89 (библиотекой rtPKCS11).

Пример генерации секретного ключа

Для генерации секретного ключа предназначена функция C_GenerateKey(), в которую передается механизм генерации и шаблон ключа.

Предварительно должна быть открыта сессия чтения/записи с авторизацией с правами Пользователя. 

Генерация симметричного ключа ГОСТ 28147-89
/* Вычисление размера массива */
#define 		 arraysize(a)   (sizeof(a)/sizeof(a[0]))
 
CK_MECHANISM     KeyGenMech	 = {CKM_GOST28147_KEY_GEN, NULL_PTR, 0}; // Генерация ключа ГОСТ 28147-89
 
CK_OBJECT_HANDLE hSecKey	 = NULL_PTR;    						// Хэндл cекретного ключа
 
...
printf("\n Generating key");
rv = pFunctionList->C_GenerateKey(hSession,                             // Хэндл открытой сессии
			                      &KeyGenMech,              			// Используемый механизм генерации ключа
			                      attrGOST28147_89SecKey,               // Шаблон для создания секретного ключа
			                      arraysize(attrGOST28147_89SecKey),    // Размер шаблона секретного ключа
			                      &hSecKey);                   			// Хэндл секретного ключа
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");

Выработка сеансового симметричного ключа 

Функция C_DeriveKey() позволяет получить одинаковый симметричный ключ для сеансовой связи на каждой из сторон, имея закрытый ключ одной стороны и открытый ключ другой стороны.

Устройства Рутокен поддерживают следующие механизмы согласования ключей:

  • CKM_GOSTR3410_DERIVE для алгоритма и ключей VKO GOST R 34.10-2001;
  • CKM_GOSTR3410_12_DERIVE для алгоритма и ключей VKO GOST R 34.10-2012.

Выработанный общий ключ согласно VKO GOST R 34.10-2001 может быть возвращен в одном из следующих форматов:

  • не диверсифицированный Key Encryption Key (KEK)
  • KEK, диверсифицированный по RFC-4357, п.6.5.  

Выработанный общий ключ согласно VKO GOST R  34.10-2012 может быть возвращен только в формате не диверсифицированного Key Encryption Key (KEK).

Для получения сеансового ключ (CEK), зашифрованного на KEK с имитовставкой CEK, необходимо после получения ключа функцией C_DeriveKey() использовать функцию маскирования C_WrapKey().

Для выработки сеансового ключ (CEK) в оперативной памяти токена, зашифрованного на KEK с имитовставкой CEK, необходимо использовать функцию расширения C_EX_WrapKey().

Параметры механизма согласования ключей задаются отдельной структурой типа CK_GOSTR3410_DERIVE_PARAMS, которая содержит:

  • флаг формата возвращаемого ключа (CKD_NULL или CKD_CPDIVERSIFY_KD),
  • значение открытого ключа второй стороны и его размер,
  • вектор синхронизации и его длину.

Пример выработки общего ключа парной связи по алгоритму VKO GOST R 34.10-2001 и VKO GOST R 34.10-2012

Выработка общего ключа парной связи по схеме ключевого обмена VKO GOST 34.10-2001 на стороне отправителя
/* Значение синхропосылки */
CK_BYTE cbUKM[] = {A9 3C 16 46 18 F0 31 F3};
 
/* Параметры механизма выработки общего ключа VKO GOST R 34.10-2001 */
CK_GOSTR3410_DERIVE_PARAMS ckDeriveParams	 = { CKD_CPDIVERSIFY_KDF,// Формат выходных данных: CKD_CPDIVERSIFY_KD - диверсифицированный KEK (для VKO GOST R 34.10-2001), 
																	// CKD_NULL - не диверсифицированный KEK
												NULL_PTR, 			// Значение открытого ключа
												0, 	 				// Длина открытого ключа в байтах	
												cbUKM,				// Вектор инициализации (синхропосылки)
												arraysize(cbUKM)};	// Длина синхропосылки в байтах (от 8 байт)

/* Механизм выработки общего ключа */
CK_MECHANISM    ckmDerivationMech2001         = {CKM_GOSTR3410_DERIVE, NULL_PTR, 0}; 
CK_MECHANISM    ckmDerivationMech2012         = {CKM_GOSTR3410_12_DERIVE, NULL_PTR, 0}; 
 
/* Значение открытого ключа получателя */
CK_BYTE cbPubRecipientKey[] = { FF 8D AB 7F 1C 0B 74 A5 AD 7F 0B 5F 8D 5B 3C 44 
							    58 37 98 C9 25 86 40 7E EC 6E AF 00 CB 44 65 A5 
							    22 9A 53 56 32 97 35 80 99 CA 1E 17 21 3A 96 0E 
						        21 FB C6 0F 25 5B 5D 99 4E C4 5C 42 08 7D 06 04 };
 
CK_OBJECT_CLASS ocSecKey 			= CKO_SECRET_KEY; 
CK_UTF8CHAR 	DerivedKeyLabel[] 	= {"Derived Key"}; 
CK_BYTE 		SecKeyID[] 			= {"GOST Secret Key"}; 
CK_KEY_TYPE 	KeyType 			= CKK_GOST28147; 
CK_BBOOL 		bTrue 				= CK_TRUE; 
CK_BBOOL 		bFalse 				= CK_FALSE; 

/* Шаблон для создания общего ключа */
CK_ATTRIBUTE attrGOST28147DerivedKey[] =
{
	{ CKA_CLASS, &ocSeckey, sizeof(ocSeckey)},                      // Объект секретного ключа ГОСТ 28147-89
	{ CKA_LABEL, &DerivedKeyLabel, sizeof(DerivedKeyLabel) - 1},    // Метка ключа
	{ CKA_KEY_TYPE, &KeyType, sizeof(KeyType)},               		// Тип ключа
	{ CKA_TOKEN, &bFalse, sizeof(bFalse)},                          // Ключ является объектом сессии
	{ CKA_MODIFIABLE, &bTrue, sizeof(bTrue)},                       // Ключ может быть изменен после создания
	{ CKA_PRIVATE, &bFalse, sizeof(bFalse)},                        // Ключ доступен без авторизации
	{ CKA_EXTRACTABLE, &bTrue, sizeof(bTrue)},                      // Ключ может быть извлечен и зашифрован
	{ CKA_SENSITIVE, &bFalse, sizeof(bFalse)}                       // Ключ не может быть извлечен в открытом виде
};
 
CK_ATTRIBUTE 		attrDerivedKeyValue = {CKA_VALUE, NULL_PTR, 0}; // Структура данных типа CK_ATTRIBUTE для хранения значения атрибута CKA_VALUE
CK_OBJECT_HANDLE 	hDerivedKey_1 = NULL_PTR;           			// Хэндл выработанного на стороне отправителя общего ключа
CK_OBJECT_HANDLE 	hObject,               							// Хэндл объекта
while(TRUE)
{
	...
		
	/* Выделить необходимое количество памяти для значения атрибута */
	ckDeriveParams.pPublicData = (CK_BYTE*)malloc(ckDeriveParams.ulPublicDataLen);
	if (ckDeriveParams.pPublicData == NULL)
	{
		printf("Memory allocation for aSlots failed! \n");
		break;
	}
	memset(ckDeriveParams.pPublicData,
	       0,
	       (ckDeriveParams.ulPublicDataLen * sizeof(CK_BYTE)))

	/* Поместить в структуру типа CK_GOSTR3410_DERIVE_PARAMS открытый ключ получателя */
	ckDeriveParams.pPublicData = &cbPubRecipientKey;
	ckDeriveParams.ulPublicDataLen = arraysize(cbPubRecipientKey);

 
	/* Поместить в структуру типа CK_MECHANISM параметры, необходимые для выработки общего ключа */
	ckmDerivationMech.pParameter = &ckDeriveParams;
	ckmDerivationMech.ulParameterLen = sizeof(ckDeriveParams);
 
	/* Выработать общий ключ ГОСТ 28147-89 на основании закрытого ключа отправителя и открытого ключа получателя */
	printf("C_DeriveKey");
	rv = pFunctionList->C_DeriveKey(hSession,							// Хэндл открытой с правами Пользователя сессии 
	                                &ckmDerivationMech2001,				// Механизм ключевого обмена
	                                hPrivateKey,						// Хэндл закрытого ключа отправителя
	                                attrGOST28147DerivedKey,			// Шаблон создания общего ключа
	                                arraysize(attrGOST28147DerivedKey), // Размер шаблона
	                                &hDerivedKey_1);					// Хэндл общего выработанного ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	
	/* Получить размер буфера для хранения значения атрибута CKA_VALUE*/
	printf("Getting object value size");
	rv = pFunctionList->C_GetAttributeValue(hSession,				// Хэндл открытой с правами Пользователя сессии 
	                                        hDerivedKey_1,			// Хэндл общего ключа
	                                        &attrDerivedKeyValue,	// Шаблон получения значения атрибута
	                                        1);						// Количество атрибутов в шаблоне
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

 
	/* Выделить необходимое количество памяти для значения атрибута */
	attrDerivedKeyValue.pValue = (CK_BYTE*)malloc(attrDerivedKeyValue.ulValueLen);
	if (attrDerivedKeyValue.pValue == NULL)
	{
		printf("Memory allocation for attrDerivedKeyValue failed! \n");
		break;
	}
	memset(attrDerivedKeyValue.pValue,
	       0,
	       (attrDerivedKeyValue.ulValueLen * sizeof(CK_BYTE)));

 
	/* Получить значение общего ключа ГОСТ 28147-89 */
	printf("Getting object value");
	rv = pFunctionList->C_GetAttributeValue(hSession,				// Хэндл открытой с правами Пользователя сессии 
	                                        hDerivedKey_1,			// Хэндл общего ключа
	                                        &attrDerivedKeyValue,	// Шаблон получения значения атрибута
	                                        1);						// Количество атрибутов в шаблоне
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Распечатать буфер со значением общего ключа ГОСТ 28147-89 */
	printf("Derived key data is:\n");
	for (i = 0;
	     i < attrDerivedKeyValue.ulValueLen;
	     i++)
	{
		printf("%02X ",  attrDerivedKeyValue.pValue[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");
	}
	break;
}

if (ckDeriveParams.pPublicData)
{
	free(ckDeriveParams.pPublicData);
	ckDeriveParams.pPublicData = NULL_PTR;
	ckDeriveParams.ulPublicDataLen = 0;
}

if (attrDerivedKeyValue.pValue)
{
	free(attrDerivedKeyValue.pValue);
	attrDerivedKeyValue.pValue = NULL_PTR;
	attrDerivedKeyValue.ulValueLen= 0;
}

if (rv != CKR_OK)
{
	pFunctionList->C_DestroyObject(hSession,
	                               hDerivedKey_1);
	hDerivedKey_1 = NULL_PTR;
}
if (rv != CKR_OK)
	printf("\nDeriving failed!\n\n");
else
	printf("Deriving has been completed successfully.\n\n");
		

Маскирование секретного ключа

Для маскирования (шифрования) симметричного ключа используются функция C_WrapKey() для маскирования и C_UnwrapKey() для обратной процедуры. 

С помощью функции маскирования вычисляется значение сессионного ключа (CEK) для алгоритмов VKO GOST R 34.10-2001 и VKO GOST R 34.10-2012. В качестве значения сессионного ключа для маскирования используется случайно сгенерированная строка. Затем сессионный ключ шифруется (маскируется) полученным с помощью функции C_Derive() общим ключом KEK.

Маскирование общего выработанного ключа
/* Размер симметричного ключа ГОСТ 28147-89 в байтах */
#define     GOST_28147_KEY_SIZE     0x20
 
/* Набор параметров КриптоПро A алгоритма ГОСТ 28147-89 */
CK_BYTE 	GOST28147params[]	= { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01 }; 
 
/* Параметры для механизма маскирования/демаскирования */
CK_GOSTR3410_KEY_WRAP_PARAMS  ckWrapParams = { GOST28147params;			// Указатель параметры (OID) алгоритма ГОСТ 28147-89 (используется только для C_WrapKey) 
																		// Если NULL_PTR, то используется CKA_GOSTR3411PARAMS. Для C_UnwrapKey должно быть NULL_PTR
 											   sizeof(GOST28147params);	// Длина объектного идентификатора
 											   cbUKM;					// Вектор инициализации (синхропосылки), если NULL_PTR -- используется случайное значение длиной 8 байт
 											   arraysize(cbUKM);		// Длина синхропосылки (в байтах, кратно 8)
 											   hKey;					// Хэндл ключа 
}
 
/* Механизм для маскирования/демаскирования ключа */
CK_MECHANISM    ckmWrapMech                 = {CKM_GOST28147_KEY_WRAP, NULL_PTR, 0};	
 
CK_OBJECT_CLASS ocSecKey 		= CKO_SECRET_KEY; 
CK_UTF8CHAR 	WrapKeyLabel[] 	= {"GOST Wrapped Key"}; 
CK_UTF8CHAR 	UnWrapKeyLabel[]= {"GOST Unwrapped Key"}; 
CK_KEY_TYPE 	KeyType 		= CKK_GOST28147; 
CK_BBOOL 		bTrue 			= CK_TRUE; 
CK_BBOOL 		bFalse 			= CK_FALSE; 

 
/* Шаблон маскируемого ключа */
CK_ATTRIBUTE attrGOST28147KeyToWrap[] =
{
	{ CKA_CLASS, &ocSeckey, sizeof(ocSeckey)},              // Объект секретного ключа ГОСТ 28147-89
	{ CKA_LABEL, &WrapKeyLabel, sizeof(WrapKeyLabel) - 1 },	// Метка ключа
	{ CKA_KEY_TYPE, &KeyType, sizeof(KeyType)},       		// Тип ключа
	{ CKA_TOKEN, &bFalse, sizeof(bFalse)},                  // Ключ является объектом сессии
	{ CKA_MODIFIABLE, &bTrue, sizeof(bTrue)},               // Ключ может быть изменен после создания
	{ CKA_PRIVATE, &bFalse, sizeof(bFalse)},                // Ключ доступен без авторизации
	{ CKA_VALUE, NULL_PTR, 0},                              // Значение ключа
	{ CKA_EXTRACTABLE, &bTrue, sizeof(bTrue)},              // Ключ может быть извлечен и зашифрован
	{ CKA_SENSITIVE, &bFalse, sizeof(bFalse)}               // Ключ не может быть извлечен в открытом виде
};

/* Шаблон демаскированного ключа */
CK_ATTRIBUTE attrGOST28147UnwrappedKey[] =
{
	{ CKA_CLASS, &ocSeckey, sizeof(ocSeckey)},                    // Объект секретного ключа ГОСТ 28147-89
	{ CKA_LABEL, &UnWrapLabelGOST, sizeof(UnWrapLabelGOST) - 1},  // Метка ключа
	{ CKA_KEY_TYPE, &KeyType, sizeof(KeyType)},                   // Тип ключа
	{ CKA_TOKEN, &bFalse, sizeof(bFalse)},                        // Ключ является объектом сессии
	{ CKA_MODIFIABLE, &bTrue, sizeof(bTrue)},                     // Ключ может быть изменен после создания
	{ CKA_PRIVATE, &bFalse, sizeof(bFalse)},                      // Ключ доступен без авторизации
	{ CKA_EXTRACTABLE, &bTrue, sizeof(bTrue)},                    // Ключ может быть извлечен и зашифрован
	{ CKA_SENSITIVE, &bFalse, sizeof(bFalse)}                     // Ключ не может быть извлечен в открытом виде
};
 
/* Структура данных типа CK_ATTRIBUTE для хранения значения атрибута CKA_VALUE */
CK_ATTRIBUTE attrValue = {CKA_VALUE, NULL_PTR, 0};  
        
CK_OBJECT_HANDLE hDerivedKey_2;           			 // Хэндл выработанного на стороне получателя общего ключа
CK_BYTE_PTR pbtSessionKey = NULL_PTR;                // Указатель на буфер, содержащий сессионный ключ
CK_BYTE_PTR pbtWrappedKey = NULL_PTR;                // Указатель на буфер, содержащий маскированный на стороне отправителя сессионный ключ
CK_ULONG ulWrappedKeySize = 0;                       // Размер буфера со значением маскированного на стороне отправителя сессионного ключа, в байтах
CK_BYTE_PTR pbtUnwrappedKey = NULL_PTR;              // Указатель на буфер, содержащий демаскированный на стороне получателя сессионный ключ
CK_ULONG ulUnwrappedKeySize = 0;                     // Размер буфера со значением демаскированного на стороне получателя сессионного ключа, в байтах
CK_OBJECT_HANDLE hTempKey = NULL_PTR;                // Хэндл ключа, который будет маскироваться/демаскироваться


while(TRUE)
{
	...
	/*Установить параметры в структуре типа CK_MECHANISM для маскирования ключа*/
	ckmWrapMech.ulParameterLen = 8;
	GenerateRandomData(ckmWrapMech.ulParameterLen, (PBYTE *)&ckmWrapMech.pParameter);
		
	/* Заполнить шаблон сессионного ключа случайными данными */
	GenerateRandomData(GOST_28147_KEY_SIZE, &pbtSessionKey);
	for (i = 0;
		 i < arraysize(attrGOST28147KeyToWrap);
		 i++)
		if (attrGOST28147KeyToWrap[i].type == CKA_VALUE)
		{
			attrGOST28147KeyToWrap[i].pValue = pbtSessionKey;
			attrGOST28147KeyToWrap[i].ulValueLen = GOST_28147_KEY_SIZE;
			break;
		}
	while(TRUE)
	{
		/* Создать ключ, который будет маскирован */
		printf("Creating the GOST 28147-89 key to wrap");
		rv = pFunctionList->C_CreateObject(hSession,							// Хэндл сессии, открытой с правами Пользователя
				                           attrGOST28147KeyToWrap,				// Шаблон создаваемого ключа
				                           arraysize(attrGOST28147KeyToWrap),	// Размер шаблона
				                           &hTempKey);							// Хэндл созданного ключа
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			break;
		}
		printf(" -> OK\n");
 
		/* Получить размер буфера, содержащего значение маскированного ключа */
		printf("Defining wrapping key size");
		rv = pFunctionList->C_WrapKey(hSession,					// Хэндл сессии, открытой с правами Пользователя		
				                      &ckmWrapMech,				// Механизм маскирования
				                      hDerivedKey_1,			// Хэндл ключа, которым будет маскироваться ключ
				                      hTempKey,					// Хэндл ключа, который будет маскирован
				                      NULL_PTR,					// Указатель на буфер с маскированным ключом
				                      &ulWrappedKeySize);		// Размер маскированного ключа
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			break;
		}
		printf(" -> OK\n");
 
		pbtWrappedKey = (CK_BYTE*)malloc(ulWrappedKeySize);
		if (pbtWrappedKey == NULL)
		{
			printf("Memory allocation for pbtWrappedKey failed! \n");
			break;
		}		
		memset(pbtWrappedKey,
		 	  0,
		   	  ulWrappedKeySize * sizeof(CK_BYTE));
	
		/* Получить маскированный ключ на стороне отправителя */
		printf("Wrapping key");
		rv = pFunctionList->C_WrapKey(hSession,					// Хэндл сессии, открытой с правами Пользователя		
				                 	  &ckmWrapMech,				// Механизм маскирования
				                      hDerivedKey_1,			// Хэндл ключа, которым будет маскироваться ключ
				                      hTempKey,					// Хэндл ключа, который будет маскирован
				                      pbtWrappedKey,			// Указатель на буфер с маскированным ключом
				                      &ulWrappedKeySize);		// Размер маскированного ключа
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			break;
		}
		printf(" -> OK\n");
 
		/* Распечатать буфер, содержащий маскированный ключ */
		printf("Wrapped key data is:\n");
		for (i = 0;
			 i < ulWrappedKeySize;
	   		 i++)
		{
			printf("%02X ", pbtWrappedKey[i]);
			if ((i + 1) % 9 == 0)
				printf("\n");
		}
		break;
	}
 
	if (hTempKey)
	{
		pFunctionList->C_DestroyObject(hSession,
			                           hTempKey);
		hTempKey = NULL_PTR;
	}
		
	if (rv == CKR_OK)
		printf("\nWrapping has been completed successfully.\n");
	else
	{
		printf("\nWrapping failed!\n");
		break;
	}
 
	while (TRUE)
	{
		/* Демаскировать ключ на стороне получателя*/
		printf("Unwrapping key");
		rv = pFunctionList->C_UnwrapKey(hSession,								// Хэндл сессии, открытой с правами Пользователя	
			                            &ckmWrapMech,							// Механизм маскирования
			                            hDerivedKey_2,							// Хэндл ключа, которым был маскирован ключ
			                            pbtWrappedKey,							// Указатель на буфер с маскированным ключом
			                            ulWrappedKeySize,						// Размер буфера с маскированным ключом
			                            attrGOST28147UnwrappedKey,				// Указатель на шаблон для демаскированного ключа
			                            arraysize(attrGOST28147UnwrappedKey),	// Размер шаблона для демаскированного ключа
			                            &hTempKey);								// Указатель на буфер с маскированным ключом
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			break;
		}
		printf(" -> OK\n");
			
		/* Получить буфер со значением демаскированного ключа */
		printf("Getting unwrapped key value...\n");
		
		/* Получить размер буфера для хранения значения атрибута CKA_VALUE */
		printf("Getting object value size");
		rv = pFunctionList->C_GetAttributeValue(hSession,   // Хэндл сессии, открытой с правами Пользователя
		                                        hTempKey,	// Хэндл объекта, значение атрибутов которых требуется получить 
		                                        &attrValue, // Указатель на шаблон с атрибутами, значение которых требуется получить
		                                        1);			// Количество строк в шаблоне
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			break;
		}
		printf(" -> OK\n");

		/* Выделить необходимое количество памяти для значения атрибута */
		attrValue.pValue = (CK_BYTE*)malloc(attrValue.ulValueLen);
		if (attrValue.pValue== NULL)
		{
			printf("Memory allocation for attrValue failed! \n");
			break;
		}		
		memset(attrValue.pValue,
		       0,
		       (attrValue.ulValueLen * sizeof(CK_BYTE)));
		
		/* Получить значение атрибута CKA_VALUE */
		printf("Getting object value");
		rv = pFunctionList->C_GetAttributeValue(hSession,   // Хэндл сессии, открытой с правами Пользователя
		                                        hTempKey,	// Хэндл объекта, значение атрибутов которых требуется получить 
		                                        &attrValue, // Указатель на шаблон с атрибутами, значение которых требуется получить
		                                        1);			// Количество строк в шаблоне
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			break;
		}
		printf(" -> OK\n");

		/* Распечатать буфер со значением демаскированного ключа  */
		printf("Unwrapped key data:\n");
		for (i = 0;
		     i < attrValue.ulValueLen;
		     i++)
		{
			printf("%02X ", attrValue.pValue[i]);
			if ((i + 1) % 8 == 0)
				printf("\n");
		}
		break;
	}
	if (hTempKey)
	{
		pFunctionList->C_DestroyObject(hSession,
			                           hTempKey);
		hTempKey = NULL_PTR;
	}
	if (rv == CKR_OK)
		printf("Unwrapping has been completed successfully.\n\n");
	else
	{
		printf("\nUnwrapping failed!\n\n");
		break;
	}
 
	/* Сравнить первоначальное значение сессионного ключа со значением демаскированного ключа */
	if ((ulUnwrappedKeySize != GOST_28147_KEY_SIZE)
		|| (memcmp(pbtSessionKey,
		           attrValue.pValue,
		           GOST_28147_KEY_SIZE) != 0))
		printf("\nThe unwrapped key is not equal to the session key!\n");
	else
		printf("The unwrapped key is equal to the session key.\n");
	break;
}

Вычисление значения хеш-функции

Поддерживаемые механизмы

Устройства Рутокен поддерживают следующие механизмы хеширования:

  • CKM_MD2 для хеширования алгоритмом MD2 (только программно),
  • CKM_MD5 для хеширования алгоритмом MD5 (только программно),
  • CKM_SHA_1 для хеширования алгоритмом SHA-1 (только программно),
  • CKM_GOSTR3411 для хеширования алгоритмом ГОСТ Р 34.11.94 (программно и аппаратно),
  • CKM_GOSTR3411_12_256 для хеширования алгоритмом ГОСТ Р 34.11.2012 с длиной значения 256 бит (только аппаратно),
  • CKM_GOSTR3411_12_512 для хеширования алгоритмом ГОСТ Р 34.11.2012 с длиной закрытого ключа 512 бит (только аппаратно).

Хеширование данных 

Для хеширования данных служат функции C_DigestInit() и C_Digest(). Сначала операцию хеширования нужно инициализировать через C_DigestInit(), передав в нее идентификатор сессии и ссылку на механизм хеширования. Затем размер буфера хешированных данных можно определить, вызвав C_Digest(), и выполнить хеширование данных, вызвав C_Digest() второй раз.

Предварительно должна быть открыта сессия чтения/записи. 

Пример хеширования данных по алгоритму ГОСТ Р 34.11-94 и ГОСТ Р 34.11-2012

Хеширование данных по алгоритму ГОСТ Р 34.11-94 / ГОСТ Р 34.11-2012
/* Данные для хеширования в виде двоичной строки */
CK_BYTE pbtData[] = { 0x3C, 0x21, 0x50, 0x49, 0x4E, 0x50, 0x41, 0x44, 0x46, 0x49, 0x4C, 0x45, 0x20, 0x52, 0x55, 0x3E, 
					  0x3C, 0x21, 0x3E, 0xED, 0xE5, 0xE2, 0xE8, 0xE4, 0xE8, 0xEC, 0xFB, 0xE9, 0x20, 0xF2, 0xE5, 0xEA };

/*  Механизм хеширования ГОСТ Р 34.11-94 */
CK_MECHANISM gostR3411_946HashMech = {CKM_GOSTR3411, NULL_PTR, 0}; 
/*  Механизм хеширования ГОСТ Р 34.11-2012(256) */
CK_MECHANISM gostR3411_12_256HashMech = {CKM_GOSTR3411_12_256, NULL_PTR, 0}; 
/*  Механизм хеширования ГОСТ Р 34.11-2012(512) */
CK_MECHANISM gostR3411_12_512HashMech = {CKM_GOSTR3411_12_512, NULL_PTR, 0 };
 
CK_BYTE_PTR pbtHash 	= NULL_PTR;            // Указатель на буфер для значения хеша данных
CK_ULONG 	ulHashSize 	= 0;                   // Размер буфера в байтах
 
while(TRUE)
{
	...
 
	/* Инициализировать операцию хеширования */
	printf("C_DigestInit");
	rv = pFunctionList->C_DigestInit(hSession,					// Хэндл сессии
								 	 &gostR3411_946HashMech );	// Механизм хеширования: необходимо выбрать соответствующий из
																// gostR3411_946HashMech, gostR3411_12_256HashMech или gostR3411_12_512HashMech 
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Определить размер значения хеша данных */
	printf("C_Digest step 1");
	rv = pFunctionList->C_Digest( hSession,				// Хэндл сессии
								  pbtData,				// Буфер с данными для хеширования
								  arraysize(pbtData),	// Размер данных для хеширования
								  pbtHash,				// Буфер для вычисленного значения хеша
								  &ulHashSize);			// Размер значения хеша
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtHash = (CK_BYTE*)malloc(ulHashSize);
	if (pbtHash == NULL)
	{
		printf("Memory allocation for pbtHash failed! \n");
		break;
	}
	memset(pbtHash,
		   0,
		   (ulHashSize * sizeof(CK_BYTE)));
	/* Сформировать хеш от исходных данных */
	printf("C_Digest step 2");
	rv = pFunctionList->C_Digest(hSession,				// Хэндл сессии
								 pbtData,				// Буфер с данными для хеширования
								 arraysize(pbtData),	// Размер данных для хеширования
								 pbtHash,				// Буфер для вычисленного значения хеша
								 &ulHashSize);			// Размер значения хеша
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	break;
}
 

Подпись и проверка подписи

Поддерживаемые механизмы

Устройства Рутокен поддерживают следующие механизмы подписи:

  • CKM_GOSTR3410 подписи алгоритмом ГОСТ Р 34.10.2001 и ГОСТ Р 34.10.2012 с длиной закрытого ключа 256 бит,
  • CKM_GOSTR3410_WITH_GOSTR3411 для совместного хеширования алгоритмом CKM_GOSTR3411 и подписи алгоритмом CKM_GOSTR3410,
  • CKM_GOSTR3410_512 для подписи алгоритмом ГОСТ Р 34.10.2012 с длиной закрытого ключа 512 бит,
  • CKM_GOSTR3410_WITH_GOSTR3411_12_256 для совместного хеширования алгоритмом CKM_GOSTR3411_12_256 и подписи на ключе длиной 256 бит,

  • CKM_GOSTR3410_WITH_GOSTR3411_12_512 для совместного хеширования алгоритмом  CKM_GOSTR3411_12_512 и подписи на ключе длиной 512 бит,

  • CKM_RSA_PKCS для подписи алгоритмом RSA.

Форматы отображения данных Рутокен PINPad (подпись в формате PKCS#7/CMS)

Если у закрытого ключа, которым подписываются данные, выставлен флаг визуализации, перед подписью Рутокен PINPad выведет на экран подписываемые данные для проверки и будет ожидать подтверждения или отмены операции. Чтобы Рутокен PINPad мог отобразить подписываемые данные, они должны быть представлены в одном из следующих форматов.

Формат PINPADFILE (стандартная таблица разметки)

Данные должны начинаться с заголовка <!PINPADFILE RU><!PINPADFILE UTF8><!PINPADFILE INVISIBLE RU> или <!PINPADFILE INVISIBLE UTF8>

Управляющий заголовок документаЗначение
<!PINPADFILE RU>
обязательный заголовок строки в кодировке CP-1251, которая будет распознаваться Рутокен PINPad и отображаться на экране устройства
<!PINPADFILE UTF8>
обязательный заголовок строки в кодировке UTF-8, которая будет распознаваться Рутокен PINPad и отображаться на экране устройства
<!PINPADFILE INVISIBLE RU>
обязательный заголовок строки в кодировке CP-1251, которая будет подписана PINPad без отображения на экране устройства
<!PINPADFILE INVISIBLE UTF8>
обязательный заголовок строки в кодировке UTF-8, которая будет подписана PINPad без отображения на экране устройства

Для данных, начинающихся с заголовков <!PINPADFILE INVISIBLE RU> или <!PINPADFILE INVISIBLE UTF8>, дальнейшая разметка документа не предусмотрена. Документ может содержаться любые данные, в том числе бинарные. Подпись таких данных может быть выполнена только в автоматическом режиме без отображения на экране устройства.
Дальнейшая разметка всего отображаемого документа осуществляется метками 
<N><V> и <T> в соответствии со следующей таблицей:

 Управляющая меткаЗначение
<N>some_text 

наименование поля текст будет отображен в левой части строки на экране PINPad

<V>some_text 

значение поля – текст будет отображен в правой части части строки на экране PINPad

<T>some_text 
информационное поле – текст будет отображен на всей строке на экране PINPad
*some_text – текстовые данные для форматирования, при наличии непечатных символов будет возвращена ошибка
Количество строк в документе (пар значений <N>, <V> и значений <T>) не может превышать 230, число символов в одной строке не может превышать 50.

Пример сообщения в формате PINPADFILE 

Исходное платежное поручение в текстовом виде
ФИО: Петров Петр Петрович Москва, Пионерская ул, д. 3, кв. 72
Перевод со счета : 42301810001000075212
Сумма : 150000
Валюта : RUR
Наименование получателя : Иванова Елена Ивановна
Номер счета получателя : 40817810338295201618
БИК банка получателя : 044525225
Наименование банка получателя : ОАО 'СБЕРБАНК РОССИИ' Г. МОСКВА
Номер счета банка получателя : 30101810400000000225
Назначение платежа : перевод личных средств
Платежное поручение в формате PINPADFILE
<!PINPADFILE RU>
<N>ФИО:<V>Петров Петр Петрович Москва, Пионерская ул, д. 3, кв. 72
<N>Перевод со счета:<V>42301810001000075212
<N>Сумма:<V>150000
<N>Валюта:<V>RUR
<N>Наименование получателя:<V>Иванова Елена Ивановна
<N>Номер счета получателя:<V>40817810338295201618
<N>БИК банка получателя:<V>044525225
<N>Наименование банка получателя:<V>ОАО 'СБЕРБАНК РОССИИ' Г. МОСКВА
<N>Номер счета банка получателя:<V>30101810400000000225
<N>Назначение платежа:<V>перевод личных средств

Чтобы данные были корректно распознаны Рутокен PINPad, они должны быть представлены в виде строки с однобайтовыми символами, соответствующими кодировке Windows-1251 или UTF-8.

Данные в формате PINPADFILE для подписи с отображением в кодировке Windows-1251
char PINPADFILECP1251[] = 	"<!PINPADFILE RU><N>ФИО:<V>Петров Петр Петрович Москва, Пионерская ул, д. 3, кв. 72"\
	                		"<N>Перевод со счета:<V>42301810001000075212<N>Сумма:<V>150000<N>Валюта:<V>RUR"\
							"<N>Наименование получателя:<V>Иванова Елена Ивановна<N>Номер счета получателя:<V>40817810338295201618"\
							"<N>БИК банка получателя:<V>044525225<N>Наименование банка получателя:<V>ОАО 'СБЕРБАНК РОССИИ' Г. МОСКВА"\
							"<N>Номер счета банка получателя:<V>30101810400000000225<N>Назначение платежа:<V>перевод личных средств";
Платежное поручение в распознаваемом Рутокен PINPad виде (UTF-8 кодировка)
char PINPADFILEUTF8[] = "<!PINPADFILE UTF8><N>ФИО:<V>Петров Петр Петрович Москва, Пионерская ул, д. 3, кв. 72"\
						"<N>Перевод со счета:<V>42301810001000075212<N>Сумма:<V>150000<N>Валюта:<V>RUR"\
						"<N>Наименование получателя:<V>Иванова Елена Ивановна<N>Номер счета получателя:<V>40817810338295201618"\
						"<N>БИК банка получателя:<V>044525225<N>Наименование банка получателя:<V>ОАО 'СБЕРБАНК РОССИИ' Г. МОСКВА"\
						"<N>Номер счета банка получателя:<V>30101810400000000225<N>Назначение платежа:<V>перевод личных средств";
Данные в формате PINPADFILE для подписи без отображения в кодировке Windows-1251
char InvisibleCP1251[] = "<!PINPADFILE INVISIBLE RU>ФИО: Петров Петр Петрович Москва, Пионерская ул, д. 3, кв. 72"\
	                	"Перевод со счета: 42301810001000075212 Сумма: 150000 Валюта: RUR Наименование получателя: "\
	                	"Иванова Елена Ивановна Номер счета получателя: 40817810338295201618 БИК банка получателя: 044525225 "\
	                	"Наименование банка получателя: ОАО 'СБЕРБАНК РОССИИ' Г. МОСКВА Номер счета банка получателя: "\
	                	"30101810400000000225 Назначение платежа: перевод личных средств";
Данные в формате PINPADFILE для подписи без отображения в кодировке UTF-8
char InvisibleUTF8[] = "<!PINPADFILE INVISIBLE UTF8><N>ФИО:<V>Петров Петр Петрович Москва, Пионерская ул, д. 3, кв. 72"\
						"<N>Перевод со счета:<V>42301810001000075212<N>Сумма:<V>150000<N>Валюта:<V>RUR"\
						"<N>Наименование получателя:<V>Иванова Елена Ивановна<N>Номер счета получателя:<V>40817810338295201618"\
						"<N>БИК банка получателя:<V>044525225<N>Наименование банка получателя:<V>ОАО 'СБЕРБАНК РОССИИ' Г. МОСКВА"\
						"<N>Номер счета банка получателя:<V>30101810400000000225<N>Назначение платежа:<V

Данные также могут быть представлены в виде двоичной строки с кодами символов в Windows-1251 или UTF-8.

Данные в виде двоичной строки (PINPADFILE, Windows-1251)
char DataCP1251[] = { 0x3C, 0x21, 0x50, 0x49, 0x4E, 0x50, 0x41, 0x44, 0x46, 0x49, 0x4C, 0x45, 0x20, 0x52, 0x55, 0x3E,
					  0x3C, 0x4E, 0x3E, 0x0A, 0xD4, 0xC8, 0xCE, 0x3A, 0x3C, 0x56, 0x3E, 0xCF, 0xE5, 0xF2, 0xF0, 0xEE,
					  0xE2, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0xEE, 0xE2, 0xE8, 0xF7, 0x20,
					  0xCC, 0xEE, 0xF1, 0xEA, 0xE2, 0xE0, 0x2C, 0x20, 0xCF, 0xE8, 0xEE, 0xED, 0xE5, 0xF0, 0xF1, 0xEA,
					  0xE0, 0xFF, 0x20, 0xF3, 0xEB, 0x2C, 0x20, 0xE4, 0x2E, 0x20, 0x33, 0x2C, 0x20, 0xEA, 0xE2, 0x2E,
					  0x20, 0x37, 0x32, 0x2C, 0x20, 0xE2, 0xF2, 0xEE, 0xF0, 0xEE, 0xE9, 0x20, 0xF7, 0xE5, 0xF0, 0xE4,
					  0xE0, 0xEA, 0x0A, 0x3C, 0x4E, 0x3E, 0xCF, 0xE5, 0xF0, 0xE5, 0xE2, 0xEE, 0xE4, 0x20, 0xF1, 0xEE,
					  0x20, 0xF1, 0xF7, 0xE5, 0xF2, 0xE0, 0x3A, 0x3C, 0x56, 0x3E, 0x34, 0x32, 0x33, 0x30, 0x31, 0x38,
					  0x31, 0x30, 0x30, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x37, 0x35, 0x32, 0x31, 0x32, 0x0A, 0x3C, 
					  0x4E, 0x3E, 0xD1, 0xF3, 0xEC, 0xEC, 0xE0, 0x3A, 0x3C, 0x56, 0x3E, 0x31, 0x35, 0x30, 0x30, 0x30,
					  0x30, 0x0A, 0x3C, 0x4E, 0x3E, 0xC2, 0xE0, 0xEB, 0xFE, 0xF2, 0xE0, 0x3A, 0x3C, 0x56, 0x3E, 0x52,
					  0x55, 0x52, 0x0A, 0x3C, 0x4E, 0x3E, 0xCD, 0xE0, 0xE8, 0xEC, 0xE5, 0xED, 0xEE, 0xE2, 0xE0, 0xED, 
					  0xE8, 0xE5, 0x20, 0xEF, 0xEE, 0xEB, 0xF3, 0xF7, 0xE0, 0xF2, 0xE5, 0xEB, 0xFF, 0x3A, 0x3C, 0x56,
					  0x3E, 0xC8, 0xE2, 0xE0, 0xED, 0xEE, 0xE2, 0xE0, 0x20, 0xC5, 0xEB, 0xE5, 0xED, 0xE0, 0x20, 0xC8, 
					  0xE2, 0xE0, 0xED, 0xEE, 0xE2, 0xED, 0xE0, 0x0A, 0x3C, 0x4E, 0x3E, 0xCD, 0xEE, 0xEC, 0xE5, 0xF0,
					  0x20, 0xF1, 0xF7, 0xE5, 0xF2, 0xE0, 0x20, 0xEF, 0xEE, 0xEB, 0xF3, 0xF7, 0xE0, 0xF2, 0xE5, 0xEB,
					  0xFF, 0x3A, 0x3C, 0x56, 0x3E, 0x34, 0x30, 0x38, 0x31, 0x37, 0x38, 0x31, 0x30, 0x33, 0x33, 0x38,
					  0x32, 0x39, 0x35, 0x32, 0x30, 0x31, 0x36, 0x31, 0x38, 0x0A, 0x3C, 0x4E, 0x3E, 0xC1, 0xC8, 0xCA,
					  0x20, 0xE1, 0xE0, 0xED, 0xEA, 0xE0, 0x20, 0xEF, 0xEE, 0xEB, 0xF3, 0xF7, 0xE0, 0xF2, 0xE5, 0xEB,
					  0xFF, 0x3A, 0x3C, 0x56, 0x3E, 0x30, 0x34, 0x34, 0x35, 0x32, 0x35, 0x32, 0x32, 0x35, 0x0A, 0x3C,
					  0x4E, 0x3E, 0xCD, 0xE0, 0xE8, 0xEC, 0xE5, 0xED, 0xEE, 0xE2, 0xE0, 0xED, 0xE8, 0xE5, 0x20, 0xE1,
					  0xE0, 0xED, 0xEA, 0xE0, 0x20, 0xEF, 0xEE, 0xEB, 0xF3, 0xF7, 0xE0, 0xF2, 0xE5, 0xEB, 0xFF, 0x3A,
					  0x3C, 0x56, 0x3E, 0xCE, 0xC0, 0xCE, 0x20, 0x27, 0xD1, 0xC1, 0xC5, 0xD0, 0xC1, 0xC0, 0xCD, 0xCA,
					  0x20, 0xD0, 0xCE, 0xD1, 0xD1, 0xC8, 0xC8, 0x27, 0x20, 0xC3, 0x2E, 0x20, 0xCC, 0xCE, 0xD1, 0xCA,
					  0xC2, 0xC0, 0x0A, 0x3C, 0x4E, 0x3E, 0xCD, 0xEE, 0xEC, 0xE5, 0xF0, 0x20, 0xF1, 0xF7, 0xE5, 0xF2,
					  0xE0, 0x20, 0xE1, 0xE0, 0xED, 0xEA, 0xE0, 0x20, 0xEF, 0xEE, 0xEB, 0xF3, 0xF7, 0xE0, 0xF2, 0xE5,
					  0xEB, 0xFF, 0x3A, 0x3C, 0x56, 0x3E, 0x33, 0x30, 0x31, 0x30, 0x31, 0x38, 0x31, 0x30, 0x34, 0x30,
					  0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, 0x32, 0x35, 0x0A, 0x3C, 0x4E, 0x3E, 0xCD, 0xE0,
					  0xE7, 0xED, 0xE0, 0xF7, 0xE5, 0xED, 0xE8, 0xE5, 0x20, 0xEF, 0xEB, 0xE0, 0xF2, 0xE5, 0xE6, 0xE0,
					  0x3A, 0x3C, 0x56, 0x3E, 0xEF, 0xE5, 0xF0, 0xE5, 0xE2, 0xEE, 0xE4, 0x20, 0xEB, 0xE8, 0xF7, 0xED,
					  0xFB, 0xF5, 0x20, 0xF1, 0xF0, 0xE5, 0xE4, 0xF1, 0xF2, 0xE2 };

 

Данные в виде двоичной строки (PINPADFILE, UTF-8)
char DataUTF8[] = { 0x3C, 0x21, 0x50, 0x49, 0x4E, 0x50, 0x41, 0x44, 0x46, 0x49, 0x4C, 0x45, 0x20, 0x55, 0x54, 0x46, 
					0x38, 0x3E, 0x3C, 0x4E, 0x3E, 0xD0, 0xA4, 0xD0, 0x98, 0xD0, 0x9E, 0x3A, 0x3C, 0x56, 0x3E, 0xD0, 
					0x9F, 0xD0, 0xB5, 0xD1, 0x82, 0xD1, 0x80, 0xD0, 0xBE, 0xD0, 0xB2, 0x20, 0xD0, 0x9F, 0xD0, 0xB5, 
					0xD1, 0x82, 0xD1, 0x80, 0x20, 0xD0, 0x9F, 0xD0, 0xB5, 0xD1, 0x82, 0xD1, 0x80, 0xD0, 0xBE, 0xD0, 
					0xB2, 0xD0, 0xB8, 0xD1, 0x87, 0x20, 0xD0, 0x9C, 0xD0, 0xBE, 0xD1, 0x81, 0xD0, 0xBA, 0xD0, 0xB2, 
					0xD0, 0xB0, 0x2C, 0x20, 0xD0, 0x9F, 0xD0, 0xB8, 0xD0, 0xBE, 0xD0, 0xBD, 0xD0, 0xB5, 0xD1, 0x80, 
					0xD1, 0x81, 0xD0, 0xBA, 0xD0, 0xB0, 0xD1, 0x8F, 0x20, 0xD1, 0x83, 0xD0, 0xBB, 0x2C, 0x20, 0xD0, 
					0xB4, 0x2E, 0x20, 0x33, 0x2C, 0x20, 0xD0, 0xBA, 0xD0, 0xB2, 0x2E, 0x20, 0x37, 0x32, 0x3C, 0x4E, 
					0x3E, 0xD0, 0x9F, 0xD0, 0xB5, 0xD1, 0x80, 0xD0, 0xB5, 0xD0, 0xB2, 0xD0, 0xBE, 0xD0, 0xB4, 0x20, 
					0xD1, 0x81, 0xD0, 0xBE, 0x20, 0xD1, 0x81, 0xD1, 0x87, 0xD0, 0xB5, 0xD1, 0x82, 0xD0, 0xB0, 0x3A, 
					0x3C, 0x56, 0x3E, 0x34, 0x32, 0x33, 0x30, 0x31, 0x38, 0x31, 0x30, 0x30, 0x30, 0x31, 0x30, 0x30, 
					0x30, 0x30, 0x37, 0x35, 0x32, 0x31, 0x32, 0x3C, 0x4E, 0x3E, 0xD0, 0xA1, 0xD1, 0x83, 0xD0, 0xBC, 
					0xD0, 0xBC, 0xD0, 0xB0, 0x3A, 0x3C, 0x56, 0x3E, 0x31, 0x35, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x4E, 
					0x3E, 0xD0, 0x92, 0xD0, 0xB0, 0xD0, 0xBB, 0xD1, 0x8E, 0xD1, 0x82, 0xD0, 0xB0, 0x3A, 0x3C, 0x56, 
					0x3E, 0x52, 0x55, 0x52, 0x3C, 0x4E, 0x3E, 0xD0, 0x9D, 0xD0, 0xB0, 0xD0, 0xB8, 0xD0, 0xBC, 0xD0, 
					0xB5, 0xD0, 0xBD, 0xD0, 0xBE, 0xD0, 0xB2, 0xD0, 0xB0, 0xD0, 0xBD, 0xD0, 0xB8, 0xD0, 0xB5, 0x20, 
					0xD0, 0xBF, 0xD0, 0xBE, 0xD0, 0xBB, 0xD1, 0x83, 0xD1, 0x87, 0xD0, 0xB0, 0xD1, 0x82, 0xD0, 0xB5, 
					0xD0, 0xBB, 0xD1, 0x8F, 0x3A, 0x3C, 0x56, 0x3E, 0xD0, 0x98, 0xD0, 0xB2, 0xD0, 0xB0, 0xD0, 0xBD, 
					0xD0, 0xBE, 0xD0, 0xB2, 0xD0, 0xB0, 0x20, 0xD0, 0x95, 0xD0, 0xBB, 0xD0, 0xB5, 0xD0, 0xBD, 0xD0, 
					0xB0, 0x20, 0xD0, 0x98, 0xD0, 0xB2, 0xD0, 0xB0, 0xD0, 0xBD, 0xD0, 0xBE, 0xD0, 0xB2, 0xD0, 0xBD, 
					0xD0, 0xB0, 0x3C, 0x4E, 0x3E, 0xD0, 0x9D, 0xD0, 0xBE, 0xD0, 0xBC, 0xD0, 0xB5, 0xD1, 0x80, 0x20, 
					0xD1, 0x81, 0xD1, 0x87, 0xD0, 0xB5, 0xD1, 0x82, 0xD0, 0xB0, 0x20, 0xD0, 0xBF, 0xD0, 0xBE, 0xD0, 
					0xBB, 0xD1, 0x83, 0xD1, 0x87, 0xD0, 0xB0, 0xD1, 0x82, 0xD0, 0xB5, 0xD0, 0xBB, 0xD1, 0x8F, 0x3A, 
					0x3C, 0x56, 0x3E, 0x34, 0x30, 0x38, 0x31, 0x37, 0x38, 0x31, 0x30, 0x33, 0x33, 0x38, 0x32, 0x39, 
					0x35, 0x32, 0x30, 0x31, 0x36, 0x31, 0x38, 0x3C, 0x4E, 0x3E, 0xD0, 0x91, 0xD0, 0x98, 0xD0, 0x9A, 
					0x20, 0xD0, 0xB1, 0xD0, 0xB0, 0xD0, 0xBD, 0xD0, 0xBA, 0xD0, 0xB0, 0x20, 0xD0, 0xBF, 0xD0, 0xBE, 
					0xD0, 0xBB, 0xD1, 0x83, 0xD1, 0x87, 0xD0, 0xB0, 0xD1, 0x82, 0xD0, 0xB5, 0xD0, 0xBB, 0xD1, 0x8F, 
					0x3A, 0x3C, 0x56, 0x3E, 0x30, 0x34, 0x34, 0x35, 0x32, 0x35, 0x32, 0x32, 0x35, 0x3C, 0x4E, 0x3E, 
					0xD0, 0x9D, 0xD0, 0xB0, 0xD0, 0xB8, 0xD0, 0xBC, 0xD0, 0xB5, 0xD0, 0xBD, 0xD0, 0xBE, 0xD0, 0xB2, 
					0xD0, 0xB0, 0xD0, 0xBD, 0xD0, 0xB8, 0xD0, 0xB5, 0x20, 0xD0, 0xB1, 0xD0, 0xB0, 0xD0, 0xBD, 0xD0, 
					0xBA, 0xD0, 0xB0, 0x20, 0xD0, 0xBF, 0xD0, 0xBE, 0xD0, 0xBB, 0xD1, 0x83, 0xD1, 0x87, 0xD0, 0xB0, 
					0xD1, 0x82, 0xD0, 0xB5, 0xD0, 0xBB, 0xD1, 0x8F, 0x3A, 0x3C, 0x56, 0x3E, 0xD0, 0x9E, 0xD0, 0x90, 
					0xD0, 0x9E, 0x20, 0x27, 0xD0, 0xA1, 0xD0, 0x91, 0xD0, 0x95, 0xD0, 0xA0, 0xD0, 0x91, 0xD0, 0x90, 
					0xD0, 0x9D, 0xD0, 0x9A, 0x20, 0xD0, 0xA0, 0xD0, 0x9E, 0xD0, 0xA1, 0xD0, 0xA1, 0xD0, 0x98, 0xD0, 
					0x98, 0x27, 0x20, 0xD0, 0x93, 0x2E, 0x20, 0xD0, 0x9C, 0xD0, 0x9E, 0xD0, 0xA1, 0xD0, 0x9A, 0xD0, 
					0x92, 0xD0, 0x90, 0x3C, 0x4E, 0x3E, 0xD0, 0x9D, 0xD0, 0xBE, 0xD0, 0xBC, 0xD0, 0xB5, 0xD1, 0x80, 
					0x20, 0xD1, 0x81, 0xD1, 0x87, 0xD0, 0xB5, 0xD1, 0x82, 0xD0, 0xB0, 0x20, 0xD0, 0xB1, 0xD0, 0xB0, 
					0xD0, 0xBD, 0xD0, 0xBA, 0xD0, 0xB0, 0x20, 0xD0, 0xBF, 0xD0, 0xBE, 0xD0, 0xBB, 0xD1, 0x83, 0xD1, 
					0x87, 0xD0, 0xB0, 0xD1, 0x82, 0xD0, 0xB5, 0xD0, 0xBB, 0xD1, 0x8F, 0x3A, 0x3C, 0x56, 0x3E, 0x33, 
					0x30, 0x31, 0x30, 0x31, 0x38, 0x31, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 
					0x32, 0x32, 0x35, 0x3C, 0x4E, 0x3E, 0xD0, 0x9D, 0xD0, 0xB0, 0xD0, 0xB7, 0xD0, 0xBD, 0xD0, 0xB0, 
					0xD1, 0x87, 0xD0, 0xB5, 0xD0, 0xBD, 0xD0, 0xB8, 0xD0, 0xB5, 0x20, 0xD0, 0xBF, 0xD0, 0xBB, 0xD0, 
					0xB0, 0xD1, 0x82, 0xD0, 0xB5, 0xD0, 0xB6, 0xD0, 0xB0, 0x3A, 0x3C, 0x56, 0x3E, 0xD0, 0xBF, 0xD0, 
					0xB5, 0xD1, 0x80, 0xD0, 0xB5, 0xD0, 0xB2, 0xD0, 0xBE, 0xD0, 0xB4, 0x20, 0xD0, 0xBB, 0xD0, 0xB8, 
					0xD1, 0x87, 0xD0, 0xBD, 0xD1, 0x8B, 0xD1, 0x85, 0x20, 0xD1, 0x81, 0xD1, 0x80, 0xD0, 0xB5, 0xD0, 
					0xB4, 0xD1, 0x81, 0xD1, 0x82, 0xD0, 0xB2 };

 

Формат XML-тегов (загружаемая таблица разметки)

Если данные не содержат в начале документа одного из заголовков <!PINPADFILE RU><!PINPADFILE UTF8><!PINPADFILE INVISIBLE RU> или <!PINPADFILE INVISIBLE UTF8>, то документ сначала отображается в соответствии с загружаемой таблицей разметки данных, а затем в исходном виде "как есть". Если отдельные теги отсутствуют в загружаемой таблице, то Рутокен PINPad сначала отобразит данные в соответствии с существующими в таблице тегами, а затем и все содержимое документа в виде "как есть", предлагая затем подписать документ или отказаться от подписи. Если загружаемая таблица тегов полностью отсутствует, то Рутокен PINPad сразу отобразит все содержимое документа в виде "как есть". Таким образом, отсутствие в загружаемой таблице используемых в документе тегов не приведет к ошибке.

По умолчанию Рутокен PINPad имеет пустую таблицу тегов. Чтение и запись таблицы выполняются с предъявлением специального пароля администрирования PINPad APDU-командой или утилитой ConfUtil, выдающейся по запросу партнерам-разработчикам.

Размеченный в соответствии с таблицей тегов документ представляет собой данные для отображения, содержащиеся между открывающимся и закрывающимся тегами:

  • <TagName>Data<\TagName> или
  • <TagName1>...<TagName2>Data<\TagName2>...<\TagName1> для вложенных тегов.

Отображение размеченных данных происходит следующим образом. Если Рутокен PINPad находит тег в загружаемой таблице разметки данных, то в качестве имени поля выводится соответствующий тегу псевдоним, а в качестве значения поля – данные, заключенные между открывающимся и закрывающимся тегом. Данные, заключенные между открывающимся и закрывающимся тегом, не должны содержать непечатных символов, иначе попытка их вывода на экран Рутокен PINPad приведет к ошибке.

Данные могут быть представлены как в кодировке Windows-1251, так и в UTF-8. Кодировка указывается добавлением к данным заголовка следующего формата:

  • <?xml version="1.0" encoding="Windows-1251"?> для использования кодировки Windows-1251 или

  • <?xml version="1.0" encoding="utf-8"?> для использования кодировки UTF-8 (используется по умолчанию).

Пример сообщения в формате XML-тегов

 

Данные в формате XML для подписи c отображением в кодировке Windows-1251
char XMLDataCP1251[] =	"<?xml version=\"1.0\" encoding=\"windows-1251\"?><Файл><ИмяФайла>Name-00001.XML</ИмяФайла><ЗаголовокФайла>"\
	                	"<ВерсияФормата>01.00</ВерсияФормата><ТипФайла>ВНЕШНИЙ</ТипФайла></ЗаголовокФайла><ФИО><Фамилия>Петров</Фамилия>"\
						"<Имя>Сергей</Имя><Отчество>Александрович</Отчество></ФИО><Пол>М</Пол><ДатаРождения>01.02.1990</ДатаРождения>"\
						"<ДатаЗаполнения>01.02.2015</ДатаЗаполнения><ext><МестоРожд>г. Москва</МестоРожд><Адрес><Индекс>123456</Индекс>"\
						"<Область>Московская обл.</Область><Район>Первомайский р-н</Район>Город>г. Москва</Город><Улица>ул. Первомайская </Улица>"\
						"<Дом>3</Дом><Корпус>1</Корпус><Квартира>123</Квартира></Адрес></ext></Файл>";

 

 

Данные в формате XML для подписи c отображением в кодировке UTF-8
char XMLDataUTF8[] =	"<?xml version=\"1.0\" encoding=\"utf-8\"?><Файл><ИмяФайла>Name-00001.XML</ИмяФайла><ЗаголовокФайла>"\
	                	"<ВерсияФормата>01.00</ВерсияФормата><ТипФайла>ВНЕШНИЙ</ТипФайла></ЗаголовокФайла><ФИО><Фамилия>Петров</Фамилия>"\
						"<Имя>Сергей</Имя><Отчество>Александрович</Отчество></ФИО><Пол>М</Пол><ДатаРождения>01.02.1990</ДатаРождения>"\
						"<ДатаЗаполнения>01.02.2015</ДатаЗаполнения><ext><МестоРожд>г. Москва</МестоРожд><Адрес><Индекс>123456</Индекс>"\
						"<Область>Московская обл.</Область><Район>Первомайский р-н</Район>Город>г. Москва</Город><Улица>ул. Первомайская </Улица>"\
						"<Дом>3</Дом><Корпус>1</Корпус><Квартира>123</Квартира></Адрес></ext></Файл>";

 

Формат хранения загружаемой таблицы тегов

Загружаемая таблица разметки данных хранится в памяти устройства и представляет собой набор XML-тегов, каждая запись которой имеет один из следующих форматов:

  • <TagName>Pseudonym\n  для одиночного тега или

  • <TagName1><TagName2>Pseudonym\n  для вложенного тега, 
    где TagName, TagName1, TagName – имя тега, Data – псевдоним, соответствующий тегу, в формате ANSI или UTF8, \n - символ перевода строки, соответствует последовательности 0x0D 0x0A.

Имя тега не должно содержать символы с кодом, меньше  0x20, а также символы "<",">" и "/".


Сырые данные

Если данные не содержат в начале документа ни одного из заголовков <!PINPADFILE RU><!PINPADFILE UTF8><!PINPADFILE INVISIBLE RU> или <!PINPADFILE INVISIBLE UTF8>, ни тегов из загружаемой таблицы разметки данных или загружаемая таблица отсутствует, Рутокен PINPad отображает все содержимое документа в виде "как есть", предлагая затем подписать документ или отказаться от подписи. 

 

Данные для подписи без форматирования
char RawData[] = "ФИО: Петров Петр Петрович Москва, Пионерская ул, д. 3, кв. 72 Перевод со счета: 42301810001000075212 "\
				 "Сумма: 150000 Валюта: RUR Наименование получателя: Иванова Елена Ивановна Номер счета получателя: "\
				 "40817810338295201618 БИК банка получателя: 044525225 Наименование банка получателя: ОАО 'СБЕРБАНК РОССИИ' Г. МОСКВА "\
				 "Номер счета банка получателя: 30101810400000000225 Назначение платежа: перевод личных средств";

 

Подпись данных 

Для вычисления подписи сообщения служат функции C_SignInit() и C_Sign(). Сначала операцию подписи нужно инициализировать через C_SignInit(), передав в нее идентификатор сессии, механизма и закрытого ключа. Затем размер буфера для подписанных данных можно определить, вызвав C_Sign() с указателем на длину буфера, равной нулю, и подписать данные, вызвав C_Sign() второй раз.

Функция C_Sign(), как и некоторые другие функции интерфейса PKCS#11, возвращает значения различной длины и подчиняется соответствующему соглашению вызова для подобных функций, описанному в пункте 11.2 стандарта PKCS#11. Краткая его суть сводится к тому, что существует два способа вызова таких функций. В первом случае функция может быть вызвана с пустым указателем для возвращаемого буфера (NULL_PTR), в таком случае функция вернет размер возвращаемого буфера в байтах в соответствующую переменную. Во втором случае функция может быть вызвана с непустым указателем для возвращаемого буфера и указанным значением длины буфера, в таком случае функция вернет результат выполнения криптографической операции в соответствующую переменную при условии достаточности размера буфера. Если указанная длина буфера недостаточна для возвращения всего результат, функция вернет ошибку CKR_BUFFER_TOO_SMALL. При успешном выполнении функция возвращает код ошибки CKR_OK.

При использовании совместных алгоритмов хеширования и подписи (например, CKM_GOSTR3410_WITH_GOSTR3411) в  C_Sign() передается открытый текст для подписи, при использовании только алгоритма подписи (например, CKM_GOSTR3410) – уже прохешированные данные.

При использовании совместных алгоритмов хеширования и подписи в механизме должны быть заданы параметры алгоритма хеширования.

В качестве данных на подпись может быть передан запрос на сертификат, представленный в байт-коде.

Особенности подписи данных на Рутокен PINPad 

Рутокен PINPad поддерживает два вида подписи сообщения: с отображением на экране и без отображения в зависимости от формата сообщения и значения атрибута ключевой пары CKA_VENDOR_KEY_CONFIRM_OP.

Для отображения сообщения используется пара функций  C_SignInit() для инициализации операции подписи, и  C_Sign() для самой подписи. Ключевая пара, которой выполняется подпись, должна иметь атрибут CKA_VENDOR_KEY_CONFIRM_OP равным true, а само сообщение не должно иметь теги <!PINPADFILE INVISIBLE RU> и <!PINPADFILE INVISIBLE UTF8> (помимо ограничений на данные), в  противном случае функция C_Sign() вернет ошибку.

Для подписи сообщения без отображения должна использоваться пара функций C_EX_SignInvisibleInit() для инициализации операции подписи, и C_EX_SignInvisible() для самой подписи. Если подпись выполняется ключевой парой с атрибутом CKA_VENDOR_KEY_CONFIRM_OP равным true, то сообщение должно иметь теги <!PINPADFILE INVISIBLE RU> и <!PINPADFILE INVISIBLE UTF8>, в  противном случае функция C_EX_SignInvisible() вернет ошибку. 

Если ключевая пара, которой осуществляется подпись, имеет атрибут CKA_VENDOR_KEY_CONFIRM_OP равным false, то подпись сообщения может быть выполнена только функциями C_EX_SignInvisibleInit() и C_EX_SignInvisible() вне зависимости от формата сообщения.

Подпись данных отдельными механизмами хеширования и подписи

При использовании отдельных механизмов хеширования и подписи сообщение сначала хешируется функциями C_DigestInit() и C_Digest(), а затем значение хеша подписывается функциями C_SignInit() и C_Sign().

Подпись данных на Рутокен PINPad отдельными механизмами хеширования и подписи

При использовании отдельных механизмов хеширования и подписи сообщение сначала хешируется функциями C_DigestInit() и C_Digest(), а затем значение хеша подписывается функциями C_SignInit()/C_EX_SignInvisibleInit() и C_Sign()/C_EX_SignInvisible().

В функцию C_DigestInit() передается механизм хеширования (например, CKM_GOSTR3411), а в функцию C_Digest() – само сообщение. При вызове функция C_Digest() вычисляет хеш сообщения и возвращает управление вместе со значением хеша. Исходное сообщение и значение хеша запоминаются в памяти Рутокен PINPad.

Затем вызывается функция инициализации подписи C_SignInit()/C_EX_SignInvisibleInit(), в которую передается механизм подписи (например, CKM_GOSTR3410), и сама функция подписи C_Sign()/C_EX_SignInvisible() с переданным в нее значением хеша.

Если подпись выполняется ключевой парой с атрибутом CKA_VENDOR_KEY_CONFIRM_OP равным true, то при вызове функции C_EX_SignInvisible() подпись выполняется в автоматическом режиме. Рутокен PINPad сверяет сохраненное значение хеша с переданным функцией C_EX_SignInvisible() и в случае совпадения выполняет подпись хеша и возвращает блок сформированной подписи размером 64 байта для механизма CKM_GOSTR3410 и 128 байт для механизма CKM_GOSTR3410_512.  Если значения хешей не совпадают, функция C_EX_SignInvisible() возвращает ошибку.

Если подпись выполняется ключевой парой с атрибутом CKA_VENDOR_KEY_CONFIRM_OP равным true, то при вызове функции C_Sign() Рутокен PINPad сверяет сохраненное значение хеша с переданным функцией и в случае совпадения отображает на экране текст исходного сообщения. Функция C_Sign() ожидает нажатия пользователем кнопки подтверждения или отказа от операции на экране Рутокен PINPad. Если значения хешей не совпадают, функция C_Sign() возвращает ошибку без вывода на экране текста сообщения.

Если пользователь подтверждает выполнение операции, то сохраненное значение хеша в Рутокен PINPad подписывается, и функция C_Sign() возвращает управление и блок сформированной цифровой подписи размером 64 байта для механизма CKM_GOSTR3410 и 128 байт для механизма CKM_GOSTR3410_512

Если пользователь отклоняет операцию подписи, функция C_Sign() немедленно возвращает управление и код ошибки. Вычисления цифровой подписи не производится.

Особенности поведения Рутокен PINPad при включенной политике "Отключить кэширование PIN2"

Устройство Рутокен PINPad по умолчанию настроено таким образом, что для подписи ключом с атрибутом CKA_VENDOR_KEY_PIN_ENTER функцией C_Sign() ввод локального PIN-кода (PIN2) с экрана устройства будет требоваться только один раз, при подписи первого документа. Далее этот PIN-код кэшируется и запоминается в памяти Рутокен PINPad, и при подписывании последующих документов выдает окно подтверждения/отказа от подписи вместо окна ввода PIN-кода. Кэш сбрасывается по истечении 15 минут или после выполнения функции C_Logout().

По умолчанию политика "Отключить кэширование PIN2" отключена. Настройка политики выполняется с предъявлением специального пароля администрирования PINPad через APDU-команду или утилиту ConfUtil, которая выдается по запросу партнерам-разработчикам.

Пример подписи данных по алгоритму ГОСТ Р 34.10-2001 отдельными механизмами хеширования и подписи для всех устройств Рутокен

Подпись данных по алгоритму ГОСТ Р 34.10-2001
/* Данные для подписи в виде двоичной строки */
CK_BYTE pbtData[] = { 0x3C, 0x21, 0x50, 0x49, 0x4E, 0x50, 0x41, 0x44, 0x46, 0x49, 0x4C, 0x45, 0x20, 0x52, 0x55, 0x3E, 
					  0x3C, 0x21, 0x3E, 0xED, 0xE5, 0xE2, 0xE8, 0xE4, 0xE8, 0xEC, 0xFB, 0xE9, 0x20, 0xF2, 0xE5, 0xEA, 
					  0xF1, 0xF2, 0x3C, 0x4E, 0x3E, 0xD4, 0xC8, 0xCE, 0x3A, 0x3C, 0x56, 0x3E, 0xCF, 0xE5, 0xF2, 0xF0, 
					  0xEE, 0xE2, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0xEE, 0xE2, 0xE8, 0xF7, 
					  0x20, 0xCC, 0xEE, 0xF1, 0xEA, 0xE2, 0xE0, 0x2C, 0x20, 0xCF, 0xE8, 0xEE, 0xED, 0xE5, 0xF0, 0xF1, 
					  0xEA, 0xE0, 0xFF, 0x20, 0xF3, 0xEB, 0x2C, 0x20, 0xE4, 0x2E, 0x20, 0x33, 0x2C, 0x20, 0xEA, 0xE2, 
					  0x2E, 0x20, 0x37, 0x32 };
 
/*  Механизм хеширования ГОСТ Р 34.11-94 */
CK_MECHANISM HashMech = {CKM_GOSTR3411, NULL_PTR, 0}; 

/* Механизм подписи/проверки подписи по алгоритму ГОСТ Р 34.10-2001 */
CK_MECHANISM    SigVerMech = {CKM_GOSTR3410, NULL_PTR, 0};
 
CK_BYTE_PTR pbtHash 		= NULL_PTR;      // Указатель на буфер для значения хеша данных
CK_ULONG 	ulHashSize 		= 0;             // Размер буфера в байтах
 
CK_BYTE_PTR pbtSignature 	= NULL_PTR;      // Указатель на буфер, содержащий подпись для исходных данных
CK_ULONG 	ulSignatureSize = 0;             // Размер буфера, содержащего подпись для исходных данных, в байтах
 
while(TRUE)
{
	...
 
	/* Инициализировать операцию хеширования  */
	printf("C_DigestInit");
	rv = pFunctionList->C_DigestInit(hSession,		// Хэндл сессии
								 	 &HashMech);	// Механизм хеширования
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Определить размер значения хеша данных */
	printf("C_Digest step 1");
	rv = pFunctionList->C_Digest( hSession,				// Хэндл сессии
								  pbtData,				// Буфер с данными для хеширования
								  arraysize(pbtData),	// Размер данных для хеширования
								  pbtHash,				// Буфер для вычисленного значения хеша
								  &ulHashSize);			// Размер значения хеша
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtHash = (CK_BYTE*)malloc(ulHashSize);
	if (pbtHash == NULL)
	{
		printf("Memory allocation for pbtHash failed! \n");
		break;
	}
	memset(pbtHash,
		   0,
		   (ulHashSize * sizeof(CK_BYTE)));
	/* Сформировать хеш от исходных данных */
	printf("C_Digest step 2");
	rv = pFunctionList->C_Digest(hSession,				// Хэндл сессии
								 pbtData,				// Буфер с данными для хеширования
								 arraysize(pbtData),	// Размер данных для хеширования
								 pbtHash,				// Буфер для вычисленного значения хеша
								 &ulHashSize);			// Размер значения хеша
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Инициализировать операцию подписи данных */
	printf("C_SignInit");
	rv = pFunctionList->C_SignInit( hSession,		// Хэндл сессии
									&SigVerMech,	// Механизм подписи
									hPrivateKey ); 	// Хэндл закрытого ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Определить размер подписи*/
	printf("C_Sign step 1");
	rv = pFunctionList->C_Sign( hSession,			// Хэндл сессии
								pbtHash,			// Буфер с данными для подписи
								ulHashSize,			// Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtSignature = (CK_BYTE*)malloc(ulSignatureSize);
	if (pbtSignature  == NULL)
	{
		printf("Memory allocation for pbtSignature failed! \n");
		break;
	}
	memset( pbtSignature,
			0,
			ulSignatureSize * sizeof(CK_BYTE));

	/* Подписать исходные данные */
	printf("C_Sign step 2");
	rv = pFunctionList->C_Sign( hSession,			// Хэндл сессии
								pbHash,				// Буфер с данными для подписи
								ulHashSize,			// Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Распечатать буфер, содержащий подпись */
	printf("Signature buffer is: \n");
	for (i = 0;
		 i < ulSignatureSize;
		 i++)
	{
		printf("%02X ", pbtSignature[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");
	}
	
	break;
}

Пример подписи данных по алгоритму ГОСТ Р 34.10-2012 отдельными механизмами хеширования и подписи для всех устройств Рутокен

Подпись данных по алгоритму ГОСТ Р 34.10-2012
/* Данные для подписи в виде двоичной строки */
CK_BYTE pbtData[] = { 0x3C, 0x21, 0x50, 0x49, 0x4E, 0x50, 0x41, 0x44, 0x46, 0x49, 0x4C, 0x45, 0x20, 0x52, 0x55, 0x3E, 
					  0x3C, 0x21, 0x3E, 0xED, 0xE5, 0xE2, 0xE8, 0xE4, 0xE8, 0xEC, 0xFB, 0xE9, 0x20, 0xF2, 0xE5, 0xEA, 
					  0xF1, 0xF2, 0x3C, 0x4E, 0x3E, 0xD4, 0xC8, 0xCE, 0x3A, 0x3C, 0x56, 0x3E, 0xCF, 0xE5, 0xF2, 0xF0, 
					  0xEE, 0xE2, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0xEE, 0xE2, 0xE8, 0xF7, 
					  0x20, 0xCC, 0xEE, 0xF1, 0xEA, 0xE2, 0xE0, 0x2C, 0x20, 0xCF, 0xE8, 0xEE, 0xED, 0xE5, 0xF0, 0xF1, 
					  0xEA, 0xE0, 0xFF, 0x20, 0xF3, 0xEB, 0x2C, 0x20, 0xE4, 0x2E, 0x20, 0x33, 0x2C, 0x20, 0xEA, 0xE2, 
					  0x2E, 0x20, 0x37, 0x32 };
 
/*  Механизм хеширования ГОСТ Р 34.11-2012(256) */
CK_MECHANISM HashMech256 = {CKM_GOSTR3411_12_256, NULL_PTR, 0}; 
 
/*  Механизм хеширования ГОСТ Р 34.11-2012(512) */
CK_MECHANISM HashMech512 = {CKM_GOSTR3411_12_512, NULL_PTR, 0}; 

/* Механизм подписи/проверки подписи по алгоритму ГОСТ Р 34.10-2012, 256 бит */
CK_MECHANISM    SigVerMech256 = {CKM_GOSTR3410, NULL_PTR, 0};
 
/* Механизм подписи/проверки подписи по алгоритму ГОСТ Р 34.10-2012, 512 бит */
CK_MECHANISM    SigVerMech512 = {CKM_GOSTR3410_512, NULL_PTR, 0};
 
 
CK_BYTE_PTR pbtHash 		= NULL_PTR;      // Указатель на буфер для значения хеша данных
CK_ULONG 	ulHashSize 		= 0;             // Размер буфера в байтах
 
CK_BYTE_PTR pbtSignature 	= NULL_PTR;      // Указатель на буфер, содержащий подпись для исходных данных
CK_ULONG 	ulSignatureSize = 0;             // Размер буфера, содержащего подпись для исходных данных, в байтах
 
while(TRUE)
{
	...
 
	/* Инициализировать операцию хеширования  */
	printf("C_DigestInit");
	rv = pFunctionList->C_DigestInit(hSession,		// Хэндл сессии
								 	 &HashMech256);	// Механизм хеширования (HashMech256 или HashMech512)
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Определить размер значения хеша данных */
	printf("C_Digest step 1");
	rv = pFunctionList->C_Digest( hSession,				// Хэндл сессии
								  pbtData,				// Буфер с данными для хеширования
								  arraysize(pbtData),	// Размер данных для хеширования
								  pbtHash,				// Буфер для вычисленного значения хеша
								  &ulHashSize);			// Размер значения хеша
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtHash = (CK_BYTE*)malloc(ulHashSize);
	if (pbtHash == NULL)
	{
		printf("Memory allocation for pbtHash failed! \n");
		break;
	}
	memset(pbtHash,
		   0,
		   (ulHashSize * sizeof(CK_BYTE)));
	/* Сформировать хеш от исходных данных */
	printf("C_Digest step 2");
	rv = pFunctionList->C_Digest(hSession,				// Хэндл сессии
								 pbtData,				// Буфер с данными для хеширования
								 arraysize(pbtData),	// Размер данных для хеширования
								 pbtHash,				// Буфер для вычисленного значения хеша
								 &ulHashSize);			// Размер значения хеша
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Инициализировать операцию подписи данных */
	printf("C_SignInit");
	rv = pFunctionList->C_SignInit( hSession,		// Хэндл сессии
									&SigVerMech256,	// Механизм подписи (SigVerMech256 или SigVerMech512)
									hPrivateKey ); 	// Хэндл закрытого ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Определить размер подписи*/
	printf("C_Sign step 1");
	rv = pFunctionList->C_Sign( hSession,			// Хэндл сессии
								pbtHash,			// Буфер с данными для подписи
								ulHashSize,			// Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtSignature = (CK_BYTE*)malloc(ulSignatureSize);
	if (pbtSignature  == NULL)
	{
		printf("Memory allocation for pbtSignature failed! \n");
		break;
	}
	memset( pbtSignature,
			0,
			ulSignatureSize * sizeof(CK_BYTE));

	/* Подписать исходные данные */
	printf("C_Sign step 2");
	rv = pFunctionList->C_Sign( hSession,			// Хэндл сессии
								pbHash,				// Буфер с данными для подписи
								ulHashSize,			// Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Распечатать буфер, содержащий подпись */
	printf("Signature buffer is: \n");
	for (i = 0;
		 i < ulSignatureSize;
		 i++)
	{
		printf("%02X ", pbtSignature[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");
	}
	
	break;
}

Подпись совместным механизмом хеширования и подписи (кроме Рутокен PINPad)

При использовании совместного механизма и хеширование, и подпись выполняются функцией C_Sign(). Сначала в функцию C_SignInit() передается совместный механизм (например, CKM_GOSTR3410_WITH_GOSTR3411), а затем в функцию C_Sign() – сообщение.

Пример подписи данных по алгоритму ГОСТ Р 34.10-2001 совместным механизмом хеширования и подписи (кроме Рутокен PINPad)

Подпись данных по алгоритму ГОСТ Р 34.10-2001
/* Данные для подписи в виде двоичной строки */
CK_BYTE pbtData[] = { 0x3C, 0x21, 0x50, 0x49, 0x4E, 0x50, 0x41, 0x44, 0x46, 0x49, 0x4C, 0x45, 0x20, 0x52, 0x55, 0x3E, 
					  0x3C, 0x21, 0x3E, 0xED, 0xE5, 0xE2, 0xE8, 0xE4, 0xE8, 0xEC, 0xFB, 0xE9, 0x20, 0xF2, 0xE5, 0xEA, 
					  0xF1, 0xF2, 0x3C, 0x4E, 0x3E, 0xD4, 0xC8, 0xCE, 0x3A, 0x3C, 0x56, 0x3E, 0xCF, 0xE5, 0xF2, 0xF0, 
					  0xEE, 0xE2, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0xEE, 0xE2, 0xE8, 0xF7, 
					  0x20, 0xCC, 0xEE, 0xF1, 0xEA, 0xE2, 0xE0, 0x2C, 0x20, 0xCF, 0xE8, 0xEE, 0xED, 0xE5, 0xF0, 0xF1, 
					  0xEA, 0xE0, 0xFF, 0x20, 0xF3, 0xEB, 0x2C, 0x20, 0xE4, 0x2E, 0x20, 0x33, 0x2C, 0x20, 0xEA, 0xE2, 
					  0x2E, 0x20, 0x37, 0x32 };
 
/* Набор параметров КриптоПро алгоритма ГОСТ Р 34.11-1994 */
CK_BYTE     GOST3411params[]  = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01 };
 
/*  Механизм подписи подписи по алгоритму ГОСТ Р 34.10-2001 с хешированием по алгоритму ГОСТ Р 34.11-94*/
CK_MECHANISM    HashSigVerMech = {CKM_GOSTR3410_WITH_GOSTR3411, GOST3411params, sizeof(GOST3411params)};
CK_BYTE_PTR pbtSignature = NULL_PTR;                 // Указатель на буфер, содержащий подпись для исходных данных
CK_ULONG ulSignatureSize = 0;                        // Размер буфера, содержащего подпись для исходных данных, в байтах
 
while(TRUE)
{
	...
 
	/* Инициализировать операцию подписи данных */
	printf("C_SignInit");
	rv = pFunctionList->C_SignInit(hSession,			// Хэндл сессии
									&HashSigVerMech ,	// Механизм подписи
									hPrivateKey ); 		// Хэндл закрытого ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Определить размер подписи*/
	printf("C_Sign step 1");
	rv = pFunctionList->C_Sign(hSession,			// Хэндл сессии
								pbtData,			// Буфер с данными для подписи
								arraysize(pbtData), // Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtSignature = (CK_BYTE*)malloc(ulSignatureSize);
	if (pbtSignature  == NULL)
	{
		printf("Memory allocation for pbtSignature failed! \n");
		break;
	}
	memset( pbtSignature,
			0,
			ulSignatureSize * sizeof(CK_BYTE));

	/* Подписать исходные данные */
	printf("C_Sign step 2");
	rv = pFunctionList->C_Sign(hSession,			// Хэндл сессии
								pbtData,			// Буфер с данными для подписи
								arraysize(pbtData), // Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Распечатать буфер, содержащий подпись */
	printf("Signature buffer is: \n");
	for (i = 0;
		 i < ulSignatureSize;
		 i++)
	{
		printf("%02X ", pbtSignature[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");
	}
	
	break;
}

Пример подписи данных по алгоритму ГОСТ Р 34.10-2012 совместным механизмом хеширования и подписи (кроме Рутокен PINPad)

Подпись данных по алгоритму ГОСТ Р 34.10-2012
/* Данные для подписи в виде двоичной строки */
CK_BYTE pbtData[] = { 0x3C, 0x21, 0x50, 0x49, 0x4E, 0x50, 0x41, 0x44, 0x46, 0x49, 0x4C, 0x45, 0x20, 0x52, 0x55, 0x3E, 
					  0x3C, 0x21, 0x3E, 0xED, 0xE5, 0xE2, 0xE8, 0xE4, 0xE8, 0xEC, 0xFB, 0xE9, 0x20, 0xF2, 0xE5, 0xEA, 
					  0xF1, 0xF2, 0x3C, 0x4E, 0x3E, 0xD4, 0xC8, 0xCE, 0x3A, 0x3C, 0x56, 0x3E, 0xCF, 0xE5, 0xF2, 0xF0, 
					  0xEE, 0xE2, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0xEE, 0xE2, 0xE8, 0xF7, 
					  0x20, 0xCC, 0xEE, 0xF1, 0xEA, 0xE2, 0xE0, 0x2C, 0x20, 0xCF, 0xE8, 0xEE, 0xED, 0xE5, 0xF0, 0xF1, 
					  0xEA, 0xE0, 0xFF, 0x20, 0xF3, 0xEB, 0x2C, 0x20, 0xE4, 0x2E, 0x20, 0x33, 0x2C, 0x20, 0xEA, 0xE2, 
					  0x2E, 0x20, 0x37, 0x32 };
 
/* Набор параметров КриптоПро алгоритма ГОСТ Р 34.11-2012, 256 бит*/
CK_BYTE     GOST3411_256_params[]  = { 0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02 };
 
/*  Механизм подписи подписи по алгоритму ГОСТ Р 34.10-2012 с хешированием по алгоритму ГОСТ Р 34.11-2012 (256 бит)*/
CK_MECHANISM    HashSigVerMech256 = {CKM_GOSTR3410_WITH_GOSTR3411_12_256, GOST3411_256_params, sizeof(GOST3411_256_params)};
 
 
/* Набор параметров КриптоПро алгоритма ГОСТ Р 34.11-2012, 512 бит*/
CK_BYTE     GOST3411_512_params[]  = { 0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03 };
 
/*  Механизм подписи подписи по алгоритму ГОСТ Р 34.10-2012 с хешированием по алгоритму ГОСТ Р 34.11-2012 (512 бит)*/
CK_MECHANISM    HashSigVerMech512 = {CKM_GOSTR3410_WITH_GOSTR3411_12_512, GOST3411_512_params, sizeof(GOST3411_512_params)};

CK_BYTE_PTR pbtSignature = NULL_PTR;                 // Указатель на буфер, содержащий подпись для исходных данных
CK_ULONG ulSignatureSize = 0;                        // Размер буфера, содержащего подпись для исходных данных, в байтах
 
while(TRUE)
{
	...
 
	/* Инициализировать операцию подписи данных */
	printf("C_SignInit");
	rv = pFunctionList->C_SignInit(hSession,			// Хэндл сессии
									&HashSigVerMech256,	// Механизм подписи (HashSigVerMech256 или HashSigVerMech512)
									hPrivateKey ); 		// Хэндл закрытого ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Определить размер подписи*/
	printf("C_Sign step 1");
	rv = pFunctionList->C_Sign(hSession,			// Хэндл сессии
								pbtData,			// Буфер с данными для подписи
								arraysize(pbtData), // Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtSignature = (CK_BYTE*)malloc(ulSignatureSize);
	if (pbtSignature  == NULL)
	{
		printf("Memory allocation for pbtSignature failed! \n");
		break;
	}
	memset( pbtSignature,
			0,
			ulSignatureSize * sizeof(CK_BYTE));

	/* Подписать исходные данные */
	printf("C_Sign step 2");
	rv = pFunctionList->C_Sign(hSession,			// Хэндл сессии
								pbtData,			// Буфер с данными для подписи
								arraysize(pbtData), // Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Распечатать буфер, содержащий подпись */
	printf("Signature buffer is: \n");
	for (i = 0;
		 i < ulSignatureSize;
		 i++)
	{
		printf("%02X ", pbtSignature[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");
	}
	
	break;
}


Подпись на Рутокен PINPad совместным механизмом хеширования и подписи

При использовании совместного механизма и хеширование, и подпись выполняются функцией C_EX_SignInvisible(). Сначала в функцию C_EX_SignInvisibleInit() передается совместный механизм (например, CKM_GOSTR3410_WITH_GOSTR3411), а затем в функцию C_EX_SignInvisible() – сообщение.

При вызове функции C_EX_SignInvisible() подпись будет выполнена в автоматическом режиме без отображения подписываемого сообщения на экране Рутокен PINPad.

Пример подписи данных по алгоритму ГОСТ Р 34.10-2001 совместным механизмом хеширования и подписи для Рутокен PINPad

Подпись данных по алгоритму ГОСТ Р 34.10-2001
/* Данные для подписи в виде двоичной строки */
CK_BYTE pbtData[] = { 0x3C, 0x21, 0x50, 0x49, 0x4E, 0x50, 0x41, 0x44, 0x46, 0x49, 0x4C, 0x45, 0x20, 0x52, 0x55, 0x3E, 
					  0x3C, 0x21, 0x3E, 0xED, 0xE5, 0xE2, 0xE8, 0xE4, 0xE8, 0xEC, 0xFB, 0xE9, 0x20, 0xF2, 0xE5, 0xEA, 
					  0xF1, 0xF2, 0x3C, 0x4E, 0x3E, 0xD4, 0xC8, 0xCE, 0x3A, 0x3C, 0x56, 0x3E, 0xCF, 0xE5, 0xF2, 0xF0, 
					  0xEE, 0xE2, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0xEE, 0xE2, 0xE8, 0xF7, 
					  0x20, 0xCC, 0xEE, 0xF1, 0xEA, 0xE2, 0xE0, 0x2C, 0x20, 0xCF, 0xE8, 0xEE, 0xED, 0xE5, 0xF0, 0xF1, 
					  0xEA, 0xE0, 0xFF, 0x20, 0xF3, 0xEB, 0x2C, 0x20, 0xE4, 0x2E, 0x20, 0x33, 0x2C, 0x20, 0xEA, 0xE2, 
					  0x2E, 0x20, 0x37, 0x32 };
 
/* Набор параметров КриптоПро алгоритма ГОСТ Р 34.11-1994 */
CK_BYTE     GOST3411params[]  = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01 };
 
/*  Механизм подписи подписи по алгоритму ГОСТ Р 34.10-2001 с хешированием по алгоритму ГОСТ Р 34.11-94*/
CK_MECHANISM    HashSigVerMech = {CKM_GOSTR3410_WITH_GOSTR3411, GOST3411params, sizeof(GOST3411params)};
CK_BYTE_PTR pbtSignature = NULL_PTR;                 // Указатель на буфер, содержащий подпись для исходных данных
CK_ULONG ulSignatureSize = 0;                        // Размер буфера, содержащего подпись для исходных данных, в байтах
 
while(TRUE)
{
	...
 
	/* Инициализировать операцию подписи данных */
	printf("C_EX_SignInvisibleInit");
	rv = pFunctionList->C_EX_SignInvisibleInit(hSession,			// Хэндл сессии
											   &HashSigVerMech ,	// Механизм подписи
											   hPrivateKey ); 		// Хэндл закрытого ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Определить размер подписи*/
	printf("C_EX_SignInvisible step 1");
	rv = pFunctionList->C_EX_SignInvisible(hSession,			// Хэндл сессии
										   pbtData,			    // Буфер с данными для подписи
										   arraysize(pbtData),  // Длина подписываемых данных
										   pbtSignature,		// Буфер с подписью
										   &ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtSignature = (CK_BYTE*)malloc(ulSignatureSize);
	if (pbtSignature  == NULL)
	{
		printf("Memory allocation for pbtSignature failed! \n");
		break;
	}
	memset( pbtSignature,
			0,
			ulSignatureSize * sizeof(CK_BYTE));

	/* Подписать исходные данные */
	printf("C_EX_SignInvisible step 2");
	rv = pFunctionList->C_EX_SignInvisible(hSession,			// Хэндл сессии
										   pbtData,			    // Буфер с данными для подписи
										   arraysize(pbtData),  // Длина подписываемых данных
										   pbtSignature,		// Буфер с подписью
										   &ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Распечатать буфер, содержащий подпись */
	printf("Signature buffer is: \n");
	for (i = 0;
		 i < ulSignatureSize;
		 i++)
	{
		printf("%02X ", pbtSignature[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");
	}
	
	break;
}

Пример подписи данных по алгоритму ГОСТ Р 34.10-2012 совместным механизмом хеширования и подписи для Рутокен PINPad

Подпись данных по алгоритму ГОСТ Р 34.10-2012
/* Данные для подписи в виде двоичной строки */
CK_BYTE pbtData[] = { 0x3C, 0x21, 0x50, 0x49, 0x4E, 0x50, 0x41, 0x44, 0x46, 0x49, 0x4C, 0x45, 0x20, 0x52, 0x55, 0x3E, 
					  0x3C, 0x21, 0x3E, 0xED, 0xE5, 0xE2, 0xE8, 0xE4, 0xE8, 0xEC, 0xFB, 0xE9, 0x20, 0xF2, 0xE5, 0xEA, 
					  0xF1, 0xF2, 0x3C, 0x4E, 0x3E, 0xD4, 0xC8, 0xCE, 0x3A, 0x3C, 0x56, 0x3E, 0xCF, 0xE5, 0xF2, 0xF0, 
					  0xEE, 0xE2, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0xEE, 0xE2, 0xE8, 0xF7, 
					  0x20, 0xCC, 0xEE, 0xF1, 0xEA, 0xE2, 0xE0, 0x2C, 0x20, 0xCF, 0xE8, 0xEE, 0xED, 0xE5, 0xF0, 0xF1, 
					  0xEA, 0xE0, 0xFF, 0x20, 0xF3, 0xEB, 0x2C, 0x20, 0xE4, 0x2E, 0x20, 0x33, 0x2C, 0x20, 0xEA, 0xE2, 
					  0x2E, 0x20, 0x37, 0x32 };
 
/* Набор параметров КриптоПро алгоритма ГОСТ Р 34.11-2012, 256 бит*/
CK_BYTE     GOST3411_256_params[]  = { 0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02 };
 
/*  Механизм подписи подписи по алгоритму ГОСТ Р 34.10-2012 с хешированием по алгоритму ГОСТ Р 34.11-2012 (256 бит)*/
CK_MECHANISM    HashSigVerMech256 = {CKM_GOSTR3410_WITH_GOSTR3411_12_256, GOST3411_256_params, sizeof(GOST3411_256_params)};
 
 
/* Набор параметров КриптоПро алгоритма ГОСТ Р 34.11-2012, 512 бит*/
CK_BYTE     GOST3411_512_params[]  = { 0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03 };
 
/*  Механизм подписи подписи по алгоритму ГОСТ Р 34.10-2012 с хешированием по алгоритму ГОСТ Р 34.11-2012 (512 бит)*/
CK_MECHANISM    HashSigVerMech512 = {CKM_GOSTR3410_WITH_GOSTR3411_12_512, GOST3411_512_params, sizeof(GOST3411_512_params)};
CK_BYTE_PTR pbtSignature = NULL_PTR;                 // Указатель на буфер, содержащий подпись для исходных данных
CK_ULONG ulSignatureSize = 0;                        // Размер буфера, содержащего подпись для исходных данных, в байтах
 
while(TRUE)
{
	...
 
	/* Инициализировать операцию подписи данных */
	printf("C_EX_SignInvisibleInit");
	rv = pFunctionList->C_EX_SignInvisibleInit(hSession,			// Хэндл сессии
											   &HashSigVerMech256,	// Механизм подписи (HashSigVerMech256 или HashSigVerMech512)
											   hPrivateKey ); 		// Хэндл закрытого ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Определить размер подписи*/
	printf("C_EX_SignInvisible step 1");
	rv = pFunctionList->C_EX_SignInvisible(hSession,			// Хэндл сессии
										   pbtData,			    // Буфер с данными для подписи
										   arraysize(pbtData),  // Длина подписываемых данных
										   pbtSignature,		// Буфер с подписью
										   &ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtSignature = (CK_BYTE*)malloc(ulSignatureSize);
	if (pbtSignature  == NULL)
	{
		printf("Memory allocation for pbtSignature failed! \n");
		break;
	}
	memset( pbtSignature,
			0,
			ulSignatureSize * sizeof(CK_BYTE));

	/* Подписать исходные данные */
	printf("C_EX_SignInvisible step 2");
	rv = pFunctionList->C_EX_SignInvisible(hSession,			// Хэндл сессии
										   pbtData,			    // Буфер с данными для подписи
										   arraysize(pbtData),  // Длина подписываемых данных
										   pbtSignature,		// Буфер с подписью
										   &ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Распечатать буфер, содержащий подпись */
	printf("Signature buffer is: \n");
	for (i = 0;
		 i < ulSignatureSize;
		 i++)
	{
		printf("%02X ", pbtSignature[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");
	}
	
	break;
}


Подпись запроса на сертификат на Рутокен PINPad в формате PKCS#10

Помимо особого формата представления данных, Рутокен PINPad распознает и отображает на экране поля сформированного запроса на сертификат в формате PKCS#10. Затем весь поступивший на подпись запрос отображается в виде "как есть" в кодировке Windows-1251, где любые не отображаемые символы заменяются на экране символом '.'

В текущей версии Рутокен PINPad обрабатывает и отображает следующие поля запроса:

Имя поляПсевдоним, отображаемый на экране

title

title

surname

SN

streetAddress

STREET

stateOrProvinceName

ST

SNILS

SNILS

pseudonym

pseudonym

postalAddress

postal Address

organizationName

O

organizationalUnitName

OU

OGRNIP

OGRNIP

OGRN

OGRN

localityName

L

INN

INN

givenName

givenName

emailAddress

Email

domainComponent

DC

countryName

C

commonName

CN

Процесс подписи запроса на сертификат ничем не отличается от подписи обычных данных: сначала данные хешируются функцией С_Digest() и запоминаются в памяти Рутокен PINPad, затем вызывается функция C_Sign(). Сохраненные в памяти Рутокен PINPad данные и их хеш cверяются с переданными функцией и подписываются после подтверждения пользователем. При использовании совместного алгоритма хеширования и подписи все необходимые действия выполняются одной функцией C_Sign().

Подпись запроса на сертификат в формате PKCS#10
/* Сформированный запрос на сертификат в виде двоичной строки */
CK_BYTE pbtReqCert[] = {0x30, 0x82, 0x02, 0x69, 0x02, 0x01, 0x00, 0x30, 0x82, 0x01, 0x83, 0x31, 0x0b, 0x30, 0x09, 0x06,
						0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x52, 0x55, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04,
						0x08, 0x13, 0x06, 0x4d, 0x6f, 0x73, 0x63, 0x6f, 0x77, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55,
						0x04, 0x07, 0x13, 0x03, 0x6d, 0x73, 0x6b, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x09,
						0x13, 0x06, 0x73, 0x74, 0x72, 0x65, 0x65, 0x74, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04,
						0x0a, 0x13, 0x05, 0x41, 0x6b, 0x74, 0x69, 0x76, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
						0x0b, 0x13, 0x02, 0x49, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x10, 0x13, 0x0e,
						0x70, 0x6f, 0x73, 0x74, 0x61, 0x6c, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x31, 0x1b,
						0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x0c, 0x1e, 0x12, 0x04, 0x34, 0x04, 0x3e, 0x04, 0x3b, 0x04,
						0x36, 0x04, 0x3d, 0x04, 0x3e, 0x04, 0x41, 0x04, 0x42, 0x04, 0x4c, 0x31, 0x19, 0x30, 0x17, 0x06,
						0x08, 0x2a, 0x85, 0x03, 0x03, 0x81, 0x03, 0x01, 0x01, 0x12, 0x0b, 0x31, 0x32, 0x33, 0x34, 0x35,
						0x36, 0x37, 0x38, 0x39, 0x38, 0x37, 0x31, 0x16, 0x30, 0x14, 0x06, 0x05, 0x2a, 0x85, 0x03, 0x64,
						0x03, 0x12, 0x0b, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x38, 0x37, 0x31, 0x16,
						0x30, 0x14, 0x06, 0x05, 0x2a, 0x85, 0x03, 0x64, 0x01, 0x12, 0x0b, 0x31, 0x32, 0x33, 0x34, 0x35,
						0x36, 0x37, 0x38, 0x39, 0x38, 0x37, 0x31, 0x16, 0x30, 0x14, 0x06, 0x05, 0x2a, 0x85, 0x03, 0x64,
						0x05, 0x12, 0x0b, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x38, 0x37, 0x31, 0x2f,
						0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x1e, 0x26, 0x04, 0x24, 0x04, 0x30, 0x04, 0x3c, 0x04,
						0x38, 0x04, 0x3b, 0x04, 0x38, 0x04, 0x4f, 0x00, 0x20, 0x04, 0x18, 0x04, 0x3c, 0x04, 0x4f, 0x00,
						0x20, 0x04, 0x1e, 0x04, 0x47, 0x04, 0x35, 0x04, 0x41, 0x04, 0x42, 0x04, 0x32, 0x04, 0x3e, 0x31,
						0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x41, 0x13, 0x09, 0x70, 0x73, 0x65, 0x75, 0x64, 0x6f,
						0x6e, 0x79, 0x6d, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x04, 0x13, 0x07, 0x73, 0x75,
						0x72, 0x6e, 0x61, 0x6d, 0x65, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x2a, 0x13, 0x0a,
						0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x6e, 0x61,	0x6d, 0x65, 0x31, 0x22, 0x30, 0x20, 0x06, 0x09,
						0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09,	0x01, 0x16, 0x13, 0x65, 0x78, 0x61, 0x6d, 0x70,
						0x6c, 0x65, 0x40, 0x65, 0x78, 0x61, 0x6d, 0x70,	0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x63,
						0x30, 0x1c, 0x06, 0x06, 0x2a, 0x85, 0x03, 0x02,	0x02, 0x13, 0x30, 0x12, 0x06, 0x07, 0x2a, 0x85,
						0x03, 0x02, 0x02, 0x23, 0x01, 0x06, 0x07, 0x2a,	0x85, 0x03, 0x02, 0x02, 0x1e, 0x01, 0x03, 0x43,
						0x00, 0x04, 0x40, 0x26, 0x68, 0x22, 0x87, 0x6b,	0x3e, 0x60, 0xde, 0x6e, 0xcf, 0x7d, 0x9b, 0xc5,
						0x99, 0x49, 0x88, 0xe3, 0xce, 0x8d, 0x05, 0xb2,	0x0a, 0x3c, 0x3d, 0x2c, 0xb3, 0x7c, 0xc6, 0x9e,
						0x7e, 0x5a, 0xc6, 0x95, 0xde, 0x97, 0x86, 0x9a,	0x56, 0xe3, 0xc5, 0xf5, 0xc5, 0xca, 0x9a, 0x4a,
						0xd9, 0x11, 0xa0, 0x40, 0x08, 0xca, 0x70, 0x29,	0x13, 0x64, 0x7f, 0xa1, 0x6c, 0x5b, 0x5b, 0x25,
						0xc9, 0xa6, 0x0c, 0xa0, 0x78, 0x30, 0x76, 0x06,	0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
						0x09, 0x0e, 0x31, 0x69, 0x30, 0x67, 0x30, 0x0b,	0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03,
						0x02, 0x06, 0xc0, 0x30, 0x16, 0x06, 0x03, 0x55,	0x1d, 0x25, 0x01, 0x01, 0xff, 0x04, 0x0c, 0x30,
						0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,	0x07, 0x03, 0x04, 0x30, 0x13, 0x06, 0x03, 0x55,
						0x1d, 0x20, 0x04, 0x0c, 0x30, 0x0a, 0x30, 0x08,	0x06, 0x06, 0x2a, 0x85, 0x03, 0x64, 0x71, 0x01,
						0x30, 0x2b, 0x06, 0x05, 0x2a, 0x85, 0x03, 0x64,	0x6f, 0x04, 0x22, 0x0c, 0x20, 0xd0, 0xa1, 0xd0,
						0x9a, 0xd0, 0x97, 0xd0, 0x98, 0x20, 0x22, 0xd0,	0xa0, 0xd0, 0xa3, 0xd0, 0xa2, 0xd0, 0x9e, 0xd0,
						0x9a, 0xd0, 0x95, 0xd0, 0x9d, 0x20, 0xd0, 0xad, 0xd0, 0xa6, 0xd0, 0x9f, 0x22
};

/* Механизм хеширования: 
CKM_GOSTR3411 			по алгоритму ГОСТ Р 34.11-94
CKM_GOSTR3411_12_256 	по алгоритму ГОСТ Р 34.11-2012, 256 бит
CKM_GOSTR3411_12_512 	по алгоритму ГОСТ Р 34.11-2012, 512 бит */
CK_MECHANISM 	HashMech 	= {CKM_GOSTR3411, NULL_PTR, 0}; 

/* Механизм подписи/проверки подписи:
CKM_GOSTR3410 		по алгоритму ГОСТ Р 34.10-2001 и  ГОСТ Р 34.10-2012, 256 бит
CKM_GOSTR3410_512 	по алгоритму ГОСТ Р 34.10-2012, 512 бит */
CK_MECHANISM    SigVerMech 	= {CKM_GOSTR3410, NULL_PTR, 0};
 
CK_BYTE_PTR 	pbtHash 		= NULL_PTR;    // Указатель на буфер для значения хеша данных
CK_ULONG 		ulHashSize 		= 0;           // Размер буфера в байтах
CK_BYTE_PTR 	pbtSignature 	= NULL_PTR;    // Указатель на буфер, содержащий подпись для исходных данных
CK_ULONG 		ulSignatureSize = 0;           // Размер буфера, содержащего подпись для исходных данных, в байтах
 
while(TRUE)
{
	...
 
	/* Инициализировать операцию хеширования  */
	printf("C_DigestInit");
	rv = pFunctionList->C_DigestInit(hSession,		// Хэндл сессии
								 	 &HashMech);	// Механизм хеширования
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Определить размер значения хеша данных */
	printf("C_Digest step 1");
	rv = pFunctionList->C_Digest( hSession,					// Хэндл сессии
								  pbtReqCert,				// Буфер с запросом на сертификат
								  arraysize(pbtReqCert),	// Размер буфера с запросом на сертификат
								  pbtHash,					// Буфер для значения хеша
								  &ulHashSize);				// Размер значения хеша
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtHash = (CK_BYTE*)malloc(ulHashSize);
	if (pbtHash == NULL)
	{
		printf("Memory allocation for pbtHash failed! \n");
		break;
	}
	memset(pbtHash,
		   0,
		   (ulHashSize * sizeof(CK_BYTE)));

	/* Сформировать хеш от исходных данных */
	printf("C_Digest step 2");
	rv = pFunctionList->C_Digest(hSession,					// Хэндл сессии
								 pbtReqCert,				// Буфер с запросом на сертификат
								 arraysize(pbtReqCert),		// Размер буфера с запросом на сертификат
								 pbtHash,					// Буфер для значения хеша
								 &ulHashSize);				// Размер значения хеша
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Инициализировать операцию подписи данных */
	printf("C_SignInit");
	rv = pFunctionList->C_SignInit( hSession,		// Хэндл сессии
									&SigVerMech,	// Механизм подписи
									hPrivateKey ); 	// Хэндл закрытого ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Определить размер подписи*/
	printf("C_Sign step 1");
	rv = pFunctionList->C_Sign( hSession,			// Хэндл сессии
								pbtHash,			// Буфер с данными для подписи
								ulHashSize,			// Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtSignature = (CK_BYTE*)malloc(ulSignatureSize);
	if (pbtSignature == NULL)
	{
		printf("Memory allocation for pbtSignature failed! \n");
		break;
	}
	memset( pbtSignature,
			0,
			ulSignatureSize * sizeof(CK_BYTE));

	/* Подписать исходные данные */
	printf("C_Sign step 2");
	rv = pFunctionList->C_Sign( hSession,			// Хэндл сессии
								pbHash,				// Буфер с данными для подписи
								ulHashSize,			// Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Распечатать буфер, содержащий подпись */
	printf("Signature buffer is: \n");
	for (i = 0;
		 i < ulSignatureSize;
		 i++)
	{
		printf("%02X ", pbtSignature[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");
	}
	
	break;
}
 

Подпись в формате PKCS#7/CMS с атрибутами на Рутокен PINPad 

Рутокен PINPad распознает, отображает и подписывает не только простые сообщения PKCS#7, но и с дополнительными атрибутами, такими как время подписи или контрасигнатура (подпись другой подписи). 

Сначала Рутокен PINPad отображает данные в отформатированном виде согласно формату PINPADFILE или XML-тегов, затем в исходном виде "как есть", и в конце отображаются подписываемые атрибуты сообщения в кодировке Windows-1251, где любые не отображаемые символы заменяются на экране символом '.'

Процесс подписи сообщения с атрибутами немного удлиняется: сначала исходное сообщение в формате PKCS#7 хешируется функцией С_Digest(), запоминается в памяти Рутокен PINPad и возвращается функцией. После этого сторонним приложением в сообщение добавляются полученный хеш и дополнительные атрибуты, и сообщение отправляется на подпись в Рутокен PINPad. Рутокен PINPad заново вычисляет значение хеша сообщения без атрибутов и сверяет с сохраненным. В случае совпадения обоих значений Рутокен PINPad вычисляет значение хеша для нового сообщения с дополнительными атрибутами через С_Digest() и подписывает функцией C_Sign(). После вызова функции приложение должно самостоятельно внести вычисленную Рутокен PINPad подпись в PKCS#7 конверт.

Подпись запроса на сертификат в формате PKCS#10
/* Сообщение для подписи в виде двоичной строки */
CK_BYTE pbtData[] = { 0x3C, 0x21, 0x50, 0x49, 0x4E, 0x50, 0x41, 0x44, 0x46, 0x49, 0x4C, 0x45, 0x20, 0x52, 0x55, 0x3E,
					  0x3C, 0x4E, 0x3E, 0x0A, 0xD4, 0xC8, 0xCE, 0x3A, 0x3C, 0x56, 0x3E, 0xCF, 0xE5, 0xF2, 0xF0, 0xEE,
					  0xE2, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0xEE, 0xE2, 0xE8, 0xF7, 0x20,
					  0xCC, 0xEE, 0xF1, 0xEA, 0xE2, 0xE0, 0x2C, 0x20, 0xCF, 0xE8, 0xEE, 0xED, 0xE5, 0xF0, 0xF1, 0xEA,
					  0xE0, 0xFF, 0x20, 0xF3, 0xEB, 0x2C, 0x20, 0xE4, 0x2E, 0x20, 0x33, 0x2C, 0x20, 0xEA, 0xE2, 0x2E,
					  0x20, 0x37, 0x32, 0x2C, 0x20, 0xE2, 0xF2, 0xEE, 0xF0, 0xEE, 0xE9, 0x20, 0xF7, 0xE5, 0xF0, 0xE4,
					  0xE0, 0xEA, 0x0A, 0x3C, 0x4E, 0x3E, 0xCF, 0xE5, 0xF0, 0xE5, 0xE2, 0xEE, 0xE4, 0x20, 0xF1, 0xEE,
					  0x20, 0xF1, 0xF7, 0xE5, 0xF2, 0xE0, 0x3A, 0x3C, 0x56, 0x3E, 0x34, 0x32, 0x33, 0x30, 0x31, 0x38,
					  0x31, 0x30, 0x30, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x37, 0x35, 0x32, 0x31, 0x32, 0x0A, 0x3C, 
					  0x4E, 0x3E, 0xD1, 0xF3, 0xEC, 0xEC, 0xE0, 0x3A, 0x3C, 0x56, 0x3E, 0x31, 0x35, 0x30, 0x30, 0x30,
					  0x30, 0x0A, 0x3C, 0x4E, 0x3E, 0xC2, 0xE0, 0xEB, 0xFE, 0xF2, 0xE0, 0x3A, 0x3C, 0x56, 0x3E, 0x52,
					  0x55, 0x52, 0x0A, 0x3C, 0x4E, 0x3E, 0xCD, 0xE0, 0xE8, 0xEC, 0xE5, 0xED, 0xEE, 0xE2, 0xE0, 0xED, 
					  0xE8, 0xE5, 0x20, 0xEF, 0xEE, 0xEB, 0xF3, 0xF7, 0xE0, 0xF2, 0xE5, 0xEB, 0xFF, 0x3A, 0x3C, 0x56,
					  0x3E, 0xC8, 0xE2, 0xE0, 0xED, 0xEE, 0xE2, 0xE0, 0x20, 0xC5, 0xEB, 0xE5, 0xED, 0xE0, 0x20, 0xC8, 
					  0xE2, 0xE0, 0xED, 0xEE, 0xE2, 0xED, 0xE0, 0x0A, 0x3C, 0x4E, 0x3E, 0xCD, 0xEE, 0xEC, 0xE5, 0xF0,
					  0x20, 0xF1, 0xF7, 0xE5, 0xF2, 0xE0, 0x20, 0xEF, 0xEE, 0xEB, 0xF3, 0xF7, 0xE0, 0xF2, 0xE5, 0xEB,
					  0xFF, 0x3A, 0x3C, 0x56, 0x3E, 0x34, 0x30, 0x38, 0x31, 0x37, 0x38, 0x31, 0x30, 0x33, 0x33, 0x38,
					  0x32, 0x39, 0x35, 0x32, 0x30, 0x31, 0x36, 0x31, 0x38, 0x0A, 0x3C, 0x4E, 0x3E, 0xC1, 0xC8, 0xCA,
					  0x20, 0xE1, 0xE0, 0xED, 0xEA, 0xE0, 0x20, 0xEF, 0xEE, 0xEB, 0xF3, 0xF7, 0xE0, 0xF2, 0xE5, 0xEB,
					  0xFF, 0x3A, 0x3C, 0x56, 0x3E, 0x30, 0x34, 0x34, 0x35, 0x32, 0x35, 0x32, 0x32, 0x35, 0x0A, 0x3C,
					  0x4E, 0x3E, 0xCD, 0xE0, 0xE8, 0xEC, 0xE5, 0xED, 0xEE, 0xE2, 0xE0, 0xED, 0xE8, 0xE5, 0x20, 0xE1,
					  0xE0, 0xED, 0xEA, 0xE0, 0x20, 0xEF, 0xEE, 0xEB, 0xF3, 0xF7, 0xE0, 0xF2, 0xE5, 0xEB, 0xFF, 0x3A,
					  0x3C, 0x56, 0x3E, 0xCE, 0xC0, 0xCE, 0x20, 0x27, 0xD1, 0xC1, 0xC5, 0xD0, 0xC1, 0xC0, 0xCD, 0xCA,
					  0x20, 0xD0, 0xCE, 0xD1, 0xD1, 0xC8, 0xC8, 0x27, 0x20, 0xC3, 0x2E, 0x20, 0xCC, 0xCE, 0xD1, 0xCA,
					  0xC2, 0xC0, 0x0A, 0x3C, 0x4E, 0x3E, 0xCD, 0xEE, 0xEC, 0xE5, 0xF0, 0x20, 0xF1, 0xF7, 0xE5, 0xF2,
					  0xE0, 0x20, 0xE1, 0xE0, 0xED, 0xEA, 0xE0, 0x20, 0xEF, 0xEE, 0xEB, 0xF3, 0xF7, 0xE0, 0xF2, 0xE5,
					  0xEB, 0xFF, 0x3A, 0x3C, 0x56, 0x3E, 0x33, 0x30, 0x31, 0x30, 0x31, 0x38, 0x31, 0x30, 0x34, 0x30,
					  0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, 0x32, 0x35, 0x0A, 0x3C, 0x4E, 0x3E, 0xCD, 0xE0,
					  0xE7, 0xED, 0xE0, 0xF7, 0xE5, 0xED, 0xE8, 0xE5, 0x20, 0xEF, 0xEB, 0xE0, 0xF2, 0xE5, 0xE6, 0xE0,
					  0x3A, 0x3C, 0x56, 0x3E, 0xEF, 0xE5, 0xF0, 0xE5, 0xE2, 0xEE, 0xE4, 0x20, 0xEB, 0xE8, 0xF7, 0xED,
					  0xFB, 0xF5, 0x20, 0xF1, 0xF0, 0xE5, 0xE4, 0xF1, 0xF2, 0xE2 };
 
/* Сообщение для подписи с атрибутами в виде двоичной строки */
CK_BYTE pbtDataAttr[] = { 0x31, 0x69, 0x30, 0x18, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x03, 0x31,
						  0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30, 0x1c, 0x06, 0x09,
						  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x05, 0x31, 0x0f, 0x17, 0x0d, 0x31, 0x35, 0x30,
						  0x33, 0x30, 0x36, 0x31, 0x34, 0x35, 0x39, 0x35, 0x36, 0x5a, 0x30, 0x2f, 0x06, 0x09, 0x2a, 0x86,
						  0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x04, 0x31, 0x22, 0x04, 0x20, 0xAC, 0x0E, 0x61, 0x2C, 0x98, 
						  0xC4, 0x27, 0x45, 0x74, 0xDF, 0xD6, 0xAB, 0xCB, 0xAB, 0x88, 0x85, 0xEF, 0x19, 0x48, 0x8C, 0xD8, 
						  0x20, 0x8C, 0xBA, 0x80, 0x3E, 0x03, 0xFB, 0xFF, 0x13, 0x46, 0x03 };
 
/*  Механизм хеширования: 
CKM_GOSTR3411 			по алгоритму ГОСТ Р 34.11-94
CKM_GOSTR3411_12_256 	по алгоритму ГОСТ Р 34.11-2012, 256 бит
CKM_GOSTR3411_12_512 	по алгоритму ГОСТ Р 34.11-2012, 512 бит */
CK_MECHANISM 	HashMech 	= {CKM_GOSTR3411, NULL_PTR, 0}; 

/* Механизм подписи/проверки подписи:
CKM_GOSTR3410 		по алгоритму ГОСТ Р 34.10-2001 и  ГОСТ Р 34.10-2012, 256 бит
CKM_GOSTR3410_512 	по алгоритму ГОСТ Р 34.10-2012, 512 бит */
CK_MECHANISM    SigVerMech 	= {CKM_GOSTR3410, NULL_PTR, 0};
 
CK_BYTE_PTR 	pbtHash 		= NULL_PTR;    // Указатель на буфер для значения хеша данных
CK_ULONG 		ulHashSize 		= 0;           // Размер буфера в байтах
CK_BYTE_PTR 	pbtSignature 	= NULL_PTR;    // Указатель на буфер, содержащий подпись для исходных данных
CK_ULONG 		ulSignatureSize = 0;           // Размер буфера, содержащего подпись для исходных данных, в байтах
 
while(TRUE)
{
	...
 
	/* Инициализировать операцию хеширования  */
	printf("C_DigestInit");
	rv = pFunctionList->C_DigestInit(hSession,		// Хэндл сессии
								 	 &HashMech);	// Механизм хеширования
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Определить размер значения хеша данных */
	printf("C_Digest step 1");
	rv = pFunctionList->C_Digest( hSession,				// Хэндл сессии
								  pbtData,				// Буфер с сообщением 
								  arraysize(pbtData),	// Размер буфера с сообщением
								  pbtHash,				// Буфер для значения хеша
								  &ulHashSize);			// Размер значения хеша
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtHash = (CK_BYTE*)malloc(ulHashSize);
	if (pbtHash == NULL)
	{
		printf("Memory allocation for pbtHash failed! \n");
		break;
	}
	memset(pbtHash,
		   0,
		   (ulHashSize * sizeof(CK_BYTE)));

	/* Сформировать хеш от исходных данных */
	printf("C_Digest step 2");
	rv = pFunctionList->C_Digest(hSession,				// Хэндл сессии
								 pbtData,				// Буфер с запросом на сертификат
								 arraysize(pbtData),	// Размер буфера с запросом на сертификат
								 pbtHash,				// Буфер для значения хеша
								 &ulHashSize);			// Размер значения хеша
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Инициализировать операцию хеширования  */
	printf("C_DigestInit");
	rv = pFunctionList->C_DigestInit(hSession,		// Хэндл сессии
								 	 &HashMech);	// Механизм хеширования
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Определить размер значения хеша данных */
	printf("C_Digest step 1");
	rv = pFunctionList->C_Digest( hSession,					// Хэндл сессии
								  pbtDataAttr,				// Буфер с сообщением с атрибутами
								  arraysize(pbtDataAttr),	// Размер буфера с сообщением
								  pbtHash,					// Буфер для значения хеша
								  &ulHashSize);				// Размер значения хеша
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	memset(pbtHash,
		   0,
		   (ulHashSize * sizeof(CK_BYTE)));

	/* Сформировать хеш от исходных данных */
	printf("C_Digest step 2");
	rv = pFunctionList->C_Digest(hSession,					// Хэндл сессии
								 pbtDataAttr,				// Буфер с запросом на сертификат
								 arraysize(pbtDataAttr),	// Размер буфера с запросом на сертификат
								 pbtHash,					// Буфер для значения хеша
								 &ulHashSize);				// Размер значения хеша
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Инициализировать операцию подписи данных */
	printf("C_SignInit");
	rv = pFunctionList->C_SignInit( hSession,		// Хэндл сессии
									&SigVerMech,	// Механизм подписи
									hPrivateKey ); 	// Хэндл закрытого ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Определить размер подписи*/
	printf("C_Sign step 1");
	rv = pFunctionList->C_Sign( hSession,			// Хэндл сессии
								pbtHash,			// Буфер с данными для подписи
								ulHashSize,			// Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");


	if (pbtSignature == NULL)
	{
		printf("Memory allocation for pbtSignature failed! \n");
		break;
	}
	memset( pbtSignature,
			0,
			ulSignatureSize * sizeof(CK_BYTE));

	/* Подписать исходные данные */
	printf("C_Sign step 2");
	rv = pFunctionList->C_Sign( hSession,			// Хэндл сессии
								pbHash,				// Буфер с данными для подписи
								ulHashSize,			// Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Распечатать буфер, содержащий подпись */
	printf("Signature buffer is: \n");
	for (i = 0;
		 i < ulSignatureSize;
		 i++)
	{
		printf("%02X ", pbtSignature[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");
	}
	
	break;
}
 

Проверка подписи

Для проверки подписи данных служат функции C_VerifyInit() и C_Verify(). Сначала операцию подписи нужно инициализировать через C_VerifyInit(), передав в нее идентификатор сессии, механизма и открытого ключа. Затем можно проверить подпись функцией C_Verify()

Совместные механизмы хеширования и подписи (например, CKM_GOSTR3410_WITH_GOSTR3411) не поддерживаются  C_VerifyInit(). В C_Verify() передаются предварительно прохешированные функцией C_Digest() исходные данные.

Проверка подписи по алгоритму ГОСТ Р 34.10-2001 / ГОСТ Р 34.10-2012
while(TRUE)
{
	...
	/* Инициализировать операцию проверки подписи */
	printf(" C_VerifyInit");
	rv = pFunctionList->C_VerifyInit(hSession,    	// Хэндл сессии
									 &SigVerMech,	// Механизм подписи
									 hPublicKey);	// Хэндл открытого ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	
	/* Проверить подпись для исходных данных */
	printf(" C_Verify");
	rv = pFunctionList->C_Verify(hSession,   		// Хэндл сессии
								 pbHash,			// Буфер с значением хеша исходногосообщения
								 ulHashSize,		// Длина буфера
								 pbtSignature,		// Буфер с подписью
								 ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	break;
}
...
if (pbtSignature)
{
	free(pbtSignature);
	pbtSignature = NULL_PTR;
}
 
if (pbHash)
{
	free(pbHash);
	pbHash= NULL_PTR;
}

Шифрование и расшифрование

Поддерживаемые механизмы

Устройства Рутокен поддерживают следующие механизмы шифрования:

  • CKM_GOST28147_ECB для шифрования алгоритмом ГОСТ 28147-89 в режиме простой замены,
  • CKM_GOST28147 для шифрования алгоритмом ГОСТ 28147-89 в режиме гаммирования с обратной связью, (программное и аппаратное) 
  • CKM_RSA_PKCS для шифрования алгоритмом RSA.

Внимание!

Так как в режиме простой замены (механизм CKM_GOST28147_ECB) шифрование каждого блока данных осуществляется одним и тем же ключом, этот механизм должен применяться только для данных небольшого размера (например, ключей). В противном случае стойкость алгоритма снижается.

Для того, чтобы шифрование/расшифрование было выполнено аппаратно самим устройством, ключ должен находиться на токене (то есть атрибут CKA_TOKEN используемого для шифрования ключа должен быть равен значению TRUE). Если атрибут CKA_TOKEN ключа, который используется для шифрования/расшифрования, равен FALSE, то операция будет выполняться программно.

Шифрование данных

Для шифрования данных одним блоком служат функции C_EncryptInit() и C_Encrypt(), для поточного – С_EncryptInit(), C_EncryptUpdate() и C_EncryptFinal(). 

Сначала операцию шифрования нужно инициализировать вызовом функции C_EncryptInit(), передав в нее идентификатор сессии, механизма и секретного ключа. В параметрах механизма для ГОСТ 28147-89 можно задать вектор инициализации и его длину.

Далее шифрование можно выполнить для всего блока данных целиком, вызвав функцию C_Encrypt(), или по частям, вызывая  C_EncryptUpdate() для каждого непоследнего блока и  C_EncryptFinal() для завершающего блока. Если в C_EncryptFinal() передать пустой блок данных, то функция вернет суммарный размер всех блоков зашифрованных данных.

Пример шифрования данных по алгоритму ГОСТ 28147-89 

Шифрование данных по алгоритму ГОСТ 28147-89 в режиме простой замены
/* Данные для шифрования */
CK_BYTE pbtData[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	                  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
	                  0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
	                  0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00 };
 
/* Механизм шифрования/расшифрования по алгоритму ГОСТ 28147-89 в режиме простой замены */
CK_MECHANISM	EncDecMech    		= {CKM_GOST28147_ECB, NULL_PTR, 0};

 
CK_BYTE_PTR 	pbtEncryptedData 	= NULL_PTR;              // Указатель на буфер, содержащий зашифрованные данные
CK_ULONG 		ulEncryptedDataSize = 0;                     // Размер буфера с зашифрованными данными, в байтах 
 
while(TRUE)
{
	...
	/* Инициализировать операцию шифрования */
	printf("C_EncryptInit");
	rv = pFunctionList->C_EncryptInit(hSession,		// Хэндл сессии
			                          &EncDecMech,	// Механизм шифрования
			                          hSecKey);		// Хэндл секретного ключа
													// Если атрибут CKA_TOKEN равен TRUE, то шифрование будет выполнено аппаратно, 
													// в противном случае - программно.
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Получить размер зашифрованного текста */
	printf("Getting encrypted data size");
	rv = pFunctionList->C_Encrypt(hSession,					// Хэндл сессии 
			                      pbtData,					// Буфер с открытыми данными для шифрования
			                      arraysize(pbtData),		// Длина буфера с открытыми данными, в байтах
			                      NULL,						// Буфер с зашифрованными данными
			                      &ulEncryptedDataSize);	// Длина буфера с зашифрованными данными
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtEncryptedData = (CK_BYTE*)malloc(ulEncryptedDataSize);
	if (pbtEncryptedData == NULL)
	{
		printf("Memory allocation for pbtEncryptedData failed! \n");
		break;
	}
	memset(pbtEncryptedData,
		   0,
		   (ulEncryptedDataSize * sizeof(CK_BYTE)));
	
	/* Зашифровать открытый текст */
	printf("C_Encrypt");
	rv = pFunctionList->C_Encrypt(hSession,					// Хэндл сессии 
			                      pbtData,					// Буфер с открытыми данными для шифрования
			                      arraysize(pbtData),		// Длина буфера с открытыми данными, в байтах
			                      pbtEncryptedData,			// Буфер с зашифрованными данными
			                      &ulEncryptedDataSize);	// Длина буфера с зашифрованными данными
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	printf("Encrypted buffer is:\n");
	for (i = 0;
		 i < ulEncryptedDataSize;
		 i++)
	{
		printf("%02X ", pbtEncryptedData[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");
	}
	break;
}
 
Шифрование данных по алгоритму ГОСТ 28147-89 в режиме гаммирования с обратной связью
/* Данные для шифрования */
CK_BYTE 	pbtData[] 	= { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	                 	 	0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
	                 		0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
	                  		0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00 };
 
CK_BYTE	 	IV[]  	 	= {1, 2, 3, 4, 5, 6, 7, 8};		// Значение вектора инициализации
CK_ULONG 	ulIVLen 	= 8;                           	// Длина вектора инициализации


/* Механизм шифрования/расшифрования по алгоритму ГОСТ 28147-89 в режиме гаммирования с обратной связью */
CK_MECHANISM	EncDecStreamMech   	= {CKM_GOST28147, IV, ulIVLen};

CK_BYTE_PTR 	pbtEncryptedData 	= NULL_PTR;         // Указатель на буфер, содержащий зашифрованные данные
CK_ULONG 		ulEncryptedDataSize = 0;                // Размер буфера с зашифрованными данными, в байтах 
CK_ULONG 		ulBlockSize 		= 32;				// Размер блока данных, в байтах
CK_ULONG 		ulCurrentPosition 	= 0;				// Текущее начало блока
CK_ULONG 		ulRestLen			= 0;				// Размер оставшегося буфера, в байтах
 
while(TRUE)
{
	...
	/* Инициализировать операцию шифрования */
	printf("C_EncryptInit");
	rv = pFunctionList->C_EncryptInit(hSession,				// Хэндл сессии
			                          &EncDecStreamMech,	// Механизм шифрования
			                          hSecKey);				// Хэндл секретного ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Зашифровать открытый текст */
	ulEncryptedDataSize = arraysize(pbtData);
	ulRestLen = arraysize(pbtData);
	pbtEncryptedData = (CK_BYTE*)malloc(ulEncryptedDataSize);
	if (pbtEncryptedData == NULL)
	{
		printf("Memory allocation for pbtEncryptedData failed! \n");
		break;
	}
	memset( pbtEncryptedData,
			0,
			(ulEncryptedDataSize * sizeof(CK_BYTE)));

	while (ulRestLen)
	{
		if (ulBlockSize > ulRestLen)
			ulBlockSize = ulRestLen;
		printf("Block size: %u B (Total: %u of %u) ", ulBlockSize, ulCurrentPosition + ulBlockSize, ulEncryptedDataSize);
		rv = pFunctionList->C_EncryptUpdate(hSession,								// Хэндл сессии
											pbtData + ulCurrentPosition,			// Буфер с блоком данных для шифрования
											ulBlockSize,							// Размер блока, в байтах
											pbtEncryptedData + ulCurrentPosition,	// Буфер с блоком зашифрованных данных
											&ulBlockSize);							// Размер блока, в байтах
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			break;
		}
		printf(" -> OK\n");

		ulCurrentPosition += ulBlockSize;
		ulRestLen -= ulBlockSize;
	}		
	if (rv != CKR_OK)
		break;

	printf("Finalizing encryption");
	rv = pFunctionList->C_EncryptFinal( hSession, 					// Хэндл сессии
										NULL_PTR,					// Буфер с последним блоком данных 
										&ulEncryptedDataSize);		// Длина буфера
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	/* Распечатать буфер, содержащий зашифрованные данные*/
	printf("Encrypted buffer is:\n");
	for (i = 0;
		 i < ulEncryptedDataSize;
	     i++)
	{
		printf("%02X ", pbtEncryptedData[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");
	}

	break;
}

Расшифрование данных

Для расшифрования данных служат функции C_DecryptInit() и C_Decrypt() для любого режима шифрования.

Сначала операцию расшифрования нужно инициализировать вызовом функции C_DecryptInit(), передав в нее идентификатор сессии, механизма и секретного ключа. В параметрах механизма для ГОСТ 28147-89 можно задать вектор инициализации и его длину.

Далее расшифрование выполняется вызовом функции C_Decrypt() с передачей в нее зашифрованные данные. Размер расшифрованных данных можно узнать, вызвав C_Decrypt()  с пустым указателем вместо указателя на буфера для расшифрованных данных.

Пример расшифрования данных по алгоритму ГОСТ 28147-89 

Расшифрование данных по алгоритму ГОСТ 28147-89
while(TRUE)
{
	...
	/* Инициализировать операцию расшифрования */
	printf("C_DecryptInit");
	rv = pFunctionList->C_DecryptInit(hSession,				// Хэндл сессии
			                          &EncDecStreamMech,	// Механизм расшифрования
			                          hSecKey);				// Хэндл секретного ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	
	/* Расшифровать шифротекст */
	printf("Getting decrypted data size");
	rv = pFunctionList->C_Decrypt(hSession,					// Хэндл сессии
			                      pbtEncryptedData,			// Буфер с зашифрованными данными
			                      ulEncryptedDataSize,		// Размер зашифрованных данных
			                      NULL_PTR,					// Буфер с расшифрованными данными
			                      &ulDecryptedDataSize);	// Размер расшифрованных данных
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	pbtDecryptedData = (CK_BYTE*)malloc(ulDecryptedDataSize);
	if (pbtDecryptedData == NULL)
	{
		printf("Memory allocation for pbtDecryptedData failed! \n");
		break;
	}
	memset(pbtDecryptedData,
	       0,
	       (ulDecryptedDataSize * sizeof(CK_BYTE)));
	
	printf("C_Decrypt");
	rv = pFunctionList->C_Decrypt(hSession,					// Хэндл сессии
			                      pbtEncryptedData,			// Буфер с зашифрованными данными
			                      ulEncryptedDataSize,		// Размер зашифрованных данных
	                              pbtDecryptedData,			// Буфер с расшифрованными данными
	                              &ulDecryptedDataSize);	// Размер расшифрованных данных
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n"); 
			
	/* Распечатать буфер, содержащий расшифрованный текст */
	printf("Decrypted buffer is:\n");
	for (i = 0;
		 i < ulDecryptedDataSize;
	     i++)
	{
		printf("%02X ", pbtDecryptedData[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");
	}
	break;
}
 	
if (pbtEncryptedData)
{
	free(pbtEncryptedData);
	pbtEncryptedData = NULL_PTR;
}
if (pbtDecryptedData)
{
	free(pbtDecryptedData);
	pbtDecryptedData = NULL_PTR;
}

Управление устройством

Форматирование токена

Форматирование токена возможно двумя функциями: функцией расширения C_EX_InitToken(), которая полностью очищает память токена и сбрасывает все настройки и PIN-коды, и стандартной функцией C_InitToken(), которая удаляет только объекты PKCS#11.

Все параметры форматирования задаются структурой типа CK_RUTOKEN_INIT_PARAM, указатель на которую вместе с PIN-кодом Администратора передаются функции C_EX_InitToken()

Форматирование Рутокен функцией C_EX_InitToken()
/* Максимальное количество попыток ввода PIN-кода для Администратора */
#define MAX_ADMIN_RETRY_COUNT           10
/* Максимальное количество попыток доступа для Пользователя */
#define MAX_USER_RETRY_COUNT            10    
/* Новый DEMO PIN-код Пользователя Рутокен */
static CK_UTF8CHAR      NEW_USER_PIN[]  = {'5', '5', '5', '5', '5', '5', '5', '5'};
/* DEMO PIN-код Администратора Рутокен */
static CK_UTF8CHAR      SO_PIN[]        = {'8', '7', '6', '5', '4', '3', '2', '1'};
/* DEMO метка Рутокен */
static CK_CHAR TOKEN_STD_LABEL[]        = {"!!!Sample Rutoken label!!!"};
 
CK_RUTOKEN_INIT_PARAM initInfo_st;        // Структура данных типа CK_RUTOKEN_INIT_PARAM, содержащая параметры для работы функции C_EX_InitToken
CK_BBOOL bIsRutokenECP = FALSE;           // Вспомогательная переменная для хранения признака типа токена

/* Заполнение полей структуры CK_RUTOKEN_INIT_PARAM */		
memset(&initInfo_st,
   	   0,
  	   sizeof(CK_RUTOKEN_INIT_PARAM));
 
initInfo_st.ulSizeofThisStructure = sizeof(CK_RUTOKEN_INIT_PARAM); 				// Размер структуры
initInfo_st.UseRepairMode = 0;													// 0 - для форматирования требуются права Администратора, !0 - нет
initInfo_st.pNewAdminPin = SO_PIN;												// Новый PIN-код Администратора
initInfo_st.ulNewAdminPinLen = sizeof(SO_PIN);									// Длина нового PIN-кода Администратора
initInfo_st.pNewUserPin = NEW_USER_PIN;											// Новый PIN-код Пользователя
initInfo_st.ulNewUserPinLen = sizeof(NEW_USER_PIN);								// Длина нового PIN-кода Администратора
initInfo_st.ulMinAdminPinLen = bIsRutokenECP ? 6 : 1;							// Минимальная длина PIN-кода Администратора
initInfo_st.ulMinUserPinLen = bIsRutokenECP ? 6 : 1;							// Минимальная длина PIN-кода Администратора
initInfo_st.ChangeUserPINPolicy = 												// Политика смены PIN-кода Пользователя - Пользователь и Администратор
	(TOKEN_FLAGS_ADMIN_CHANGE_USER_PIN | TOKEN_FLAGS_USER_CHANGE_USER_PIN);
initInfo_st.ulMaxAdminRetryCount = MAX_ADMIN_RETRY_COUNT;						// Максимальное количество неудачных попыток аутентификации Администратора
initInfo_st.ulMaxUserRetryCount = MAX_USER_RETRY_COUNT;							// Максимальное количество неудачных попыток аутентификации Пользователя
initInfo_st.pTokenLabel = TOKEN_STD_LABEL;										// Метка токена
initInfo_st.ulLabelLen = sizeof(TOKEN_STD_LABEL);								// Длина метки токена
 
/* Инициализация токена */
printf("Initializing token");
rv = pFunctionListEx->C_EX_InitToken(aSlots[0],				// Идентификатор слота с подключенным токеном
		                             SO_PIN,				// Текущий PIN-кода Администратора
		                             arraysize(SO_PIN),		// Длина текущего PIN-кода Администратора
		                             &initInfo_st);			// Указатель на структуру с параметрами форматирования
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");

При форматировании стандартной функцией C_InitToken() возможно задать только новый PIN-код Администратора и метку токена. После форматирования следует задать PIN-код Пользователя  функцией C_InitPIN(), поскольку после форматирования через C_InitToken() его значение остается незаданным. 

Форматирование Рутокен функцией C_InitToken()
/* DEMO метка Rutoken */
static CK_CHAR TOKEN_LABEL[]        = { 'M', 'y', ' ', 'R', 'u', 't', 'o', 'k',
                                        'e', 'n', ' ', ' ', ' ', ' ', ' ', ' ',
                                        ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                                        ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' };		
 
while(TRUE)
{
	...
	
	/* Инициализировать токен */
	printf("C_InitToken");
	rv = pFunctionList->C_InitToken(aSlots[0],        // Идентификатор слота с подключенным токеном
		                            SO_PIN,			  // Новый PIN-код Администратора
		                            sizeof(SO_PIN),	  // Длина нового PIN-кода Администратора
		                            TOKEN_LABEL);	  // Новая метка токена
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	...
	
	/* Инициализировать PIN-код Пользователя */
	printf(" Initializing User PIN ");
	rv = pFunctionList->C_InitPIN(hSession,    			// Хэндл открытой сессии с правами Администратора
		                          USER_PIN,				// Новый PIN-код пользователя
		                          sizeof(USER_PIN));    // Длина нового PIN-кода пользователя
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	break;
}

Установка и смена локального PIN-кода

Помимо двух глобальных ролей Администратора и Пользователя, устройства Рутокен поддерживают до 29 PIN-кодов Локальных Пользователей для разных технических нужд.

Установка или смена локального PIN-кода должна выполняться Пользователем Рутокен и не требует открытой сессии и авторизации – PIN-код Пользователя передается прямо в функцию C_EX_SetLocalPin().

Установка локального PIN-кода
CK_SLOT_ID  	slotID;				// Идентификатор слота, к которому подключен токен
CK_UTF8CHAR_PTR pUserPin;     		// PIN-код Пользователя Рутокен
CK_ULONG    	ulUserPinLen;		// Длина  PIN-кода Пользователя Рутокен
CK_UTF8CHAR_PTR pNewLocalPin;		// Локальный PIN-код
CK_ULONG    	ulNewLocalPinLen; 	// Длина локального PIN-кода
CK_ULONG    	ulLocalID;         	// Идентификатор локального PIN-кода
 
printf("Setting Local PIN-code");
rv = pfGetFunctionListEx->C_EX_SetLocalPIN( slotID,				// Идентификатор слота, к которому подключен Рутокен
			                  				pUserPin,			// Текущий PIN-код Пользователя Рутокен
			                  				ulUserPinLen,		// Длина текущего PIN-кода Пользователя Рутокен
			                  				pNewLocalPin,		// Новый локальный PIN-код
			                  				&ulNewLocalPinLen, 	// Длина нового локального PIN-кода
											ulLocalID);	    	// Идентификатор локального PIN-кода
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");	

Разблокировка PIN-кода Пользователя

В целях безопасности для пользовательского PIN-кода введен счетчик попыток неправильного ввода. При превышении заданного максимума попыток PIN-код блокируется и дальнейшая аутентификация с правами Пользователя становится невозможной. Сброс счетчика попыток неудачного входа осуществляется функцией C_EX_UnblockUserPIN(), в которую передается хэндл сессии с предварительно выполненным входом с правами Администратора.

Счетчик автоматически сбрасывается при вводе правильного PIN-кода Пользователя, если не был превышен заданный максимум попыток.

Разблокировка токена
/* Разблокировать PIN-код Пользователя */
printf("Unlock User PIN");
rv = pFunctionListEx->C_EX_UnblockUserPIN(hSession); // Хэндл открытой с правами Администратора сессии
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");

Работа с журналом Рутокен PINPad

Рутокен PINPad предоставляет возможность подтверждения того, что операция подписи была выполнена конкретным устройством Рутокен PINPad. Для этого в памяти устройства после каждой успешной операции подписи сохраняется подробная информация о выполненной операции (журнал операции), которую затем можно извлечь и подписать специальной ключевой парой. Журнал и ключевая пара хранятся в защищенном разделе памяти и остаются доступными после выполнения любых операций конечным пользователем, в том числе и форматирования устройства. 

Атрибуты ключевой пары журнала

Для того, чтобы ключевая пара была записана в защищенном от форматирования разделе памяти, шаблоны закрытого и открытого ключа ГОСТ 34.10-2011, используемые для генерации ключевой пары журнала, должны содержать атрибут CKA_VENDOR_KEY_JOURNAL со значением СК_TRUE.

Перед генерацией ключевой пары должна быть выполнена авторизация на Рутокен PINPad с правами Пользователя.

Удаление такой ключевой пары средствами PKCS#11 невозможно. 

Атрибуты ключевой пары журнала
CK_OBJECT_CLASS GostPubKey       = CKO_PUBLIC_KEY;
CK_OBJECT_CLASS GostPrivKey      = CKO_PRIVATE_KEY;
CK_KEY_TYPE     GostKeyType      = CKK_GOSTR3410;
CK_BBOOL        bTrue            = CK_TRUE; 
CK_BBOOL        bFalse           = CK_FALSE; 
 
CK_ATTRIBUTE	PubKeyJournal [] =
{
	{ CKA_CLASS, &GostPubKey, sizeof(CK_OBJECT_CLASS) },
	{ CKA_KEY_TYPE, &GostKeyType, sizeof(CK_KEY_TYPE) },
	{ CKA_TOKEN, &bTrue, sizeof(CK_BBOOL) },
	{ CKA_PRIVATE, &bFalse, sizeof(CK_BBOOL) },
	{ CKA_VENDOR_KEY_JOURNAL, &bTrue, sizeof(CK_BBOOL) },
};

CK_ATTRIBUTE	PriKeyJournal [] =
{
	{ CKA_CLASS, &GostPrivKey, sizeof(CK_OBJECT_CLASS) },
	{ CKA_KEY_TYPE, &GostKeyType, sizeof(CK_KEY_TYPE) },
	{ CKA_TOKEN, &bTrue, sizeof(CK_BBOOL) },
	{ CKA_PRIVATE, &bTrue, sizeof(CK_BBOOL) },
	{ CKA_VENDOR_KEY_JOURNAL, &bTrue, sizeof(CK_BBOOL) },
};

Получение записи журнала

Журнал позволяет хранить только одну запись с информацией о последней выполненной операции подписи. Неудачные попытки подписи в журнале не фиксируются. Описание формата записи журнала доступно по ссылке Формат записи журнала.

Для получения записи журнала необходимо вызвать функцию C_EX_GetJournal() с переданными в нее значением слота, к которому подключен Рутокен PINPad, и данными буфера, в который будет возвращена запись журнала. Вызов функции C_EX_GetJournal() с указателем на NULL вместо буфера вернет длину записи журнала.

Перед запросом информации о журнале необходимо выполнить авторизацию на Рутокен PINPad с правами владельца ключевой пары журнала (Пользователя).

Получение значения журнала
CK_BYTE_PTR           pJournal = NULL_PTR;       	// Указатель на значение журнала
CK_ULONG         	  ulJournalSize = 0;   			// Размер журнала
 
while(TRUE)
{
...
    /* Получить размер журнала */
    printf("Getting journal size");
    rv = pFunctionListEx->C_EX_GetJournal(aSlots[0],  	  // Хэндл слота с подключенным токеном
										  NULL_PTR,		  // Указатель на журнал
										  &ulJournalSize);// Размер журнала
    if (rv != CKR_OK)
    {
        printf(" -> Failed\n");
        break;
    }
    printf(" -> OK\n");
 
    pJournal = (CK_BYTE*)malloc(ulSlotCount * sizeof(CK_BYTE));
    if (pJournal == NULL)
    {
        printf("Memory allocation for pJournal failed! \n");
        break;
    }
    memset(pJournal, 0, (ulJournalSize * sizeof(CK_BYTE)));
 
    /* Получить журнал */
    printf("Getting journal");
    rv = pFunctionListEx->C_EX_GetJournal(aSlots[0],  	  // Хэндл слота с подключенным токеном
										  pJournal,		  // Указатель на журнал
										  &ulJournalSize);// Размер журнала
    if (rv != CKR_OK)
    {
        printf(" -> Failed %X\n", (int)rv);
        break;
    }
    printf(" -> OK\n");
 
    ...
    break;
}

Пример ниже разбирает полученную запись согласно описанному формату записи журнала. 

Парсинг записи журнала Рутокен PINPad
typedef enum {
	TLVTJournal = 0x80,
	TLVTHash = 0xAA,
	TLVTSignature = 0xB6,
	TLVTOperationInfo = 0x85,
	TLVTPinPadId = 0x83,
	TLVTTagTableInfo = 0x86
} TLVType;

typedef enum {
	OTSignature = 0x01,
} OperationType;

typedef enum 
{
	RSFSTypeGCHV = 0x01,
	RSFSTypeLCHV = 0x21,
	RSFSTypeSGOST = 0x02,
	RSFSTypeAES = 0x22,
	RSFSType3DES = 0x42,
	RSFSTypeAGOSTPR = 0x03,
	RSFSTypeRSAPR = 0x23,
	RSFSTypeAGOST_512PR = 0x43,
	RSFSTypeAGOSTPU = 0x13,
	RSFSTypeRSAPU = 0x33,
	RSFSTypeAGOST_512PU = 0x53,
	RSFSTypeX509CER = 0x14,
	RSFSTypeACGOST = 0x05,
	RSFSTypeSE = 0x1F
} RSFSubType;

typedef enum
{
	RSFFAllowKeyExch = 0x01,
	RSFFKeyForSM = 0x02,
	RSFFKeySignsJournal = 0x04,
	RSFFKeyImported = 0x08,
	RSFFKeyPINEnter = 0x10,
	RSFFKeyConfirmOp = 0x20
} RSFFlags;

typedef enum
{
	OIDeviceHash = 0x01,
	OIKeyPINEnter = 0x02,
	OIPIN2Entered = 0x10,
	OIOpConfirmed = 0x20
} OperationInfoFlags;

typedef enum
{
	PPPPIN2CashDisabled = 0x01,
	PPPDefPINNotRequested = 0x02,
	PPPChangePINOnScreen = 0x04
} PinPadPolicyFlags;

#pragma pack(push, 1)
typedef struct OperationInfo_ {
	uint8_t operationType;
	uint8_t rsfType;
	uint8_t rsfFlags;
	uint8_t oiFlags;
	uint8_t pppFlags;
	uint8_t reserved;
	uint8_t rsfIDHigh;
	uint8_t rsfIDLow;
	uint32_t ulSignatureCount; 
} OperationInfo;

typedef struct TagTableInfo_ {
	uint16_t tableNumber;
	uint16_t tableEncoding;
	uint8_t tableHash[32];
} TagTableInfo;

#pragma pack(pop)
typedef struct TLVPrinter_ {
	TLVType type;
	void (*print)(const CK_BYTE*);
} TLVPrinter;

uint16_t be2les(uint16_t be) {
	uint16_t le;
	int i = 0;
	for (i = 0; i < sizeof(be); ++i) {
		le = (le << 8) | (be & 0xFF);
		be >>= 8;
	}
	return le;
}

uint32_t be2lei(uint32_t be) {
	uint32_t le;
	int i = 0;
	for (i = 0; i < sizeof(be); ++i) {
		le = (le << 8) | (be & 0xFF);
		be >>= 8;
	}
	return le;
}

const CK_BYTE* FindTlVElement(CK_BYTE ubTag, const CK_BYTE* pFirstTLVElt, CK_ULONG ulLen)
{
	const CK_BYTE* pLastValueByte = pFirstTLVElt + ulLen;
	const CK_BYTE* pTmpTag = pFirstTLVElt;
	while (pTmpTag < pLastValueByte)
	{
		if (*pTmpTag == ubTag)
			return pTmpTag;
		pTmpTag += *(pTmpTag + 1) + 2;
	}
	return 0; // tag is not found
}

const CK_BYTE* GetTlVValue(const CK_BYTE* pTLVElt) {
	return pTLVElt+2;
}

CK_ULONG GetTlVLen(const CK_BYTE* pTLVElt) {
	return *(pTLVElt+1);
}

void printHexBuffer(const CK_BYTE* pBuffer, const CK_ULONG ulLen, const CK_ULONG offset) {
	unsigned int i = 0, j=0;
	for (i = 0; i < ulLen; ++i)
	{
		if(i%8 == 0)
			for(j = 0; j < offset; ++j)
				printf("\t");
		printf("%02X ", pBuffer[i]);
		if ((i + 1) % 8 == 0 || (i + 1) == ulLen)
			printf("\n");
	}
}

void printOperationType(OperationType operationType)
{
	printf("\t\tOperation Type: ");
	switch(operationType)  {
		case OTSignature: printf("Signature"); break;
		default: printf("unknown (%02X)", operationType);
	}
	printf("\n");
}

void printRSFType(RSFSubType rsfType) {
	printf("\t\tRSF Type: ");
	switch(rsfType)  {
		case RSFSTypeGCHV: printf("GCHV (Global PIN)"); break;
		case RSFSTypeLCHV: printf("LCHV (Local PIN)"); break;
		case RSFSTypeSGOST: printf("SGOST (GOST 28147-89 key)"); break;
		case RSFSTypeAES: printf("AES"); break;
		case RSFSType3DES: printf("3DES"); break;
		case RSFSTypeAGOSTPR: printf("AGOST_PR (GOST 34.10-2001 private key)"); break;
		case RSFSTypeRSAPR: printf("RSA_PR (RSA private key)"); break;
		case RSFSTypeAGOST_512PR: printf("AGOST2012_PR (GOST 34.10-2012 private key)"); break;
		case RSFSTypeAGOSTPU: printf("AGOST_PU (GOST 34.10-2001 public key)"); break;
		case RSFSTypeRSAPU: printf("RSA_PU (RSA public key)"); break;
		case RSFSTypeAGOST_512PU: printf("AGOST2012_PU (GOST 34.10-2012 public key)"); break;
		case RSFSTypeX509CER: printf("X509 (X509 certificate)"); break;
		case RSFSTypeACGOST: printf("ACGOST (GOST 28147-89 key)"); break;
		case RSFSTypeSE: printf("SE (SE environment object)"); break;
		default: printf("unknown (%02X)", rsfType);
	}
	printf("\n");
}

void printRSFFlags(RSFFlags rsfFlags) {
	printf("\t\tRSF Flags: \n");
	if (rsfFlags&RSFFAllowKeyExch) printf("\t\t\tKey Exchange Allowed\n");
	if (rsfFlags&RSFFKeyForSM) printf("\t\t\tKey used for SM\n");
	if (rsfFlags&RSFFKeySignsJournal) printf("\t\t\tKey used to sign journal\n");
	if (rsfFlags&RSFFKeyImported) printf("\t\t\tKey was imported\n");
	if (rsfFlags&RSFFKeyPINEnter) printf("\t\t\tKey usage requires PIN2 enter\n");
	if (rsfFlags&RSFFKeyConfirmOp) printf("\t\t\tKey usage requires operation confirmation\n");
	if (!rsfFlags)
		printf("\t\t\tNone\n");
}

void printOperationInfoFlags(OperationInfoFlags oiFlags) {
	printf("\t\tOperation Info Flags: \n");
	if (oiFlags&OIDeviceHash) printf("\t\t\tHash was performed on device\n");
	if (oiFlags&OIKeyPINEnter) printf("\t\t\tKey used requires PIN2 enter\n");
	if (oiFlags&OIPIN2Entered) printf("\t\t\tPIN2 was entered\n");
	if (oiFlags&OIOpConfirmed) printf("\t\t\tOperation was confirmed\n");
	if (!oiFlags)
		printf("\t\t\tNone\n");
}

void printPinPadPolicyFlags(PinPadPolicyFlags pppFlags) {
	printf("\t\tPinPad Policy Flags: \n");
	if (pppFlags&PPPPIN2CashDisabled) printf("\t\t\tPIN2 cache disabled\n");
	if (pppFlags&PPPDefPINNotRequested) printf("\t\t\tDefault PIN2 not requested on PIN2 change\n");
	if (pppFlags&PPPChangePINOnScreen) printf("\t\t\tPIN2 can be change on screen only\n");
	if (!pppFlags)
		printf("\t\t\tNone\n");
}

void printRSFID(CK_BYTE rsfIDHigh, CK_BYTE rsfIDLow) {
	unsigned int rsfID = ((unsigned int)rsfIDHigh<<8) | rsfIDLow;
	printf("\t\tRSF Id: %04X\n", rsfID);
}

void printSignatureCount(uint32_t ulSignatureCount) {
	uint32_t ulSignatureCountLE = 0;
	unsigned int i = 0;
	for (i = 0; i < sizeof(ulSignatureCount); ++i) {
		ulSignatureCountLE = (ulSignatureCountLE << 8) | (ulSignatureCount & 0xFF);
		ulSignatureCount >>= 8;
	}
	printf("\t\tSuccessfull Signature Count: %d\n", ulSignatureCountLE);
}

void printOperationInfo(const CK_BYTE* pTLVElt) {
	const OperationInfo* pOperationInfo = (const OperationInfo*)GetTlVValue(pTLVElt);
	printf("\tOperation Info:\n");
	printOperationType(pOperationInfo->operationType);
	printRSFType(pOperationInfo->rsfType);
	printRSFFlags(pOperationInfo->rsfFlags);
	printOperationInfoFlags(pOperationInfo->oiFlags);
	printPinPadPolicyFlags(pOperationInfo->pppFlags);
	printRSFID(pOperationInfo->rsfIDHigh, pOperationInfo->rsfIDLow);
	printSignatureCount(pOperationInfo->ulSignatureCount);
}

void printTableNumber(uint16_t tableNumber) {
	printf("\t\tTable number: %d\n", be2les(tableNumber));
}

void printTableEncoding(uint16_t tableEncoding) {
	printf("\t\tTable encoding: ");
	switch(be2les(tableEncoding)) {
		case 0: printf("CP-1251"); break;
		case 1: printf("UTF-8"); break;
		default: printf("unknown");
	}
	printf("\n");
}

void printTableHash(const CK_BYTE* pTableHash, CK_ULONG ulLen) {
	printf("\t\tHash:\n");
	printHexBuffer(pTableHash, ulLen, 3);
}

void printTagTableInfo(const CK_BYTE* pTLVElt) {
	const TagTableInfo* pTagTableInfo = (const TagTableInfo*)GetTlVValue(pTLVElt);
	printf("\tTag Table Info:\n");
	printTableNumber(pTagTableInfo->tableNumber);
	printTableEncoding(pTagTableInfo->tableEncoding);
	printTableHash(pTagTableInfo->tableHash, sizeof(pTagTableInfo->tableHash));
}

void printHash(const CK_BYTE* pTLVElt) {
	printf("\tHash:\n");
	printHexBuffer(GetTlVValue(pTLVElt), GetTlVLen(pTLVElt), 2);
}

void printSignature(const CK_BYTE* pTLVElt) {
	printf("\tSignature:\n");
	printHexBuffer(GetTlVValue(pTLVElt), GetTlVLen(pTLVElt), 2);
}

void printPinPadId(const CK_BYTE* pTLVElt) {
	unsigned int i = 0;
	const CK_BYTE* id = GetTlVValue(pTLVElt);
	printf("\tPinPad Id: ");
	for (i = 0; i < GetTlVLen(pTLVElt); ++i)
		printf("%02X", id[i]);
	printf("\n");
}

void printJournal(const CK_BYTE* pBuffer, CK_ULONG ulLength)
{
	const CK_BYTE* pJournal = NULL;
	const CK_BYTE* pJournalBody = NULL;
	CK_ULONG ulJournalLength = 0;
	TLVPrinter journalPrinters[] = {
		{TLVTHash, printHash},
		{TLVTSignature, printSignature},
		{TLVTOperationInfo, printOperationInfo},
		{TLVTPinPadId, printPinPadId},
		{TLVTTagTableInfo, printTagTableInfo}
	};
	unsigned int i = 0;
	printf("PinPad Journal: ");
	pJournal = FindTlVElement(TLVTJournal, pBuffer, ulLength);
	if (!pJournal) {
		printf("not found\n");
		return;
	} else {
		printf("\n");
	}
	pJournalBody = GetTlVValue(pJournal);
	ulJournalLength = GetTlVLen(pJournal);
	for (i = 0; i < arraysize(journalPrinters); ++i) {
		const CK_BYTE* pTLVElt = FindTlVElement(journalPrinters[i].type, pJournalBody, ulJournalLength);
		if (pTLVElt)
			journalPrinters[i].print(pTLVElt);
	}
}
 
int main(void)
{
	...
	if (pJournal)
	{
		printJournal(pJournal, ulJournalSize);
		free(pJournal);
		pJournal = NULL_PTR;
	}
	else
		printf("Journal is empty\n");
	...
}

Подпись журнала

Подписанная ключевой парой журнала запись журнала может служить доказательством того, что подпись была выполнена именно этим устройством Рутокен PINPad. Удостоверяющаяся сторона проверяет полученную подпись записи журнала открытым ключом и делает вывод о подлинности устройства. Кроме того, из самой записи журнала может быть получена дополнительная информация о выполненной операции подписи (хэш и подпись сообщения, используемый для подписи ключ, действующий на момент подписи политики устройства и т.д.).

Подпись журнала осуществляется только функциями C_EX_SignInvisibleInit() и C_EX_SignInvisible(). В  C_EX_SignInvisibleInit() передается идентификатор сессии, механизма и закрытого ключа журнальной пары. В C_EX_SignInvisible() передаются предварительно прохешированные данные, возвращенные функцией C_EX_GetJournal(), для определения размера подписанных данных и вторым вызовом C_EX_SignInvisible() выполняет подпись. В случае, если переданные в C_EX_SignInvisible() данные не совпадут с хранящимися в журнале, подпись выполнена не будет.

Операция подписи записи журнала в самом журнале не фиксируется.

Управление памятью Рутокен ЭЦП Flash

Получение объема флеш-памяти

Получить весь объем внешней флеш-памяти можно с помощью функции расширения C_EX_GetDriveSize(), передав в нее идентификатор слота с подключенным токеном и указатель на буфер, в который будет возвращен полученный объем памяти в Мб.

Получение объема флэш-памяти Рутокен ЭЦП Flash
CK_ULONG ulDriveSize = 0;                               // Общий объем флеш-памяти
 
printf("Get Flash memory size");
rv = pFunctionListEx->C_EX_GetDriveSize(aSlots[0],      // Идентификатор слота с подключенным токеном
										&ulDriveSize);  // Возвращаемый размер флеш-памяти в Мб
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
{
	printf(" -> OK\n");
	printf("Memory size: %d Mb\n", (int)ulDriveSize);
}

Создание разделов флеш-памяти

Флеш-память Рутокен ЭЦП Flash может быть разбита на несколько независимых разделов с разными правами доступа к ним. Минимальное количество разделов – 1, максимальное – 8.

Рутокен ЭЦП Flash поддерживает следующие права доступа к разделам: для чтения и записи (ACCESS_MODE_RW), только для чтения (ACCESS_MODE_RO), скрытый раздел, защищенный от отображения в операционной системе, чтения, записи и любого другого типа доступа (ACCESS_MODE_HIDDEN) и раздел, эмулирующий CD-ROM (ACCESS_MODE_CD).

Владельцем раздела может выступать Администратор Рутокен (CKU_SO), Пользователь Рутокен (CKU_USER), а также локальный пользователь (с идентификатором в пределах от 0x03 до 0x1E) с предварительно заданным функцией  C_EX_SetLocalPin() PIN-кодом.

Для разметки флеш-памяти на разделы предназначена функция C_EX_FormatDrive(). Вся информация о разделах (объем памяти, права доступа, владелец и флаги) задается в массиве структур типа CK_VOLUME_FORMAT_INFO_EXTENDED, который затем вместе в PIN-кодом Администратора и идентификатором слота, к которому подключен Рутокен, передается в функцию C_EX_FormatDrive().

Создание разделов флеш-памяти Рутокен ЭЦП Flash
CK_ULONG				VolumeRWSize = 0;              // Размер раздела для чтения и записи
CK_ULONG				VolumeROSize = 0;              // Размер раздела только для чтения
CK_ULONG				VolumeCDSize = 0;              // Размер раздела CD-ROM
CK_ULONG				VolumeHISize = 0;              // Размер раздела скрытого раздела

CK_ULONG			    CKU_LOCAL_1 = 0x03;			   // Идентификатор локального пользователя
CK_ULONG			    CKU_LOCAL_2 = 0x1E;            // Идентификатор локального пользователя
	
/* Шаблон для разметки разделов */
CK_VOLUME_FORMAT_INFO_EXTENDED InitParams[] =
{
	{ VolumeRWSize, ACCESS_MODE_RW, CKU_USER, 0 },
	{ VolumeROSize, ACCESS_MODE_RO, CKU_SO, 0 },
	{ VolumeHISize, ACCESS_MODE_HIDDEN, CKU_LOCAL_1, 0 },
	{ VolumeCDSize, ACCESS_MODE_CD, CKU_LOCAL_2, 0 }
};
 
...

InitParams[0].ulVolumeSize = ulDriveSize / 2;
InitParams[1].ulVolumeSize = ulDriveSize / 4;
InitParams[2].ulVolumeSize = ulDriveSize / 8;
InitParams[3].ulVolumeSize = ulDriveSize - (ulDriveSize / 2) - (ulDriveSize / 4) - (ulDriveSize / 8);
 
printf("\nFormatting flash memory");
rv = pFunctionListEx->C_EX_FormatDrive( aSlots[0],				// Идентификатор слота с подключенным токеном
										CKU_SO,					// Форматирование выполняется только с правами Администратора
										SO_PIN,					// Текущий PIN-код Администратора
										sizeof(SO_PIN),			// Длина PIN-кода Администратора
										InitParams,				// Массив с информацией о разделах
										arraysize(InitParams)); // Размер массива
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");

Получение информации о разделах флеш-памяти

Получить информацию о существующих на флеш-памяти разделах можно с помощью функции C_EX_GetVolumesInfo(), передав в нее идентификатор слота, к которому подключен токен, указатель на буфер и его размер, куда будет возвращен массив структур типа CK_VOLUME_INFO_EXTENDED с информацией о разделах (идентификатор раздела, его размер, права доступа, владелец и флаги). Размер буфера можно определить, вызвав C_EX_GetVolumesInfo() с пустым указателем на буфер.

Получение информации о разделах флеш-памяти Рутокен ЭЦП Flash
CK_VOLUME_INFO_EXTENDED_PTR   pVolumesInfo = NULL_PTR;  // Указатель на массив структур с информацией о разделах
CK_ULONG                      ulVolumesInfoCount = 0;    // Размер массива с информацией о разделах
 
printf("\nGetting volumes info");
rv = pFunctionListEx->C_EX_GetVolumesInfo( aSlots[0],			// Идентификатор слота с подключенным токеном
										   NULL_PTR,			// Указатель на массив с возвращаемой информацией о разделах
										   &ulVolumesInfoCount);// Размер массива 

pVolumesInfo = (CK_VOLUME_INFO_EXTENDED*)malloc(ulVolumesInfoCount * sizeof(CK_VOLUME_INFO_EXTENDED));
memset(pVolumesInfo,
		0,
		(ulVolumesInfoCount * sizeof(CK_ULONG)));

rv = pFunctionListEx->C_EX_GetVolumesInfo(aSlots[0],			// Идентификатор слота с подключенным токеном
										  pVolumesInfo,			// Указатель на массив с возвращаемой информацией о разделах
										  &ulVolumesInfoCount); // Размер массива
if (rv != CKR_OK)
{
	printf(" -> Failed\n");
}
else
{
	printf(" -> OK\n");
	for (i = 0; i < (int)ulVolumesInfoCount; i++)
	{   
		printf("\nPrinting volume %1.2d info:\n", (int)i+1);
		printf(" Volume id:         %2.2d \n", pVolumesInfo[i].idVolume);    	// Идентификатор раздела
	    printf(" Volume size:       %d Mb \n", pVolumesInfo[i].ulVolumeSize);	// Объем раздела
		printf(" Access mode:       %2.2d \n", pVolumesInfo[i].accessMode);		// Права доступа к разделу
		printf(" Volume owner:      %2.2d \n", pVolumesInfo[i].volumeOwner);	// Владелец раздела
		printf(" Flags:             0x%8.8X \n", pVolumesInfo[i].flags);		// Флаги раздела
	}
}

Изменение атрибутов разделов флеш-памяти

Работа с защищенными разделами (с правами доступа только для чтения, скрытым и CD-ROM разделам) сводится к тому, что владелец раздела на время работы с разделом меняет к нему права доступа. Доступно два способа изменения атрибутов раздела: временное и постоянное. Временное изменение меняет права доступа до первого отключения устройства из USB-порта, после чего права доступа сбрасываются на прежние. Постоянное изменение атрибутов доступа действует вплоть до следующего изменения атрибутов.

Для обоих способов изменения атрибутов используется одна функция C_EX_ChangeVolumeAttributes(), в которую передается идентификатор слота с подключенным токеном, владелец раздела и его PIN-код, а также новые права доступа к разделу и флаг временности/постоянности изменения.

Изменение атрибутов разделов флеш-памяти Рутокен ЭЦП Flash
printf("\nChanging volume attributes");
rv = pFunctionListEx->C_EX_ChangeVolumeAttributes(aSlots[0],		// Идентификатор слота с подключенным токеном
													CKU_SO,			// Владелец раздела
													SO_PIN,			// PIN-код владельца раздела
													sizeof(SO_PIN),	// Длина PIN-кода владельца раздела
													VolumeRO,		// Идентификатор раздела
													ACCESS_MODE_RW,	// Новые права доступа к разделу
													CK_TRUE);       // CK_TRUE - постоянное изменение атрибутов, CK_FALSE - временное изменение атрибутов
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");


  • Нет меток