Page tree

Versions Compared

Key

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

...

  • создание, запись, чтение, изменение, удаление двоичных файлов,
  • генерация и импорт ключей шифрования ГОСТ 28147-89, 
  • шифрование данных по алгоритму ГОСТ 28147-89 в режимах простой замены, гаммирования и гаммирования с обратной связью, 
  • вычисление имитовставки по алгоритму ГОСТ 28147-89 длиной 32 бит, 
  • генерация последовательности случайных чисел длиной 256 бит.
Info
titleОграничения Rutoken S
  • Рутокен S для работы требует установки драйверов: для Windows, для Linux, для macOS.
  • Рутокен S не поддерживается библиотекой rtpkcs11ecp.
  • Рутокен S в ОС Windows поддерживается устаревшей библиотекой rtPKCS11.dll и очень ограниченно поддерживается библиотекой opensc-pkcs11 из состава OpenSC.

Рутокен Lite

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

...

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

Рутокен ЭЦП 2.0

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

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

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

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

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

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

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

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

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

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

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

Рутокен ЭЦП 3.0

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

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

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

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

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

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

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

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

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

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

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

Code Block
languagecpp
titleЗагрузка библиотеки и списка функций PKCS#11
/* Имя библиотеки PKCS#11 */
#ifdef _WIN32
/* Библиотека для Рутокен S, Рутокен Lite и Рутокен ЭЦП, поддерживает только алгоритмы RSA */
	#define PKCS11_LIBRARY_NAME         "rtPKCS11.dll" 
/* Библиотека для Рутокен Lite, Рутокен ЭЦП поддерживает алгоритмы ГОСТ и RSA */
	#define PKCS11ECP_LIBRARY_NAME      "rtPKCS11ECP.dll"
#endif 
#ifdef __unix__
/* Библиотека для Рутокен Lite, Рутокен ЭЦП поддерживает алгоритмы ГОСТ и RSA */
	#define PKCS11_LIBRARY_NAME         "librtpkcs11ecp.so"
	#define PKCS11ECP_LIBRARY_NAME      "librtpkcs11ecp.so"
