Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents
maxLevel3
outlinetrue
stylenone

Введение

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

Рутокен S

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

...

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

Рутокен Lite

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

Рутокен ЭЦП

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

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

Рутокен PINPad

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

...

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

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

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

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

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

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

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

...

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

...

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

...

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

...

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

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

...

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

...

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ввести PIN-код на тачскрине устройства

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

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

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

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

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

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

...

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

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

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

...

Code Block
languagecpp
titleГенерация ключевой пары ГОСТ Р 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");

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

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

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

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

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

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

  • CKK_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_GOST28147_PARAMS, GOST28147params, sizeof(GOST28147params)} // Параметры алгоритма 
};

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

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

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

...

Code Block
languagecpp
titleГенерация симметричного ключа ГОСТ 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");

// 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);	// Механизм хеширования
	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.

...

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

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

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

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

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,			// Длина подписываемых данных
								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().

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

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

Code Block
languagecpp
titleПроверка подписи по алгоритму ГОСТ Р 34.10-2001Подпись запроса на сертификат в формате PKCS#10
/* Сформированный запрос на сертификат в виде двоичной строки */
CK_BYTE pbtReqCert[] = {0x30, 0x82, 0x02, 0x69, 0x02, 0x01, 0x00, 0x30, 0x82, 0x01, 0x83, 0x31, 0x0b, 0x30, 0x09, 0x06,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,		// Буфер с подписью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,
								 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.

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

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

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

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

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

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
};
 
/*  Механизм хеширования ГОСТ Р 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,					// Хэндл сессии
								  pbtReqCert,				// Буфер с запросом на сертификат
								  arraysize(pbtReqCert),	// Размер буфера с запросом на сертификат
								  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,					// Хэндл сессии
								 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);
	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_Verify() передается исходный текст, при использовании отдельного механизма подписи (например, CKM_GOSTR3410) – предварительно прохешированные функцией C_Digest() исходные данные.

Code Block
languagecpp
titleПроверка подписи по алгоритму ГОСТ Р 34.10-2001
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.

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

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

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

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

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

Code Block
languagecpp
titleШифрование данных по алгоритму ГОСТ 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,		// Хэндл сессии
			
Code Block
languagecpp
titleШифрование данных по алгоритму ГОСТ 28147-89 в режиме простой замены
/* Данные для шифрования */
CK_BYTE pbtData[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	                  0x01,  0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08&EncDecMech,
	// Механизм шифрования
			                0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
	   hSecKey);		// Хэндл секретного ключа
	if (rv != CKR_OK)
	{
		printf("         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;-> Failed\n");
		break;
	}
	printf(" -> OK\n");

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

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


/* Механизм шифрования/расшифрования по алгоритму ГОСТ 28147-89 в режиме гаммирования с зашифрованными данными
	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;
}
 
Code Block
languagecpp
titleШифрование данных по алгоритму ГОСТ 28147-89 в режиме гаммирования с обратной связью
/* Данные для шифрования */
CK_BYTE 	pbtData[] 	= { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	                 	 	0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
	обратной связью */
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,				// Хэндл сессии
			                     		0x02, 0x03, 0x04, 0x05, 0x06, 0x07&EncDecStreamMech,	// 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;hSecKey);				// Хэндл секретного ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Зашифровать открытый текст */
	ulEncryptedDataSize = arraysize(pbtData);
	ulRestLen = arraysize(pbtData);
	pbtEncryptedData = (CK_BYTE*)malloc(ulEncryptedDataSize);
	
	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);							// Размер блока данных, в байтах
CK_ULONG 		ulCurrentPosition 	= 0;				// Текущее начало блока
CK_ULONG 		ulRestLen			= 0;				// Размер оставшегося буфера, в байтах
 
