Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Восстановить из v. 10

...

Для работы с устройствами Рутокен по стандарту PKCS#11 приложение предварительно должно динамически загрузить библиотеку, реализующую интерфейс стандарта. Рутокен SDK предоставляет две библиотеки rtPKCS11.dll и rtPKCS11ECP.dll, подробнее об особенностях выбора и использования которых можно ознакомиться в разделе Использование библиотек rtPKCS11 и rtPKCS11ECP. Основная разница заключается в том, что российские алгоритмы доступны в библиотеке rtPKCS11ECP.dll, а зарубежные – в- в rtPKCS11. Библиотеки доступны для платформ Windows, Linux и MacOS Xdll.

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

...

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

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

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

Code Block
languagecpp
titleИнициализация библиотеки
...
 
/* Инициализировать библиотеку */
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_GetSlotInfo(), в которую передается идентификатор слота. Ее вызов запускает обновление информации обо всех слотах. Если токен извлечь из разъема и затем снова вставить в тот же самый разъем, то он может подключиться к любому свободному слоту, а не обязательно к тому же самому.

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

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

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

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

Code Block
languagecpp
titleМониторинг событий в слотах
 

...

Code Block
languagecpp
titleОпределение класса токена
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;			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) { 
				                                                          "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; 
	} 
	/* Определить класс токена */ 
	if (tokenInfoEx.ulTokenClass == TOKEN_CLASS_S) { 
	{
		bIsRutokenPINPad = FALSE; 
		printf(": Rutoken / Rutoken S\n"); 
	} 
	else if (tokenInfoEx.ulTokenClass == TOKEN_CLASS_ECP) { 
	{
		bIsRutokenPINPad = FALSE; 
		printf(": Rutoken ECP\n"); 
	} 
	else if (tokenInfoEx.ulTokenClass == TOKEN_CLASS_LITE) { 
	{
		bIsRutokenPINPad = FALSE; 
		printf(": Rutoken Lite\n"); 
	} 
	else if (tokenInfoEx.ulTokenClass == TOKEN_CLASS_WEB) { 
	{
		bIsRutokenPINPad = FALSE; 
		printf(": Rutoken WEB\n"); 
	} 
	else if (tokenInfoEx.ulTokenClass == TOKEN_TYPE_RUTOKEN_PINPAD_FAMILY) { 
	{
		bIsRutokenPINPad = TRUE; 
		printf(": Rutoken PINPad\n"); 
	} 
	else { 
	{
		bIsRutokenPINPad = FALSE; 
		printf(": undefined\n"); } 
	}

	break; 
}

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

// Про колбэк

Code Block
languagecpp
titleМониторинг событий в слотах
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 -- администратор Рутокен.

Code Block
languagecpp
titleМониторинг событий в слотах
/* 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.

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

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

...

Если ключ был создан с флагом повышенной защиты CKA_VENDOR_KEY_PIN_ENTER, то для подписи таким ключом таким ключом перед операцией потребуется ввести PIN-код на тачскрине устройства

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

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

  • CKK_GOSTR3410 для ключей ГОСТ  для ключей ГОСТ Р 34.10-2001,

  • CKK_GOSTR3410_512 для ключей ГОСТ Р 34.10-2012,
  • CKK_RSA для ключей RSA.

Примеры шаблонов ключей

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

...

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

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

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

...

Если ключ был создан с флагом повышенной защиты CKA_VENDOR_KEY_PIN_ENTER, то для шифрования таким ключом перед операцией потребуется ввести PIN-код на тачскрине устройства. 

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

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

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

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

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

Code Block
languagecpp
titleШаблон секретного ключа ГОСТ 28147-89
CK_OBJECT_CLASS ocPubKey 		= 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_VENDOR_KEY_CONFIRM_OP, &bTrue, sizeof(bTrue) }, 	// Операция шифрования/расшифрования требует подтверждения на PINPad (только для Рутокен PINPad) 
	{ CKA_VENDOR_KEY_PIN_ENTER, &bTrue, sizeof(bTrue) }, 	// Операция шифрования/расшифрования требует ввода PIN-кода на PINPad (только для Рутокен PINPad) 
	{ CKA_GOST28147_PARAMS, GOST28147params, sizeof(GOST28147params)} // Параметры алгоритма 
};

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

...

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

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

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

Code Block
languagecpp
titleГенерация симметричного ключа ГОСТ 28147-89
/* Вычисление размера массива */
#define #define		 arraysize(a)   (sizeof(a)/sizeof(a[0])) 
  
CK_MECHANISM  KeyGenMech = {  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");

// TO DO Импорт ключа

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

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

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

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

  • 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() второй раз.

 открытой сессии
			                      &KeyGenMech,              			// Используемый механизм генерации ключа
			                      attrGOST28147_89SecKey,               // Шаблон для создания секретного ключа
			                      arraysize(attrGOST28147_89SecKey),    // Размер шаблона секретного ключа
			                      &hSecKey);                   			// Хэндл секретного ключа
if (rv != CKR_OK)
	printf(" -> Failed\n");
else
	printf(" -> OK\n");


// TO DO Импорт ключа

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

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

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

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

  • 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() второй раз.

Code Block
languagecpp
titleХеширование данных по алгоритму ГОСТ Р 34.11-94
/* Данные для хеширования в виде двоичной строки */
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 HashMech = {CKM_GOSTR3411, NULL_PTR, 0}; 

 
CK_BYTE_PTR pbtHash = NULL_PTR;            // Указатель на буфер для хешированных данных
CK_ULONG ulHashSize = 0;                   // Размер буфера в байтах
 
while(TRUE)
{
	...
 
	/* Инициализировать операцию хеширования  */
	printf("C_DigestInit");
	rv = pFunctionList->C_DigestInit(hSession,
								 	 &HashMech);
	
Code Block
languagecpp
titleХеширование данных по алгоритму ГОСТ Р 34.11-94
/* Данные для хеширования в виде двоичной строки */ 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 HashMech = {CKM_GOSTR3411, NULL_PTR, 0};   CK_BYTE_PTR pbtHash = NULL_PTR; // Указатель на буфер для хешированных данных CK_ULONG ulHashSize = 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); 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,
  • 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

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

Code Block
languagecpp
titleФормат данных Рутокен PINPad
<!PINPADFILE RU> // обязательный признак строки, которая будет распознаваться Rutoken PINPad <!>some text // текст, нераспознаваемый Rutoken PINPad <N>some text // наименование поля <V>some text // значение поля

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 

...




	/* Определить размер хешированных данных */
	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);
	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,
  • 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

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

Code Block
languagecpp
titleФормат данных Рутокен PINPad
<!PINPADFILE RU> 	// обязательный признак строки, которая будет распознаваться Rutoken PINPad
<!>some text 		// текст, нераспознаваемый Rutoken PINPad
<N>some text 		// наименование поля
<V>some text 		// значение поля

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

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

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

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

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

Code Block
languagecpp
titleПодпись данных по алгоритму ГОСТ Р 34.10-2001
/* Механизм подписи/проверки подписи по алгоритму ГОСТ Р 34.10-2001 */ 
CK_MECHANISM    SigVerMech = {CKM_GOSTR3410, NULL_PTR, 0}; 
  
/* Набор параметров КриптоПро алгоритма ГОСТ Р 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, 		// Хэндл сессии 
									&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); 
	memset( pbtSignature, 
			0, 
			ulSignatureSize * sizeof(CK_BYTE)); 

	/* Подписать исходные данные */ 
	printf("C_Sign step 2"); 
	rv = pFunctionList->C_Sign( hSession, 			// Хэндл сессии 
								pbHash, 				// Буфер с данными для подписи ulHashSize, подписи
								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"); 
	}

Подпись данных в формате CMS (PKCS#7)

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

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


 

Code Block
languagecpp
titleПодпись данных в формате CMS (PKCS#7)
CK_OBJECT_HANDLE hCert;			 // Хэндл сертификата 
  
... 
  
/* Подпись данных */ 
printf("PKCS7 Sign"); 
rv = pFunctionListEx->C_EX_PKCS7Sign( hSession, 				// Хэндл сессии
									  pbtData, 					// Буфер с данными для подписи
									  arraysize(pbtData), 		// Размер данных для подписи
									  hCert, 					// Хэндл сертификата
									  &pbtSignature, 			// Буфер для подписанных данных 
									  &ulSignatureSize, 		// Размер подписанных данных  
									  hPrvKey,  					// Хэндл закрытого ключа, соответствующего сертификату 
									  NULL,  					// Указатель на массив сертификатов
									  0,  						// Размер массива
									  0); 						// Формат подписи: 0 0 - неотделяемая подпись (подпись вместе с исходными данными); 
 																// PKCS7_DETACHED_SIGNATURE - отделяемая подпись (подпись без исходных данных) 
if (rv != CKR_OK) 
	printf(" -> Failed\n"); 
else 
	printf(" -> OK\n"); 

... 
  
/* Освобождение памяти*/ 
printf("C_EX_FreeBuffer"); 
rv = pFunctionListEx->C_EX_FreeBuffer(pbtSignature); 
if (rv != CKR_OK) 
	printf(" -> Failed\n"); 
else 
	printf(" -> OK\n");


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

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

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

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

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

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

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

Режим гаммирования

Режим гаммирования с обратной связью

...