#endif 	
#ifdef __APPLE__
/* Библиотека для Рутокен Lite, Рутокен ЭЦП поддерживает алгоритмы ГОСТ и 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;
}

...

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

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

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

CK_ATTRIBUTE GOST34_10_2012_256_PrivateKey[] = { 					// Шаблон для ключа длиной 256 бит
	{ CKA_CLASS, &ocPrivKey, sizeof(ocPrivKey)}, 					// Объект закрытого ключа 
	{ CKA_LABEL, &PrivKeyLabel, sizeof(PrivKeyLabel) - 1}, 			// Метка ключа 
	{ CKA_ID, &KeyPairID, sizeof(KeyPairID) - 1}, 					// Идентификатор ключевой пары #1 (должен совпадать у открытого и закрытого ключей) 
	{ CKA_KEY_TYPE, &keyTypeGostR3410, sizeof(keyTypeGostR3410)}, 	// Тип ключа 
	{ CKA_TOKEN, &bTrue, sizeof(bTrue)},							// Ключ является объектом токена 
	{ CKA_PRIVATE, &bTrue, sizeof(bTrue)}, 							// Ключ доступен только после авторизации на токене 
	{ CKA_DERIVE, &bTrue, sizeof(bTrue)}, 							// Ключ поддерживает деривацию (из него могут быть получены другие ключи) 
	{ CKA_GOSTR3410_PARAMS, parametersGostR3410_2012_256, sizeof(parametersGostR3410_2012_256)}, // Параметры алгоритма 
};
 
CK_ATTRIBUTE GOST34_10_2012_512_	{ CKA_GOSTR3411_PARAMS, parametersGostR3411_2012_256, sizeof(parametersGostR3411_2012_256)} // Параметры алгоритма ГОСТ Р 34.11-2012(256)
};
 
CK_ATTRIBUTE GOST34_10_2012_512_PrivateKey[] = { 					// Шаблон для ключа длиной 512 бит
	{ CKA_CLASS, &ocPrivKey, sizeof(ocPrivKey)}, 					// Объект закрытого ключа 
	{ CKA_LABEL, &PrivKeyLabel, sizeof(PrivKeyLabel) - 1}, 			// Метка ключа 
	{ CKA_ID, &KeyPairID, sizeof(KeyPairID) - 1}, 					// Идентификатор ключевой пары #1 (должен совпадать у открытого и закрытого ключей) 
	{ CKA_KEY_TYPE, &keyTypeGostR3410_512, sizeof(keyTypeGostR3410)_512},   // Тип ключа 
	{ CKA_TOKEN, &bTrue, sizeof(bTrue)},							// Ключ является объектом токена 
	{ CKA_PRIVATE, &bTrue, sizeof(bTrue)}, 							// Ключ доступен только после авторизации на токене 
	{ CKA_DERIVE, &bTrue, sizeof(bTrue)}, 							// Ключ поддерживает деривацию (из него могут быть получены другие ключи) 
	{ CKA_GOSTR3410_PARAMS, parametersGostR3410_2012_512, sizeof(parametersGostR3410_2012_512)}, // Параметры алгоритма
	{ CKA_GOSTR3411_PARAMS, paramsGostR3410parametersGostR3411_2012_512, sizeof(paramGostR3410parametersGostR3411_2012_512)}  // Параметры алгоритма  алгоритма ГОСТ Р 34.11-2012(512)
};


Code Block
languagecpp
titleШаблон открытого ключа ГОСТ Р 34.10-2012
CK_OBJECT_CLASS ocPubKey 				= CKO_PUBLIC_KEY; 
CK_UTF8CHAR 	PubKeyLabel[] 			= {"GOST Public Key"}; 
CK_BYTE 		KeyPairID[] 			= {"GOST keypair"}; 
CCK_KEY_TYPE 	keyTypeGostR3410		= CKK_GOSTR3410;		// Для закрытого ключа длиной 256 бит
CK_KEY_TYPE 	keyTypeGostR3410_512 	= CKK_GOSTR3410_512;	// Для закрытого ключа длиной 512 бит
CK_BBOOL 		bTrue 					= CK_TRUE; 
CK_BBOOL 		bFalse 					= CK_FALSE; 

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

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

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

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

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

...

Code Block
languagecpp
titleГенерация ключевой пары ГОСТ Р 34.10-2012
/* Вычисление размера массива */
#define 		 arraysize(a)   (sizeof(a)/sizeof(a[0]))
 
CK_MECHANISM     gostR3410KeyPairGenMechgostR3410_256KeyPairGenMech 		= {CKM_GOSTR3410_KEY_PAIR_GEN, NULL_PTR, 0}; 		// Механизм генерации ключевой пары ГОСТ Р 34.10-20012012(256)
CK_MECHANISM 	 gostR3410_512KeyPairGenMech	= {CKM_GOSTR3410_512_KEY_PAIR_GEN, NULL_PTR, 0};  	// Механизм генерации ключевой пары ГОСТ Р 34.10-2012(512) */

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

...

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

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

...

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

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

...

2012

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

