Page tree

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 38 Next »


Введение

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

Рутокен 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(). В нее передается предварительно созданный шаблон с атрибутами создаваемого объекта, размер шаблона и хэндл открытой сессии с правами Пользователя. Функция возвращает хэндл созданного с указанными атрибутами объекта.

Создание объекта типа 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), размер шаблона и хэндл открытой сессии с правами Пользователя. Функция возвращает хэндл созданного с указанными атрибутами объекта.

Ввиду запрета регуляторов импорт ключей ГОСТ 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

Unable to render {include} The included page could not be found.

Управление памятью Рутокен ЭЦП 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");


  • No labels