while(TRUE)
{
	...
	/* Инициализировать операцию шифрования */
	printf("C_EncryptInit		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_EncryptInitEncryptFinal( hSession, 					// Хэндл сессии
										NULL_PTR,					// Буфер с последним блоком                      &EncDecStreamMech,данных 
										&ulEncryptedDataSize);		// МеханизмДлина шифрованиябуфера
			if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	/* Распечатать буфер, содержащий зашифрованные данные*/
	printf("Encrypted buffer is:\n");
	for (i = 0;
		 i < ulEncryptedDataSize;
	      hSecKey);				// Хэндл секретного ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OKi++)
	{
		printf("%02X ", pbtEncryptedData[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");

	/* Зашифровать открытый текст */
	ulEncryptedDataSize = arraysize(pbtData);
	ulRestLen = arraysize(pbtData);
	pbtEncryptedData = (CK_BYTE*)malloc(ulEncryptedDataSize);
	
	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);
	}

	break;
}

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

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

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

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

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

Code Block
languagecpp
titleРасшифрование данных по алгоритму ГОСТ 28147-89
while(TRUE)
{
	...
	/* Инициализировать операцию расшифрования */
	printf("C_DecryptInit");
			rv = pFunctionList->C_EncryptUpdateDecryptInit(hSession,								// Хэндл сессии
											pbtData + ulCurrentPosition,			// Буфер с блоком данных для шифрования
											ulBlockSize,							// Размер блока, в байтах
											pbtEncryptedData + ulCurrentPosition,	// Буфер с блоком зашифрованных данных
											&ulBlockSize                          &EncDecStreamMech,	// Механизм расшифрования
			                          hSecKey);							// РазмерХэндл блока, в байтах
	секретного ключа
	if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			break;
		}
		printf(" -> OK\n");

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

/* Расшифровать шифротекст */
	printf("Finalizing encryptionGetting decrypted data size");
	rv = pFunctionList->C_EncryptFinalDecrypt( hSession, 					// Хэндл сессии
			                      pbtEncryptedData,							NULL_PTR,			// Буфер с зашифрованными данными
			                      ulEncryptedDataSize,		// Буфер с последним блокомРазмер зашифрованных данных 
				                      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 

Code Block
languagecpp
titleРасшифрование данных по алгоритму ГОСТ 28147-89
while(TRUE)
{
	...
	/* Инициализировать операцию расшифрования */Буфер с расшифрованными данными
			                      &ulDecryptedDataSize);	// Размер расшифрованных данных
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	pbtDecryptedData = (CK_BYTE*)malloc(ulDecryptedDataSize);
	memset(pbtDecryptedData,
	       0,
	       (ulDecryptedDataSize * sizeof(CK_BYTE)));
	
	printf("C_DecryptInitDecrypt");
	rv = pFunctionList->C_DecryptInitDecrypt(hSession,					// Хэндл сессии
			                          &EncDecStreamMechpbtEncryptedData,			// Буфер с Механизмзашифрованными расшифрованияданными
			                          hSecKey);ulEncryptedDataSize,				// ХэндлРазмер секретного ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	
	/* Расшифровать шифротекст */
	printf("Getting decrypted data size");
	rv = pFunctionList->C_Decrypt(hSession,					// Хэндл сессии
			зашифрованных данных
	                              pbtEncryptedDatapbtDecryptedData,			// Буфер с зашифрованнымирасшифрованными данными
			                      ulEncryptedDataSize,		// Размер зашифрованных данных
			     &ulDecryptedDataSize);	// Размер расшифрованных данных
	if (rv != CKR_OK)
	{
		printf("           NULL_PTR,					// Буфер с расшифрованными данными
			                      &ulDecryptedDataSize);	// Размер расшифрованных данных
	if (rv != CKR_OK)
	{
		printf(" -> Failed-> 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)
{
	printf(" -> OK\n"free(pbtEncryptedData);
	pbtDecryptedDatapbtEncryptedData = (CK_BYTE*)malloc(ulDecryptedDataSize);
	memset(pbtDecryptedData,
	       0,
	       (ulDecryptedDataSize * sizeof(CK_BYTE)));
	
	printf("C_Decrypt"NULL_PTR;
}
if (pbtDecryptedData)
{
	free(pbtDecryptedData);
	rvpbtDecryptedData = pFunctionList->C_Decrypt(hSession,	NULL_PTR;
}

 

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

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

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

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

Code Block
languagecpp
titleРасшифрование данных по алгоритму ГОСТ 28147-89
CK_SLOT_ID  	slotID;				// Хэндл сессии
			     Идентификатор слота, к которому подключен токен
CK_UTF8CHAR_PTR pUserPin;             		// PIN-код Пользователя Рутокен
CK_ULONG    pbtEncryptedData,	ulUserPinLen;		// Буфер с зашифрованными данными
			 Длина  PIN-кода Пользователя Рутокен
CK_UTF8CHAR_PTR pNewLocalPin;		// Локальный PIN-код
CK_ULONG    	ulNewLocalPinLen; 	// Длина локального PIN-кода
CK_ULONG    	ulLocalID;         ulEncryptedDataSize,		// РазмерИдентификатор зашифрованных данных
	           локального PIN-кода
 
printf("Setting Local PIN-code");
rv = pfGetFunctionListEx->C_EX_SetLocalPIN( slotID,				// Идентификатор слота, к которому подключен токен
			                   pbtDecryptedData				pUserPin,			// Буфер с расшифрованными данными
	Текущий PIN-код Пользователя Рутокен
			                  				ulUserPinLen,		// Длина текущего PIN-кода Пользователя Рутокен
			            &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("	pNewLocalPin,		// Новый локальный PIN-код
			                  				&ulNewLocalPinLen, 	// Длина нового локального PIN-кода
											ulLocalID);	    	// Идентификатор локального PIN-кода
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
	}
	break;
	}
 	
if (pbtEncryptedData)
{
	free(pbtEncryptedData);
	pbtEncryptedData = NULL_PTR;
}
if (pbtDecryptedData)
{
	free(pbtDecryptedData);
	pbtDecryptedData = NULL_PTR;
}printf(" -> OK\n");