/* Механизм выработки общего ключа */
CK_MECHANISM    ckmDerivationMech2001         = {CKM_GOSTR3410_DERIVE, NULL_PTR, 0}; 
CK_MECHANISM    ckmDerivationMech2012         = {#define DERIVE_PARAMS_256_LENGTH 84

/*************************************************************************
* Параметры для выработки ключа обмена по схеме VKO GOST R 34.10-2012-256*
* Содержат в себе данные по диверсификации ключа, открытый ключ и UKM    *
*************************************************************************/
CK_BYTE deriveParameters2012_256[DERIVE_PARAMS_256_LENGTH] = { 0x00, };

const CK_ULONG keyLengthOffset = 4;       // Смещение длины ключа в массиве
const CK_ULONG publicKeyValueOffset = 8;  // Смещение значения ключа в массиве
const CK_ULONG ukmLengthOffset = 72;      // Смещение длины UKM в массиве
const CK_ULONG ukmDataOffset = 76;        // Смещение UKM в массиве


/*************************************************************************
* Функция записи четырёхбайтного значения длины в буфер                  *
*************************************************************************/
void ulongToBuffer(CK_BYTE_PTR buffer, CK_ULONG value)
{
	buffer[0] = value & 0xFF;
	buffer[1] = (value >> 8) & 0xFF;
	buffer[2] = (value >> 16) & 0xFF;
	buffer[3] = (value >> 24) & 0xFF;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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



/*Установить параметры в структуре типа CK_MECHANISM для маскирования ключа*/
ckmWrapMech.ulParameterLen = 8;
GenerateRandomData(ckmWrapMech.ulParameterLen, (PBYTE *)&ckmWrapMech.pParameter);
		
/* Заполнить шаблон сессионного ключа случайными данными */
GenerateRandomData(GOST_28147_KEY_SIZE, &pbtSessionKey);
for (i = 0;
	 i <  // Размер буфера со значением демаскированного на стороне получателя сессионного ключа, в байтах
CK_OBJECT_HANDLE hTempKey = NULL_PTR;arraysize(attrGOST28147KeyToWrap);
	 i++)
	if (attrGOST28147KeyToWrap[i].type == CKA_VALUE)
	{
		attrGOST28147KeyToWrap[i].pValue = pbtSessionKey;
		attrGOST28147KeyToWrap[i].ulValueLen = GOST_28147_KEY_SIZE;
		break;
	}


/*************************************************************************
* Маскирование ключа                // Хэндл ключа, который будет маскироваться/демаскироваться


while(TRUE)
{
	...
	/*Установить параметры в структуре типа CK_MECHANISM для маскирования ключа*/
	ckmWrapMech.ulParameterLen = 8;
	GenerateRandomData(ckmWrapMech.ulParameterLen, (PBYTE *)&ckmWrapMech.pParameter);
		
	/* Заполнить шаблон сессионного ключа случайными данными */
	GenerateRandomData(GOST_28147_KEY_SIZE, &pbtSessionKey);
	for (i = 0;
		 i < arraysize(attrGOST28147KeyToWrap);
		 i++)
		if (attrGOST28147KeyToWrap[i].type == CKA_VALUE)
		{
			attrGOST28147KeyToWrap[i].pValue = pbtSessionKey;
			attrGOST28147KeyToWrap[i].ulValueLen = GOST_28147_KEY_SIZE;
			break;
		}
	while(TRUE)
	{
		*
*************************************************************************/

/* Создать ключ, который будет маскирован */
		printf("Creating the GOST 28147-89 key to wrap");
		rv = pFunctionList->C_CreateObject(hSession,							// Хэндл сессии, открытой с правами Пользователя
				                           attrGOST28147KeyToWrap,				// Шаблон создаваемого ключа
				                           arraysize(attrGOST28147KeyToWrap),	// Размер шаблона
				                           &hTempKey);							// Хэндл созданного ключа
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			breakgoto wrap_exit;
		}
		printf(" -> OK\n");
 
		/* Получить размер буфера, содержащего значение маскированного ключа */
		printf("Defining wrapping key size");
		rv = pFunctionList->C_WrapKey(hSession,					// Хэндл сессии, открытой с правами Пользователя		
				                      &ckmWrapMech,				// Механизм маскирования
				                      hDerivedKey_1,			// Хэндл ключа, которым будет маскироваться ключ
				                      hTempKey,					// Хэндл ключа, который будет маскирован
				                      NULL_PTR,					// Указатель на буфер с маскированным ключом
				                      &ulWrappedKeySize);		// Размер маскированного ключа
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			breakgoto wrap_exit;
		}
		printf(" -> OK\n");
 
		pbtWrappedKey = (CK_BYTE*)malloc(ulWrappedKeySize);
		if (pbtWrappedKey == NULL)
		{
			printf("Memory allocation for pbtWrappedKey failed! \n");
			breakgoto wrap_exit;
		}		
		memset(pbtWrappedKey,
		 	  0,
		   	  ulWrappedKeySize * sizeof(CK_BYTE));
	
		/* Получить маскированный ключ на стороне отправителя */
		printf("Wrapping key");
		rv = pFunctionList->C_WrapKey(hSession,					// Хэндл сессии, открытой с правами Пользователя		
				                 	  &ckmWrapMech,				// Механизм маскирования
				                      hDerivedKey_1,			// Хэндл ключа, которым будет маскироваться ключ
				                      hTempKey,					// Хэндл ключа, который будет маскирован
				                      pbtWrappedKey,			// Указатель на буфер с маскированным ключом
				                      &ulWrappedKeySize);		// Размер маскированного ключа
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			breakgoto wrap_exit;
		}
		printf(" -> OK\n");
 
		/* Распечатать буфер, содержащий маскированный ключ */
		printf("Wrapped key data is:\n");
		for (i = 0;
			 i < ulWrappedKeySize;
	   		 i++)
		{
			printf("%02X ", pbtWrappedKey[i]);
			if ((i + 1) % 9 == 0)
				printf("\n");
}


