Введение

Предназначение устройств Рутокен

Рутокен S

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

Рутокен S реализует следующие возможности стандарта PKCS#11:

Рутокен Lite

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

Рутокен ЭЦП

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

Рутокен PINPad

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

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

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

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

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

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

Для работы с функциями расширения необходимо получить адрес функции CK_C_EX_GetFunctionListExtended() и вызвать ее для получения списка функций расширения 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));
	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 и номер соответствующего слота.

 

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

Получить информацию о подключенном к слоту токене можно с помощью функции расширения 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; } /* Определить класс токена */ 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; }

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

// Про колбэк

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 -- администратор Рутокен.

/* 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_CONFIRM_OP, то данные, которые подписываются с помощью такого ключа, перед подписью будут показаны на экране устройства и для их подписи потребуется подтверждение пользователя в виде нажатия специальной кнопки на тачскрине устройства. 

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

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

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

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

Ниже представлены примеры шаблонов закрытого и открытого ключа ГОСТ Р 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_DECRYPT, &bTrue, sizeof(bTrue)}, 							// Ключ предназначен для расшифрования 
	{ 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)} // Параметры алгоритма 
};
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_ENCRYPT, &bTrue, sizeof(bTrue)}, 							// Ключ предназначен для зашифрования 
	{ 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)} // Параметры алгоритма 
};

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

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

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

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

/* Вычисление размера массива */
#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");

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

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

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

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

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

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

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

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

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

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

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)} // Параметры алгоритма };

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

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

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

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

/* Вычисление размера массива */ #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");


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

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

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

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

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

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

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

/* Данные для хеширования в виде двоичной строки */ 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; }  

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

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

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

Формат данных для подписи, отображаемый Рутокен PINPad

Если у закрытого ключа, которым подписываются данные, выставлен флаг визуализации, перед подписью Рутокен PINPad выведет на экран подписываемые данные для проверки и будет ожидать подтверждения или отмены операции. Чтобы Рутокен 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 немедленно возвращает управление и код ошибки. Вычисления цифровой подписи не производится.

 

Пример подписи данных по алгоритму ГОСТ Р 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, // Длина подписываемых данных 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 существует функция C_EX_PKCS7Sign(), в которую сразу передаются все необходимые для операции данные. Для такой подписи необходимо наличие сертификата, которым будут подписываться данные.

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

CK_OBJECT_HANDLE hCert; // Хэндл сертификата   ...   /* Подпись данных */ printf("PKCS7 Sign"); rv = pFunctionListEx->C_EX_PKCS7Sign( hSession, // Хэндл сессии pbtData, // Буфер с данными для подписи arraysize(pbtData), // Размер данных для подписи hCert, // Хэндл сертификата &pbtSignature, // Буфер для подписанных данных &ulSignatureSize, // Размер подписанных данных hPrvKey, // Хэндл закрытого ключа, соответствующего сертификату NULL,  // Указатель на массив сертификатов 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) – уже прохешированные данные.

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

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

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

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

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

Работа с токеном

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

Смена пин-кода

Работа с сертификатами

Создание подписанного запроса на сертификат в формате PKCS#10

Получение информации о сертификате

Импорт сертификата на токен