wrap_exit:
if (hTempKey)
{
	pFunctionList->C_DestroyObject(hSession,
		                           hTempKey);
	hTempKey = NULL_PTR;
}
		break;
	}
 
	if (hTempKey)
	{
		pFunctionList->C_DestroyObject(hSession,
			
if (rv == CKR_OK)
	printf("\nWrapping has been completed successfully.\n");
else
{
	printf("\nWrapping failed!\n");
	goto exit;
}
 

/*************************************************************************
* Демаскирование ключа                                     hTempKey);
		hTempKey = NULL_PTR;
	}
		
	if (rv == CKR_OK)
		printf("\nWrapping has been completed successfully.\n");
	else
	{
		printf("\nWrapping failed!\n");
		break;
	}
 
	while (TRUE)
	{
		/* Демаскировать ключ на стороне получателя*/
		 *
*************************************************************************/
printf("Unwrapping key");
		rv = pFunctionList->C_UnwrapKey(hSession,								// Хэндл сессии, открытой с правами Пользователя	
			                            &ckmWrapMech,							// Механизм маскирования
			                            hDerivedKey_2,							// Хэндл ключа, которым был маскирован ключ
			                            pbtWrappedKey,							// Указатель на буфер с маскированным ключом
			                            ulWrappedKeySize,						// Размер буфера с маскированным ключом
			                            attrGOST28147UnwrappedKey,				// Указатель на шаблон для демаскированного ключа
			                            arraysize(attrGOST28147UnwrappedKey),	// Размер шаблона для демаскированного ключа
			                            &hTempKey);								// Указатель на буфер с маскированным ключом
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			breakgoto unwrap_exit;
		}
		printf(" -> OK\n");
			
		/* Получить буфер со значением демаскированного ключа */
		printf("Getting unwrapped key value...\n");
		
		/* Получить размер буфера для хранения значения атрибута CKA_VALUE */
		printf("Getting object value size");
		rv = pFunctionList->C_GetAttributeValue(hSession,   // Хэндл сессии, открытой с правами Пользователя
		                                        hTempKey,	// Хэндл объекта, значение атрибутов которых требуется получить 
		                                        &attrValue, // Указатель на шаблон с атрибутами, значение которых требуется получить
		                                        1);			// Количество строк в шаблоне
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			breakgoto unwrap_exit;
		}
		printf(" -> OK\n");

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

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

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

...

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

...

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

...

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

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

...