Page tree

Versions Compared

Key

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


Info
titleРутокен SDK

Расширенный набор примеров для встраивания содержится в Комплекте разработчика Рутокен

Table of Contents
maxLevel4
outlinetrue
stylenone

...

Warning

Устройства Рутокен, сертифицированные ФСБ, не поддерживают создание (импорт) ключей функцией C_CreateObject по алгоритмам ГОСТ 28147-89, ГОСТ Р 34.10-2001 и ГОСТ Р 34.10-2012 в долговременную память (с флагом CKA_TOKEN = TRUE).

...

Warning

Устройства Рутокен, сертифицированные ФСБ, не поддерживают создание (импорт) ключей функцией C_CreateObject по алгоритмам ГОСТ 28147-89, ГОСТ Р 34.10-2001 и ГОСТ Р 34.10-2012 в долговременную память (с флагом CKA_TOKEN = TRUE).

...

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

  • CKK_GOST28147 для ключей ГОСТ 28147-89,
  • CKK_MAGMA для использования в алгоритмах шифрования ГОСТ 34.12-2018 (ГОСТ Р 34.12-2015) с длиной блока 128 64 бит,
  • CKK_KUZNYECHIK KUZNECHIK для использования в алгоритмах шифрования ГОСТ 34.12-2018 (ГОСТ Р 34.12-2015) с длиной блока 64 128 бит,
  • CKK_MAGMA_TWIN_KEY для использования в алгоритме экспорта и импорта ключей ГОСТ Р 1323565.1.017-2018, построенном на основании блочного шифра «Магма»,

  • CKK_KUZNYECHIKKUZNECHIK_TWIN_KEY для использования в алгоритме экспорта и импорта ключей ГОСТ Р 1323565.1.017-2018, построенном на основании блочного шифра «Кузнечик»,

...

Code Block
languagecpp
titleШаблоны секретных ключей для ГОСТ Р 34.12-2015
CK_OBJECT_CLASS secretKeyObject = CKO_SECRET_KEY;
CK_UTF8CHAR secretKeyKuznechikLabel[] = { "Sample Kuznechik Secret Key (Aktiv Co.)" };
CK_UTF8CHAR secretKeyMagmaLabel[] = { "Sample Magma Secret Key (Aktiv Co.)" };
CK_BYTE secretKeyKuznechikId[] = { "Kuznechik Secret Key ID (Aktiv Co.)" };
CK_BYTE secretKeyMagmaId[] = { "Magma Secret Key ID (Aktiv Co.)" };
CK_KEY_TYPE keyTypeKuznechik = CKK_KUZNECHIK;
CK_KEY_TYPE keyTypeMagma = CKK_MAGMA;
CK_BBOOL attributeTrue = CK_TRUE;
CK_BBOOL attributeFalse = CK_FALSE;

/*************************************************************************
* Шаблон для генерации симметричного ключа Кузнечик для шифрования       *
*************************************************************************/
CK_ATTRIBUTE kuznechikKeyTemplate[] =
{
	{ CKA_CLASS, &secretKeyObject, sizeof(secretKeyObject) },                     // Класс - секретный ключ
	{ CKA_LABEL, &secretKeyKuznechikLabel, sizeof(secretKeyKuznechikLabel) - 1 }, // Метка ключа
	{ CKA_ID, &secretKeyKuznechikId, sizeof(secretKeyKuznechikId) - 1 },          // Идентификатор ключа
	{ CKA_KEY_TYPE, &keyTypeKuznechik, sizeof(keyTypeKuznechik) },                // Тип ключа - Кузнечик
	{ CKA_ENCRYPT, &attributeTrue, sizeof(attributeTrue) },                       // Ключ предназначен для зашифрования
	{ CKA_DECRYPT, &attributeTrue, sizeof(attributeTrue) },                       // Ключ предназначен для расшифрования
	{ CKA_SIGN, &attributeTrueattributeFalse, sizeof(attributeTrueattributeFalse) },                          // Ключ предназначен для вычисления MAC
	{ CKA_VERIFY, &attributeTrueattributeFalse, sizeof(attributeTrueattributeFalse) },                        // Ключ предназначен для проверки MAC
	{ CKA_TOKEN, &attributeTrue, sizeof(attributeTrue) },                         // Ключ является объектом токена
	{ CKA_PRIVATE, &attributeTrue, sizeof(attributeTrue) },                       // Ключ недоступен без аутентификации на токене
};

/*************************************************************************
* Шаблон для генерации симметричного ключа Магма для шифрования          *
*************************************************************************/
CK_ATTRIBUTE magmaKeyTemplate[] =
{
	{ CKA_CLASS, &secretKeyObject, sizeof(secretKeyObject) },             // Класс - секретный ключ
	{ CKA_LABEL, &secretKeyMagmaLabel, sizeof(secretKeyMagmaLabel) - 1 }, // Метка ключа
	{ CKA_ID, &secretKeyMagmaId, sizeof(secretKeyMagmaId) - 1 },          // Идентификатор ключа
	{ CKA_KEY_TYPE, &keyTypeMagma, sizeof(keyTypeMagma) },                // Тип ключа - Магма
	{ CKA_ENCRYPT, &attributeTrue, sizeof(attributeTrue) },               // Ключ предназначен для зашифрования
	{ CKA_DECRYPT, &attributeTrue, sizeof(attributeTrue) },               // Ключ предназначен для расшифрования
	{ CKA_SIGN, &attributeTrueattributeFalse, sizeof(attributeTrueattributeFalse) },                  // Ключ предназначен для вычисления MAC
	{ CKA_VERIFY, &attributeTrueattributeFalse, sizeof(attributeTrueattributeFalse) },                // Ключ предназначен для проверки MAC
	{ CKA_TOKEN, &attributeTrue, sizeof(attributeTrue) },                 // Ключ является объектом токена
	{ CKA_PRIVATE, &attributeTrue, sizeof(attributeTrue) },               // Ключ недоступен без аутентификации на токене
};

...

  • CKM_GOST28147_KEY_GEN для генерации секретного ключа ГОСТ 28147-89 (библиотекой rtPKCS11ECP),
  • CKM_GOST_KEY_GEN для генерации секретного ключа ГОСТ 28147-89 (библиотекой rtPKCS11).
  • CKM_KUZNECHIK_KEY_GEN для генерации секретного ключа ГОСТ 34.12-2018 (ГОСТ Р 34.12-2015), используемого в алгоритмах шифрования с длиной блока 64 бит.
  • CKM_MAGMA_KEY_GEN для генерации секретного ключа ГОСТ 34.12-2018 (ГОСТ Р 34.12-2015), используемого в алгоритмах шифрования с длиной блока 128 бит.

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

...

  1. kdf – идентификатор механизма диверсификации. Может быть использован один из двух механизмов:
    • CKD_NULL - нет диверсификации
    • CKD_CPDIVERSIFY_KDF
  2. pPublicData – открытый ключ получателя
  3. pUKM – имитовставкасинхропосылка

VKO GOST R  34.10-2012 (256 бит и 512 бит)

Выработанный общий ключ согласно VKO GOST R  34.10-2012 может быть возвращен только в формате не диверсифицированного Key Encryption Key (KEK).

Параметры механизма CKM_GOSTR3410_201212_DERIVE задаются байтовым массивом, который имеет следующую структуру:

  1.  4 байта (little-endian, т.е. младшие байты сначала) представляют собой значение KDF. Значение определяет механизм диверсификации:
    • CKD_NULL - нет диверсификации
    • CKM_KDF_4357
    • CKD_CPDIVERSIFY_KDF
    • CKM_KDF_GOSTR3411_2012_256
  2. 4 байта (little-endian) задают длину открытого ключа в байтах. (64 либо для 256 бит – это 64. Для 512 бит – 128)
  3. открытый ключ (n-байтовый вектор в little-endian), длина которого определена предыдущим полем
  4. 4 байта (little-endian) задают длину UKM (от 8 байт)
  5. UKM (n-байтовый вектор в little-endian) , длина определена выше.

...

  1. pLabel, pSeed – строки задающие параметры label и seed.
  2. ulR –  количество байт в счетчике итераций, с возможными значениями 1, 2, 3, 4.
  3. ulL – необходимая байтовая длина вырабатываемого ключевого материала.
  4. ulOffset – байтовое смещение, в последовательности ключевого материала, начиная с которого полученные байты используются для получения диверсифицированного ключа.

CKM

...

_GOST_KEG

Механизм CKM_VENDOR_GOST_KEG позволяет вырабатывать двойственный ключ из закрытого ключа отправителя и открытого ключа получателя.

...

Для задания параметров механизма используется структура CK_VENDORECDH1_GOST_KEGDERIVE_PARAMS:


Code Block
languagecpp
titleструктура CK_VENDORECDH1_GOST_KEGDERIVE_PARAMS
typedef struct CK_VENDORECDH1_GOST_KEGDERIVE_PARAMS {
	  CK_BYTEEC_KDF_PTRTYPE pPublicDatakdf;
	  CK_ULONG ulPublicDataLenulSharedDataLen;
	  CK_BYTE_PTR pUKMpSharedData;
	  CK_ULONG ulUKMLenulPublicDataLen;
}  CK_VENDOR_GOST_KEG_PARAMSBYTE_PTR pPublicData;
};


  1. pPublicData – открытый ключ получателя
  2. pUKM – имитовставкасинхропосылка.

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

Code Block
languagebash
titleВыработка общего ключа парной связи по схеме ключевого обмена VKO GOST 34.10-2001 на стороне отправителя
/* Размер синхропосылки в байтах */
#define UKM_LENGTH					8

#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[] = { 0xFF, 0x8D, 0xAB, 0x7F, 0x1C, 0x0B, 0x74, 0xA5, 0xAD, 0x7F, 0x0B, 0x5F, 0x8D, 0x5B, 0x3C, 0x44,
								0x58, 0x37, 0x98, 0xC9, 0x25, 0x86, 0x40, 0x7E, 0xEC, 0x6E, 0xAF, 0x00, 0xCB, 0x44, 0x65, 0xA5,
								0x22, 0x9A, 0x53, 0x56, 0x32, 0x97, 0x35, 0x80, 0x99, 0xCA, 0x1E, 0x17, 0x21, 0x3A, 0x96, 0x0E,
								0x21, 0xFB, 0xC6, 0x0F, 0x25, 0x5B, 0x5D, 0x99, 0x4E, 0xC4, 0x5C, 0x42, 0x08, 0x7D, 0x06, 0x04 };

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_BYTE 			ukm[UKM_LENGTH];									// Буфер, содержащий UKM
CK_OBJECT_HANDLE 	hDerivedKey = NULL_PTR;           					// Хэндл выработанного общего ключа
CK_OBJECT_HANDLE 	hObject;											// Хэндл объекта

...

/*************************************************************************
* Установить параметры в структуре типа CK_GOSTR3410_DERIVE_PARAMS       *
* для выработки ключа обмена                                             *
*************************************************************************/
rv = pFunctionList->C_GenerateRandom(hSession, ukm, sizeof(ukm));
if (rv != CKR_OK)
{
	printf(" -> Failed\n");
	goto exit;
}


/*************************************************************************
* Поместить в структуру типа CK_MECHANISM параметры, необходимые         *
* для выработки ключа обмена                                             *
*************************************************************************/
ulongToBuffer(deriveParameters2012_256, CKM_KDF_GOSTR3411_2012_256);
ulongToBuffer(deriveParameters2012_256 + keyLengthOffset, sizeof(cbPubRecipientKey));
memcpy(deriveParameters2012_256 + publicKeyValueOffset, cbPubRecipientKey, sizeof(cbPubRecipientKey));
ulongToBuffer(deriveParameters2012_256 + ukmLengthOffset, sizeof(ukm));
memcpy(deriveParameters2012_256 + ukmDataOffset, ukm, sizeof(ukm));
gostR3410_12DerivationMech.pParameter = deriveParameters2012_256;
gostR3410_12DerivationMech.ulParameterLen = sizeof(deriveParameters2012_256);

/* Выработать общий ключ ГОСТ 28147-89 на основании закрытого ключа отправителя и открытого ключа получателя */
printf("C_DeriveKey");
rv = pFunctionList->C_DeriveKey(hSession,	// Хэндл открытой с правами Пользователя сессии 
	&gostR3410_12DerivationMech,  			// Механизм ключевого обмена
	hPrivateKey,							// Хэндл закрытого ключа отправителя
	attrGOST28147DerivedKey,				// Шаблон создания общего ключа
	arraysize(attrGOST28147DerivedKey), 	// Размер шаблона
	&hDerivedKey);						// Хэндл общего выработанного ключа
if (rv != CKR_OK)
{
	printf(" -> Failed\n");
	goto exit;
}
printf(" -> OK\n");

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

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

/* Получить значение общего ключа ГОСТ 28147-89 */
printf("Getting object value");
rv = pFunctionList->C_GetAttributeValue(hSession,	// Хэндл открытой с правами Пользователя сессии 
	hDerivedKey,									// Хэндл общего ключа
	&attrDerivedKeyValue,							// Шаблон получения значения атрибута
	1);												// Количество атрибутов в шаблоне
if (rv != CKR_OK)
{
	printf(" -> Failed\n");
	goto exit;
}
printf(" -> OK\n");
/* Распечатать буфер со значением общего ключа ГОСТ 28147-89 */
printf("Derived key data is:\n");
for (size_t i = 0; i < attrDerivedKeyValue.ulValueLen; i++)
{
	printf("%02X ", ((CK_BYTE_PTR) attrDerivedKeyValue.pValue)[i]);
	if ((i + 1) % 8 == 0)
		printf("\n");
}

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

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

Пример выработки двойственного ключа по алгоритму

...

KEG

Code Block
languagebash
titleВыработка двойственного ключа по алгоритму KegKEG
/* Размер синхропосылки в байтах */
#define UKM_KEG_LENGTH				24

CK_UTF8CHAR derivedKuznechikTwinKeyLabel[] = { "Derived Kuznechik twin key" } // Значение для ключа ГОСТ 2012-256
//#define UKM_KEG_LENGTH				16 // Значение для ключа ГОСТ 2012-512

CK_UTF8CHAR derivedKuznechikTwinKeyLabel[] = { "Derived Kuznechik twin key" };
CK_UTF8CHAR derivedMagmaTwinKeyLabel[] = { "Derived Magma twin key" };
CK_OBJECT_CLASS secretKeyObject = CKO_SECRET_KEY;
CK_KEY_TYPE keyTypeKuznechikTwin = CKK_KUZNECHIK_TWIN_KEY;
CK_KEY_TYPE keyTypeMagmaTwin = CKK_MAGMA_TWIN_KEY;
CK_BBOOL attributeTrue = CK_TRUE;
CK_BBOOL attributeFalse = CK_FALSE;


/* Значение открытого ключа получателя для ГОСТ 2012-256*/
CK_BYTE cbPubRecipientKey[] = { 0xFF, 0x8D, 0xAB, 0x7F, 0x1C, 0x0B, 0x74, 0xA5, 0xAD, 0x7F, 0x0B, 0x5F, 0x8D, 0x5B, 0x3C, 0x44,
								0x58, 0x37, 0x98, 0xC9, 0x25, 0x86, 0x40, 0x7E, 0xEC, 0x6E, 0xAF, 0x00, 0xCB, 0x44, 0x65, 0xA5,
								0x22, 0x9A, 0x53, 0x56, 0x32, 0x97, 0x35, 0x80, 0x99, 0xCA, 0x1E, 0x17, 0x21, 0x3A, 0x96, 0x0E,
								0x21, 0xFB, 0xC6, 0x0F, 0x25, 0x5B, 0x5D, 0x99, 0x4E, 0xC4, 0x5C, 0x42, 0x08, 0x7D, 0x06, 0x04 };

/*************************************************************************
* Шаблон для создания двойственного Значение открытого ключа экспортаполучателя типадля Кузнечик         *
*************************************************************************/
CK_ATTRIBUTE derivedTwinKeyTemplate[] =
{
	{ CKA_LABEL, &derivedKuznechikTwinKeyLabel, sizeof(derivedKuznechikTwinKeyLabel) - 1},  // Метка ключа
	{ CKA_CLASS, &secretKeyObject, sizeof(secretKeyObject) },ГОСТ 2012-512*/
/*CK_BYTE cbPubRecipientKey[] = {	0xFC, 0xD5, 0xD3, 0x91, 0xEF, 0x58, 0x66, 0x50, 0x26, 0x59, 0x6C, 0x71, 0xE5, 0x89, 0x35, 0xC7,
								// Класс - секретный ключ
	{ CKA_KEY_TYPE, &keyTypeKuznechikTwin, sizeof(keyTypeKuznechikTwin)},					// Тип ключа - двойственный Кузнечик
	{ CKA_TOKEN, &attributeFalse, sizeof(attributeFalse)},0x35, 0x71, 0x28, 0xA4, 0xAD, 0x3C, 0xD5, 0x0A, 0xA3, 0xF8, 0xB1, 0xD9, 0xC1, 0x77, 0xB3, 0x17,
									// Ключ является объектом сессии
	{ CKA_MODIFIABLE, &attributeTrue, sizeof(attributeTrue)},								// Ключ может быть изменен после создания
	{ CKA_PRIVATE, &attributeTrue, sizeof(attributeTrue)},0x65, 0x0C, 0x7E, 0x6E, 0x11, 0x12, 0xC2, 0x62, 0xB3, 0xDF, 0x43, 0x32, 0x54, 0xB4, 0x7C, 0x7D,
									// Ключ доступен только после аутентификации на токене
	{ CKA_EXTRACTABLE, &attributeTrue, sizeof(attributeTrue)},	0xF3, 0x3C, 0x1F, 0xD7, 0xEA, 0x02, 0xE7, 0x70, 0x15, 0xCC, 0xFC, 0x28, 0xC6, 0xAE, 0x91, 0x29,
								// Ключ может быть извлечен в зашифрованном виде
	{ CKA_SENSITIVE, &attributeFalse, sizeof(attributeFalse)}0x58, 0xFB, 0x75, 0x14, 0x7B, 0x0E, 0x99, 0x59, 0xF9, 0x4B, 0xE9, 0x80, 0xA5, 0xBB, 0x18, 0x8E,
								0xED, 0x43, 0xCC, 0x8D, 0x9E, 0x39, 0x14, 0x6A, 0xBA, 0xC7, 0x5F, 0xFF, 0x02, 0x4C, 0x1C, 0x9E,
								// Ключ может быть извлечен в открытом виде
};0xFE, 0x71, 0xF2, 0xC3, 0xFD, 0xD6, 0x1C, 0x76, 0xBE, 0xCF, 0x77, 0xB6, 0xD7, 0x5D, 0xFF, 0x35,
								0x3C, 0x35, 0x70, 0x78, 0x03, 0xED, 0x6E, 0x0A, 0x03, 0x65, 0xDC, 0xA4, 0xAA, 0x59, 0x8B, 0xDB };*/

/*************************************************************************
* Шаблон для создания двойственного ключа экспорта типа Магма   Кузнечик         *
*************************************************************************/
//CK_ATTRIBUTE derivedTwinKeyTemplate[] =
//{
//	{ CKA_LABEL, &derivedMagmaTwinKeyLabelderivedKuznechikTwinKeyLabel, sizeof(derivedMagmaTwinKeyLabelderivedKuznechikTwinKeyLabel) - 1},          /// Метка ключа
//	{ CKA_CLASS, &secretKeyObject, sizeof(secretKeyObject) },								// Класс - секретный ключ
//	{ CKA_KEY_TYPE, &keyTypeMagmaTwinkeyTypeKuznechikTwin, sizeof(keyTypeMagmaTwinkeyTypeKuznechikTwin)},							// Тип ключа - двойственный МагмаКузнечик
//	{ CKA_TOKEN, &attributeFalse, sizeof(attributeFalse)},									// Ключ является объектом сессии
//	{ CKA_MODIFIABLE, &attributeTrue, sizeof(attributeTrue)},								// Ключ может быть изменен после создания
//	{ CKA_PRIVATE, &attributeTrue, sizeof(attributeTrue)},									// Ключ доступен только после аутентификации на токене
//	{ CKA_EXTRACTABLE, &attributeTrue, sizeof(attributeTrue)},								// Ключ может быть извлечен в зашифрованном виде
//	{ CKA_SENSITIVE, &attributeFalse, sizeof(attributeFalse)}								// Ключ может быть извлечен в открытом виде
//};

CK_MECHANISM gostKegDerifivationMech = { CKM_VENDOR_GOST_KEG, NULL_PTR, 0 };
CK_VENDOR_GOST_KEG_PARAMS keg256DeriveParams;
CK_ATTRIBUTE 		attrDerivedKeyValue = { CKA_VALUE, NULL_PTR, 0 }; 	// Структура данных типа CK_ATTRIBUTE для хранения значения атрибута CKA_VALUE
CK_BYTE 			ukm[UKM_KEG_LENGTH];									// Буфер, содержащий UKM
CK_OBJECT_HANDLE 	hDerivedKey = NULL_PTR;           					// Хэндл выработанного общего ключа
CK_OBJECT_HANDLE 	hObject;											// Хэндл объекта

...

/*************************************************************************
* УстановитьШаблон параметрыдля всоздания структуредвойственного типа CK_VENDOR_GOST_KEG_PARAMS        *
* для выработки двлйственного ключа                ключа экспорта типа Магма                      *
*************************************************************************/
rv = pFunctionList->C_GenerateRandom(hSession, ukm, sizeof(ukm));
if (rv != CKR_OK)
{
	printf(" -> Failed\n");
	goto exit;
}


/*************************************************************************
* Поместить в структуру типа CK_MECHANISM параметры, необходимые//CK_ATTRIBUTE derivedTwinKeyTemplate[] =
//{
//	{ CKA_LABEL, &derivedMagmaTwinKeyLabel, sizeof(derivedMagmaTwinKeyLabel) - 1},          *
* для выработки ключа обмена                                             *
*************************************************************************/

keg256DeriveParams.pPublicData = cbPubRecipientKey;
keg256DeriveParams.ulPublicDataLen = sizeof(cbPubRecipientKey);
keg256DeriveParams.pUKM = ukm;
keg256DeriveParams.ulUKMLen = sizeof(ukm);

gostR3410_12DerivationMech.pParameter = keg256DeriveParams;
gostR3410_12DerivationMech.ulParameterLen = sizeof(keg256DeriveParams);

/* Выработать общий ключ ГОСТ 28147-89 на основании закрытого ключа отправителя и открытого ключа получателя */
printf("C_DeriveKey");
rv = pFunctionList->C_DeriveKey(hSession,	// Хэндл открытой с правами Пользователя сессии 
	&gostR3410_12DerivationMech,  			// Механизм ключевого обмена
	hPrivateKey,// Метка ключа
//	{ CKA_CLASS, &secretKeyObject, sizeof(secretKeyObject) },								// Класс - секретный ключ
//	{ CKA_KEY_TYPE, &keyTypeMagmaTwin, sizeof(keyTypeMagmaTwin)},							// Тип ключа - двойственный Магма
//	{ CKA_TOKEN, &attributeFalse, sizeof(attributeFalse)},									// Ключ является объектом сессии
//	{ CKA_MODIFIABLE, &attributeTrue, sizeof(attributeTrue)},								// Ключ может быть изменен после создания
//	{ CKA_PRIVATE, &attributeTrue, sizeof(attributeTrue)},									// Ключ доступен только после аутентификации на токене
//	{ CKA_EXTRACTABLE, &attributeTrue, sizeof(attributeTrue)},								// Ключ может быть извлечен в зашифрованном виде
//	{ CKA_SENSITIVE, &attributeFalse, sizeof(attributeFalse)}								// Ключ может быть извлечен в открытом виде
//};

CK_ECDH1_DERIVE_PARAMS keg256DeriveParams;
CK_MECHANISM gostKegDerifivationMech = { CKM_GOST_KEG, &keg256DeriveParams, sizeof(keg256DeriveParams)};
CK_ATTRIBUTE 		attrDerivedKeyValue = { CKA_VALUE, NULL_PTR, 0 }; 	// Структура данных типа CK_ATTRIBUTE для хранения значения атрибута CKA_VALUE
CK_BYTE 			ukm[UKM_KEG_LENGTH];								// Хэндл закрытого ключа отправителя
	derivedTwinKeyTemplate,				// Шаблон создания общего ключа
	arraysize(derivedTwinKeyTemplate), 	// Размер шаблона
	&hDerivedKey);						// Хэндл общего выработанного ключа
if (rv != CKR_OK)
{
	printf(" -> Failed\n");
	goto exit;
}
printf(" -> OK\n");

/* Получить размер буфера для хранения значения атрибута CKA_VALUE*/
printf("Getting object value size");
rv = pFunctionList->C_GetAttributeValue(hSession,	// Хэндл открытой с правами Пользователя сессии 
	hDerivedKey,									// Хэндл общего ключа
	&attrDerivedKeyValue,							// Шаблон получения значения атрибута
	1);	Буфер, содержащий UKM
CK_OBJECT_HANDLE 	hDerivedKey = NULL_PTR;           					// Хэндл выработанного общего ключа
CK_OBJECT_HANDLE 	hObject;											// Количество атрибутов в шаблоне
if (rv != CKR_OK)
{
	printf(" -> Failed\n");
	goto exit;
}
printf(" -> OK\n");

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

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

...

/*************************************************************************
* Установить параметры в структуре типа CK_VENDOR_GOST_KEG_PARAMS        *
* для выработки двойственного ключа                                      *
*************************************************************************/
rv = pFunctionList->C_GenerateRandom(hSession, ukm, sizeof(ukm));
if (rv != CKR_OK)
{
	printf(" -> Failed\n");
	goto exit;
}
printf(" -> OK\n");
/* Распечатать буфер со значением общего ключа ГОСТ 28147-89 */
printf("Derived key data is:\n");
for (size_t i = 0; i < attrDerivedKeyValue.ulValueLen; i++)
{
	printf("%02X ", ((CK_BYTE_PTR)attrDerivedKeyValue.pValue)[i]);
	if ((i + 1) % 8 == 0)
		printf("\n");
}

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

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


/*************************************************************************
* Поместить в структуру типа CK_MECHANISM параметры, необходимые         *
* для выработки ключа обмена                                             *
*************************************************************************/

keg256DeriveParams.kdf = CKD_NULL;
keg256DeriveParams.pPublicData = cbPubRecipientKey;
keg256DeriveParams.ulPublicDataLen = sizeof(cbPubRecipientKey);
keg256DeriveParams.pUKM = ukm;
keg256DeriveParams.ulUKMLen = sizeof(ukm);

gostKegDerifivationMech .pParameter = &keg256DeriveParams;
gostKegDerifivationMech .ulParameterLen = sizeof(keg256DeriveParams);

/* Выработать общий двойственный ключ на основании закрытого ключа отправителя и открытого ключа получателя */
printf("C_DeriveKey");
rv = pFunctionList->C_DeriveKey(hSession,	// Хэндл открытой с правами Пользователя сессии 
	&gostKegDerifivationMech,  			// Механизм ключевого обмена
	hPrivateKey,							// Хэндл закрытого ключа отправителя
	derivedTwinKeyTemplate,					// Шаблон создания общего ключа
	arraysize(derivedTwinKeyTemplate), 		// Размер шаблона
	&hDerivedKey);							// Хэндл общего выработанного ключа
if (rv != CKR_OK)
{
	printf(" -> Failed\n");
	goto exit;
}
printf(" -> OK\n");

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

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

/* Получить значение общего двойственного ключа */
printf("Getting object value");
rv = pFunctionList->C_GetAttributeValue(hSession,	// Хэндл открытой с правами Пользователя сессии 
	hDerivedKey,									// Хэндл общего ключа
	&attrDerivedKeyValue,							// Шаблон получения значения атрибута
	1);												// Количество атрибутов в шаблоне
if (rv != CKR_OK)
{
	printf(" -> Failed\n");
	goto exit;
}
printf(" -> OK\n");
/* Распечатать буфер со значением общего двойственного ключа */
printf("Derived key data is:\n");
for (size_t i = 0; i < attrDerivedKeyValue.ulValueLen; i++)
{
	printf("%02X ", ((CK_BYTE_PTR)attrDerivedKeyValue.pValue)[i]);
	if ((i + 1) % 8 == 0)
		printf("\n");
}

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

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

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

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

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

...

Code Block
languagecpp
titleМаскирование общего выработанного ключа
/* ИмитовставкаСинхропосылка для механизма импорта и экспорта ключа алгоритма KExp15 на двойственном ключе Кузнечик */
CK_BYTE gostKuznechikKExp15Ukm[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
/* ИмитовставкаСинхропосылка для механизма импорта и экспорта ключа алгоритма KExp15 на двойственном ключе Магма */
CK_BYTE gostMagmaKExp15Ukm[] = { 0x00, 0x00, 0x00, 0x00 };

/* Механизм импорта и экспорта ключа по алгоритму KExp15 на двойственном ключе Кузнечик */
CK_MECHANISM gostKuznechikKExp15Mech = { CKM_KUZNECHIK_KEXP_15_WRAP, &gostKuznechikKExp15Ukm, sizeof(gostKuznechikKExp15Ukm) };
/* Механизм импорта и экспорта ключа по алгоритму KExp15 на двойственном ключе Кузнечик */
CK_MECHANISM gostMagmaKExp15Mech = { CKM_MAGMA_KEXP_15_WRAP, &gostMagmaKExp15Ukm, sizeof(gostMagmaKExp15Ukm) };


CK_MECHANISM_PTR gostKExp15Mech = &gostKuznechikKExp15Mech;
//CK_MECHANISM gostKExp15Mech = gostMagmaKExp15Mech;

/* DEMO-метка для сессионного ключа */
CK_UTF8CHAR sessionKeyLabel[] = { "GOST 28147-89 key to wrap" };
CK_UTF8CHAR sessionKuznechikKeyLabel[] = { "Kuznechik key to wrap" };
CK_UTF8CHAR sessionMagmaKeyLabel[] = { "Magma key to wrap" };
CK_OBJECT_CLASS secretKeyObject = CKO_SECRET_KEY;
CK_KEY_TYPE keyTypeGost28147 = CKK_GOST28147;
CK_KEY_TYPE keyTypeKuznechik = CKK_KUZNECHIK;
CK_KEY_TYPE keyTypeMagma = CKK_MAGMA;
CK_BBOOL attributeTrue = CK_TRUE;
CK_BBOOL attributeFalse = CK_FALSE;

/*************************************************************************
* Шаблон сессионного ключа типа Кузнечик                                 *
*************************************************************************/
CK_ATTRIBUTE sessionKeyTemplate[] =
{
	{ CKA_LABEL, &sessionKuznechikKeyLabel, sizeof(sessionKuznechikKeyLabel) - 1 },	// Метка ключа
	{ CKA_CLASS, &secretKeyObject, sizeof(secretKeyObject) },						// Класс - секретный ключ
	{ CKA_KEY_TYPE, &keyTypeKuznechik, sizeof(keyTypeKuznechik)},					// Тип ключа - Кузнечик
	{ CKA_TOKEN, &attributeFalse, sizeof(attributeFalse)},							// Ключ является объектом сессии
	{ CKA_MODIFIABLE, &attributeTrue, sizeof(attributeTrue)},						// Ключ может быть изменен после создания
	{ CKA_PRIVATE, &attributeTrue, sizeof(attributeTrue)},							// Ключ доступен только после аутентификации на токене
	{ CKA_VALUE, NULL_PTR, 0},														// Значение ключа
	{ CKA_EXTRACTABLE, &attributeTrue, sizeof(attributeTrue)},						// Ключ может быть извлечен в зашифрованном виде
	{ CKA_SENSITIVE, &attributeFalse, sizeof(attributeFalse)}						// Ключ может быть извлечен в открытом виде
};

/*************************************************************************
* Шаблон сессионного ключа типа Магма                                    *
*************************************************************************/
//CK_ATTRIBUTE sessionKeyTemplate[] =
//{
//	{ CKA_LABEL, &sessionMagmaKeyLabel, sizeof(sessionMagmaKeyLabel) - 1 },// Метка ключа
//	{ CKA_CLASS, &secretKeyObject, sizeof(secretKeyObject) },              // Класс - секретный ключ
//	{ CKA_KEY_TYPE, &keyTypeMagma, sizeof(keyTypeMagma)},				   // Тип ключа - Магма
//	{ CKA_TOKEN, &attributeFalse, sizeof(attributeFalse)},                 // Ключ является объектом сессии
//	{ CKA_MODIFIABLE, &attributeTrue, sizeof(attributeTrue)},              // Ключ может быть изменен после создания
//	{ CKA_PRIVATE, &attributeTrue, sizeof(attributeTrue)},                 // Ключ доступен только после аутентификации на токене
//	{ CKA_VALUE, NULL_PTR, 0},                                             // Значение ключа
//	{ CKA_EXTRACTABLE, &attributeTrue, sizeof(attributeTrue)},             // Ключ может быть извлечен в зашифрованном виде
//	{ CKA_SENSITIVE, &attributeFalse, sizeof(attributeFalse)}              // Ключ может быть извлечен в открытом виде
//};

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


/*************************************************************************
* Шаблон демаскированного сессионного ключа типа Кузнечик                *
*************************************************************************/
CK_ATTRIBUTE unwrapSessionKeyTemplate[] =
{
	{ CKA_LABEL, &sessionKuznechikKeyLabel, sizeof(sessionKuznechikKeyLabel) - 1 },	// Метка ключа
	{ CKA_CLASS, &secretKeyObject, sizeof(secretKeyObject) },						// Класс - секретный ключ
	{ CKA_KEY_TYPE, &keyTypeKuznechik, sizeof(keyTypeKuznechik)},					// Тип ключа - Кузнечик
	{ CKA_TOKEN, &attributeFalse, sizeof(attributeFalse)},							// Ключ является объектом сессии
	{ CKA_MODIFIABLE, &attributeTrue, sizeof(attributeTrue)},						// Ключ может быть изменен после создания
	{ CKA_PRIVATE, &attributeTrue, sizeof(attributeTrue)},							// Ключ доступен только после аутентификации на токене
	{ CKA_EXTRACTABLE, &attributeTrue, sizeof(attributeTrue)},						// Ключ может быть извлечен в зашифрованном виде
	{ CKA_SENSITIVE, &attributeFalse, sizeof(attributeFalse)}						// Ключ может быть извлечен в открытом виде
};

/*************************************************************************
* Шаблон демаскированного сессионного ключа типа Магма                   *
*************************************************************************/
//CK_ATTRIBUTE sessionKeyTemplate[] =
//{
//	{ CKA_LABEL, &sessionMagmaKeyLabel, sizeof(sessionMagmaKeyLabel) - 1 },// Метка ключа
//	{ CKA_CLASS, &secretKeyObject, sizeof(secretKeyObject) },              // Класс - секретный ключ
//	{ CKA_KEY_TYPE, &keyTypeMagma, sizeof(keyTypeMagma)},				   // Тип ключа - Магма
//	{ CKA_TOKEN, &attributeFalse, sizeof(attributeFalse)},                 // Ключ является объектом сессии
//	{ CKA_MODIFIABLE, &attributeTrue, sizeof(attributeTrue)},              // Ключ может быть изменен после создания
//	{ CKA_PRIVATE, &attributeTrue, sizeof(attributeTrue)},                 // Ключ доступен только после аутентификации на токене
//	{ CKA_EXTRACTABLE, &attributeTrue, sizeof(attributeTrue)},             // Ключ может быть извлечен в зашифрованном виде
//	{ CKA_SENSITIVE, &attributeFalse, sizeof(attributeFalse)}              // Ключ может быть извлечен в открытом виде
//};

/*************************************************************************
* Шаблон демаскированного сессионного ключа типа ГОСТ 28147-89           *
*************************************************************************/
//CK_ATTRIBUTE sessionKeyTemplate[] =
//{
// 	{ CKA_LABEL, &sessionKeyLabel, sizeof(sessionKeyLabel) - 1 },          // Метка ключа
// 	{ CKA_CLASS, &secretKeyObject, sizeof(secretKeyObject) },              // Класс - секретный ключ
// 	{ CKA_KEY_TYPE, &keyTypeGost28147, sizeof(keyTypeGost28147)},          // Тип ключа - ГОСТ 28147-89
// 	{ CKA_TOKEN, &attributeFalse, sizeof(attributeFalse)},                 // Ключ является объектом сессии
// 	{ CKA_MODIFIABLE, &attributeTrue, sizeof(attributeTrue)},              // Ключ может быть изменен после создания
// 	{ CKA_PRIVATE, &attributeTrue, sizeof(attributeTrue)},                 // Ключ доступен только после аутентификации на токене
// 	{ CKA_EXTRACTABLE, &attributeTrue, sizeof(attributeTrue)},             // Ключ может быть извлечен в зашифрованном виде
// 	{ CKA_SENSITIVE, &attributeFalse, sizeof(attributeFalse)}              // Ключ может быть извлечен в открытом виде
//};

/* Структура данных типа CK_ATTRIBUTE для хранения значения атрибута CKA_VALUE */
CK_ATTRIBUTE attrValue = { CKA_VALUE, NULL_PTR, 0 };

CK_OBJECT_HANDLE hDerivedTwinKey;           		 // Хэндл выработанного двйоственного ключа
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;                // Хэндл ключа, который будет маскироваться/демаскироваться

...

/* Заполнить шаблон сессионного ключа случайными данными */
GenerateRandomData(GOST_28147_KEY_SIZE, &pbtSessionKey);
for (int i = 0; i < arraysize(sessionKeyTemplate); i++)
	if (sessionKeyTemplate[i].type == CKA_VALUE)
	{
		sessionKeyTemplate[i].pValue = pbtSessionKey;
		sessionKeyTemplate[i].ulValueLen = GOST_KUZNECHIK_KEY_SIZE;
		break;
	}

/*************************************************************************
* Маскирование ключа                                                     *
*************************************************************************/

/* Создать ключ, который будет маскирован */
printf("Creating the GOST 28147-89 key to wrap");
rv = pFunctionList->C_CreateObject(hSession,	// Хэндл сессии, открытой с правами Пользователя
	sessionKeyTemplate,					// Шаблон создаваемого ключа
	arraysize(sessionKeyTemplate),		// Размер шаблона
	&hTempKey);							// Хэндл созданного ключа
if (rv != CKR_OK)
{
	printf(" -> Failed\n");
	goto wrap_exit;
}
printf(" -> OK\n");

/* Получить размер буфера, содержащего значение маскированного ключа */
printf("Defining wrapping key size");
rv = pFunctionList->C_WrapKey(hSession,					// Хэндл сессии, открытой с правами Пользователя		
	gostKExp15Mech,			// Механизм маскирования
	hDerivedTwinKey,		// Хэндл ключа, которым будет маскироваться ключ
	hTempKey,				// Хэндл ключа, который будет маскирован
	NULL_PTR,				// Указатель на буфер с маскированным ключом
	&ulWrappedKeySize);		// Размер маскированного ключа
if (rv != CKR_OK)
{
	printf(" -> Failed\n");
	goto wrap_exit;
}
printf(" -> OK\n");

pbtWrappedKey = (CK_BYTE*)malloc(ulWrappedKeySize);
if (pbtWrappedKey == NULL_PTR)
{
	printf("Memory allocation for pbtWrappedKey failed! \n");
	goto wrap_exit;
}
memset(pbtWrappedKey,
	0,
	ulWrappedKeySize * sizeof(CK_BYTE));

/* Получить маскированный ключ на стороне отправителя */
printf("Wrapping key");
rv = pFunctionList->C_WrapKey(hSession,					// Хэндл сессии, открытой с правами Пользователя		
	gostKExp15Mech,			// Механизм маскирования
	hDerivedTwinKey,		// Хэндл ключа, которым будет маскироваться ключ
	hTempKey,				// Хэндл ключа, который будет маскирован
	pbtWrappedKey,			// Указатель на буфер с маскированным ключом
	&ulWrappedKeySize);		// Размер маскированного ключа
if (rv != CKR_OK)
{
	printf(" -> Failed\n");
	goto wrap_exit;
}
printf(" -> OK\n");

/* Распечатать буфер, содержащий маскированный ключ */
printf("Wrapped key data is:\n");
for (int 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;
}

if (rv == CKR_OK)
printf("\nWrapping has been completed successfully.\n");
else
{
	printf("\nWrapping failed!\n");
	goto exit;
}


/*************************************************************************
* Демаскирование ключа                                                   *
*************************************************************************/
printf("Unwrapping key");
rv = pFunctionList->C_UnwrapKey(hSession,								// Хэндл сессии, открытой с правами Пользователя	
	gostKExp15Mech,							// Механизм маскирования
	hDerivedTwinKey,						// Хэндл ключа, которым был маскирован ключ
	pbtWrappedKey,							// Указатель на буфер с маскированным ключом
	ulWrappedKeySize,						// Размер буфера с маскированным ключом
	unwrapSessionKeyTemplate,				// Указатель на шаблон для демаскированного ключа
	arraysize(unwrapSessionKeyTemplate),	// Размер шаблона для демаскированного ключа
	&hTempKey);								// Указатель на буфер с маскированным ключом
if (rv != CKR_OK)
{
	printf(" -> Failed\n");
	goto 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");
	goto unwrap_exit;
}
printf(" -> OK\n");

/* Выделить необходимое количество памяти для значения атрибута */
attrValue.pValue = (CK_BYTE*)malloc(attrValue.ulValueLen);
if (attrValue.pValue == NULL_PTR)
{
	printf("Memory allocation for attrValue failed! \n");
	goto 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");
	goto unwrap_exit;
}
printf(" -> OK\n");
/* Распечатать буфер со значением демаскированного ключа  */
printf("Unwrapped key data:\n");
for (int i = 0; i < attrValue.ulValueLen; i++)
{
	printf("%02X ", ((CK_BYTE_PTR)attrValue.pValue)[i]);
	if ((i + 1) % 8 == 0)
		printf("\n");
}

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");
	goto exit;
}

/* Сравнить первоначальное значение сессионного ключа со значением демаскированного ключа */
if ((ulUnwrappedKeySize != GOST_KUZNECHIK_KEY_SIZE)
	|| (memcmp(pbtSessionKey,
		attrValue.pValue,
		GOST_KUZNECHIK_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");

exit:
printf("Finish");

...

  • CKM_MD2 для хеширования алгоритмом MD2 (только программно),
  • CKM_MD5 для хеширования алгоритмом MD5 (только программно),
  • CKM_SHA_1 для хеширования алгоритмом SHA-1 (только программно),
  • CKM_SHA256 для хеширования алгоритмом SHA-256 (только программно),
  • CKM_SHA512 для хеширования алгоритмом SHA-512 (только программно),
  • CKM_GOSTR3411 для хеширования алгоритмом ГОСТ Р 34.11.94 (программно и аппаратно),
  • CKM_GOSTR3411_12_256 для хеширования алгоритмом ГОСТ Р 34.11.2012 с длиной значения 256 бит (только программно и аппаратно),
  • CKM_GOSTR3411_12_512 для хеширования алгоритмом ГОСТ Р 34.11.2012 с длиной закрытого ключа 512 бит (только программно и аппаратно).

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

...

Code Block
languagecpp
titleПолучение MAC от данных по алгоритму ГОСТ Р 34.12-2015
/* Данные для хеширования в виде двоичной строки */
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 };

/*  Механизм выработки имитовставки ГОСТ 28147-89 */
CK_MECHANISM gost28147MacMech = {CKM_GOST28147_MAC, NULL_PTR, 0};
/*  Механизм выработки имитовставки ГОСТ Р 34.12-2015 */
CK_MECHANISM kuznechikMacMech = { CKM_KUZNECHIK_MAC, NULL_PTR, 0 };
CK_MECHANISM magmaMacMech = { CKM_MAGMA_MAC, NULL_PTR, 0 };
 
CK_BYTE_PTR pbtMac 	= NULL_PTR;            // Указатель на буфер для значения MAC
CK_ULONG 	ulMacSize 	= 0;               // Размер буфера в байтах
 
while(TRUE)
{
	...
 
	/* Инициализировать операцию взятия MAC*/
	printf("C_SignInit");
	rv = functionList->C_SignInit(hSession,					// Хэндл сессии
								  &kuznechikMacMech,	    // Механизм взятия MAC
								  hSecretKey);              // Хендл секретного ключа, на котором вычисляем MAC
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Определить размер значения MAC */
	printf("C_Sign step 1");
	rv = pFunctionList->C_Sign(   hSession,				// Хэндл сессии
								  pbtData,				// Буфер с данными для хеширования
								  arraysize(pbtData),	// Размер данных для хеширования
								  pbtMac,				// Буфер для вычисленного значения хеша
								  &ulMacSize);			// Размер значения хеша
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtMac = (CK_BYTE*)malloc(ulMacSize);
	if (pbtHash == NULL)
	{
		printf("Memory allocation for pbtMac failed! \n");
		break;
	}
	memset(pbtMac,
		   0,
		   (ulMacSize * sizeof(CK_BYTE)));
	/* Вычислить MAC от исходных данных */
	printf("C_Sign step 2");
	rv = pFunctionList->C_Sign(  hSession,				// Хэндл сессии
								 pbtData,				// Буфер с данными для хеширования
								 arraysize(pbtData),	// Размер данных для хеширования
								 pbtMac,				// Буфер для вычисленного значения хеша
								 &ulMacSize);			// Размер значения хеша
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	break;
}
 

...

Code Block
languagecpp
titleПроверка MAC от данных по алгоритму ГОСТ Р 34.12-2015
/* Данные для хеширования в виде двоичной строки */
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 };

/*  Механизм выработки MAC ГОСТ Р 34.12-2015 */
CK_MECHANISM kuznechikMacMech = { CKM_KUZNECHIK_MAC, NULL_PTR, 0 };
CK_MECHANISM magmaMacMech = { CKM_MAGMA_MAC, NULL_PTR, 0 };
 
while(TRUE)
{
	...
 
	/* Инициализировать операцию проверки MAC*/
	printf("C_VerifyInit");
	rv = functionList->C_VerifyInit(hSession,				  // Хэндл сессии
								    &kuznechikMacMech,	      // Механизм взятия MAC
								    hSecretKey); 		      // Хендл секретного ключа, на котором вычисляем MAC
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Проверка MAC */
	printf("C_Verify");
	rv = pFunctionList->C_Verify( hSession,				// Хэндл сессии
								  pbtData,				// Буфер с данными для хеширования
								  arraysize(pbtData),	// Размер данных для хеширования
								  pbtMac,				// Буфер для вычисленного значения хеша
								  &ulMacSize);			// Размер значения хеша
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	break;
}
 

...

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

  • CKM_GOSTR3410 подписи алгоритмом ГОСТ Р 34.10.2001 и ГОСТ Р 34.10.2012 с длиной закрытого ключа 256 бит,
  • CKM_GOSTR3410_WITH_GOSTR3411 для совместного хеширования алгоритмом CKM_GOSTR3411 и подписи алгоритмом CKM_GOSTR3410,
  • CKM_GOSTR3410_512 для подписи алгоритмом ГОСТ Р 34.10.2012 с длиной закрытого ключа 512 бит,
  • CKM_GOSTR3410_WITH_GOSTR3411_12_256 для совместного хеширования алгоритмом CKM_GOSTR3411_12_256 и подписи на ключе длиной 256 бит,

  • CKM_GOSTR3410_WITH_GOSTR3411_12_512 для совместного хеширования алгоритмом  CKM_GOSTR3411_12_512 для совместного хеширования алгоритмом  CKM_GOSTR3411_12_512 и подписи на ключе длиной 512 бит,512 и подписи на ключе длиной 512 бит,

  • CKM_RSA_PKCS для подписи алгоритмом RSA.
  • CKM_MD5_RSA_PKCS, CKM_SHA1_RSA_PKCS, CKM_SHA224_RSA_PKCS, CKM_SHA256_RSA_PKCS, CKM_SHA384_RSA_PKCS, CKM_SHA512_RSA_PKCS для алгоритма взятия хеша с последующим взятием подписи по алгоритму RSA
  • CKM_ECDSA для подписи алгоритмом ECDSACKM_RSA_PKCS для подписи алгоритмом RSA.


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

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

...

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

...

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

...

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

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

Code Block
languagecpp
titleПодпись данных по алгоритму ГОСТ Р 34.10-2012
/* Данные для подписи в виде двоичной строки */
CK_BYTE pbtData[] = { 0x3C, 0x21, 0x50, 0x49, 0x4E, 0x50, 0x41, 0x44, 0x46, 0x49, 0x4C, 0x45, 0x20, 0x52, 0x55, 0x3E, 
					  0x3C, 0x21, 0x3E, 0xED, 0xE5, 0xE2, 0xE8, 0xE4, 0xE8, 0xEC, 0xFB, 0xE9, 0x20, 0xF2, 0xE5, 0xEA, 
					  0xF1, 0xF2, 0x3C, 0x4E, 0x3E, 0xD4, 0xC8, 0xCE, 0x3A, 0x3C, 0x56, 0x3E, 0xCF, 0xE5, 0xF2, 0xF0, 
					  0xEE, 0xE2, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0x20, 0xCF, 0xE5, 0xF2, 0xF0, 0xEE, 0xE2, 0xE8, 0xF7, 
					  0x20, 0xCC, 0xEE, 0xF1, 0xEA, 0xE2, 0xE0, 0x2C, 0x20, 0xCF, 0xE8, 0xEE, 0xED, 0xE5, 0xF0, 0xF1, 
					  0xEA, 0xE0, 0xFF, 0x20, 0xF3, 0xEB, 0x2C, 0x20, 0xE4, 0x2E, 0x20, 0x33, 0x2C, 0x20, 0xEA, 0xE2, 
					  0x2E, 0x20, 0x37, 0x32 };
 
/* Набор параметров КриптоПро алгоритма ГОСТ Р 34.11-2012, 256 бит*/
CK_BYTE     GOST3411_256_params[]  = { 0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02 };
 
/*  Механизм подписи подписи по алгоритму ГОСТ Р 34.10-2012 с хешированием по алгоритму ГОСТ Р 34.11-2012 (256 бит)*/
CK_MECHANISM    HashSigVerMech256 = {CKM_GOSTR3410_WITH_GOSTR3411_12_256, GOST3411_256_params, sizeof(GOST3411_256_params)};
 
 
/* Набор параметров КриптоПро алгоритма ГОСТ Р 34.11-2012, 512 бит*/
CK_BYTE     GOST3411_512_params[]  = { 0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03 };
 
/*  Механизм подписи подписи по алгоритму ГОСТ Р 34.10-2012 с хешированием по алгоритму ГОСТ Р 34.11-2012 (512 бит)*/
CK_MECHANISM    HashSigVerMech512 = {CKM_GOSTR3410_WITH_GOSTR3411_12_512, GOST3411_512_params, sizeof(GOST3411_512_params)};

CK_BYTE_PTR pbtSignature = NULL_PTR;                 // Указатель на буфер, содержащий подпись для исходных данных
CK_ULONG ulSignatureSize = 0;                        // Размер буфера, содержащего подпись для исходных данных, в байтах
 
while(TRUE)
{
	...
 
	/* Инициализировать операцию подписи данных, 0x01, 0x01, 0x02, 0x03 };
 
/*  Механизм подписи подписи по алгоритму ГОСТ Р 34.10-2012 с хешированием по алгоритму ГОСТ Р 34.11-2012 (512 бит)*/
CK_MECHANISM    HashSigVerMech512 = {CKM_GOSTR3410_WITH_GOSTR3411_12_512, GOST3411_512_params, sizeof(GOST3411_512_params)};

CK_BYTE_PTR pbtSignature = NULL_PTR;                 // Указатель на буфер, содержащий подпись для исходных данных
CK_ULONG ulSignatureSize = 0;                        // Размер буфера, содержащего подпись для исходных данных, в байтах
 
while(TRUE)
{
	...
 
	/* Инициализировать операцию подписи данных */
	printf("C_SignInit");
	rv = pFunctionList->C_SignInit(hSession,			// Хэндл сессии
									&HashSigVerMech256,	// Механизм подписи (HashSigVerMech256 или HashSigVerMech512)
									hPrivateKey ); 		// Хэндл закрытого ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
 
	/* Определить размер подписи*/
	printf("C_Sign step 1");
	rv = pFunctionList->C_Sign(hSession,			// Хэндл сессии
								pbtData,			// Буфер с данными для подписи
								arraysize(pbtData), // Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtSignature = (CK_BYTE*)malloc(ulSignatureSize);
	if (pbtSignature  == NULL)
	{
		printf("Memory allocation for pbtSignature failed! \n");
		break;
	}
	memset( pbtSignature,
			0,
			ulSignatureSize * sizeof(CK_BYTE));

	/* Подписать исходные данные */
	printf("C_SignInitSign step 2");
	rv = pFunctionList->C_SignInitSign(hSession,			// Хэндл сессии
								pbtData,			// Буфер с данными для подписи
								arraysize(pbtData), // Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&HashSigVerMech256,ulSignatureSize);	// МеханизмДлина подписи
	if (HashSigVerMech256rv или HashSigVerMech512!= CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

		hPrivateKey ); 		// Хэндл закрытого ключа
	if (rv != CKR_OK)
	{
/* Распечатать буфер, содержащий подпись */
	printf("Signature buffer is: \n");
	for (i = 0;
		 i < ulSignatureSize;
		 i++)
	{
		printf("%02X ", pbtSignature[i]);
		if ((i + 1) % 8 == 0)
			printf(" -> Failed\n");
		break;}
	}
	printf(" -> OK\n");
 
	/* Определить размер подписи*/
	printf("C_Sign step 1");
	rv = pFunctionList->C_Sign(hSession,			// Хэндл сессии
								pbtData,			// Буфер с данными для подписи
								arraysize(pbtData), // Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtSignature = (CK_BYTE*)malloc(ulSignatureSize);
	if (pbtSignature  == NULL)
	{
		printf("Memory allocation for pbtSignature failed! \n");
		break;
	}
	memset( pbtSignature,
			0,
			ulSignatureSize * sizeof(CK_BYTE));

	/* Подписать исходные данные */
	printf("C_Sign step 2");
	rv = pFunctionList->C_Sign(hSession,			// Хэндл сессии
								pbtData,			// Буфер с данными для подписи
								arraysize(pbtData), // Длина подписываемых данных
								pbtSignature,		// Буфер с подписью
								&ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Распечатать буфер, содержащий подпись */
	printf("Signature buffer is: \n");
	for (i = 0;
		 i < ulSignatureSize;
		 i++)
	{
		printf("%02X ", pbtSignature[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");
	}
	
	break;
}

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

break;
}

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

Code Block
languagecpp
titleПодпись данных по алгоритму ECDSA
/*************************************************************************
* Данные для цифровой подписи                                            *
*************************************************************************/
CK_BYTE pbtData[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };

CK_MECHANISM sha256Mech = { CKM_SHA256, NULL_PTR, 0 };
CK_MECHANISM ecdsaSigVerMech = { CKM_ECDSA, NULL_PTR, 0 };

CK_BYTE_PTR pbtSignature;                           // Указатель на буфер, содержащий цифровую подпись для данных
CK_ULONG ulSignatureSize ;                          // Размер буфера, содержащего цифровую подпись для данных, в байтах

CK_BYTE_PTR pbtHash ;          
Code Block
languagecpp
titleПодпись данных по алгоритму ECDSA
/*************************************************************************
* Данные для цифровой подписи                     // Указатель на временный буфер для хэш-кода от данных
CK_ULONG ulHashSize;                  *
*************************************************************************/
CK_BYTE pbtData[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };

CK_MECHANISM sha256Mech = { CKM_SHA256 // Размер временного буфера в байтах



while (TRUE)
{
	...

		/* Инициализировать операцию хеширования  */
		rv = pFunctionList->C_DigestInit(hSession, &sha256Mech);
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Определить размер значения хеша данных */
	printf("C_Digest step 1");
	rv = pFunctionList->C_Digest(hSession, pbtData, sizeof(pbtData), NULL_PTR, 0 }&ulHashSize);
CK_MECHANISM ecdsaSigVerMech = { CKM_ECDSA, NULL_PTR, 0 };

CK_BYTE_PTR pbtSignature;                           // Указатель на буфер, содержащий цифровую подпись для данных
CK_ULONG ulSignatureSize ;                          // Размер буфера, содержащего цифровую подпись для данных, в байтах

CK_BYTE_PTR pbtHash ;                               // Указатель на временный буфер для хэш-кода от данных
CK_ULONG ulHashSize;                              // Размер временного буфера в байтах



while 
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtHash = (CK_BYTE*)malloc(ulHashSize);
	if (pbtHash == NULL_PTR)
	{
		printf("Memory allocation for pbtHash failed! \n");
		break;
	}
	memset(pbtHash,
		0,
		(ulHashSize * sizeof(CK_BYTE)));
	/* Сформировать хеш от исходных данных */
	printf("C_Digest step 2");
	rv = pFunctionList->C_Digest(hSession, pbtData, sizeof(pbtData), pbtHash, &ulHashSize);
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Инициализировать операцию подписи данных */
	printf("C_SignInit");
	rv = pFunctionList->C_SignInit(hSession, &ecdsaSigVerMech, hPrivateKey);
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtSignature = (CK_BYTE*)malloc(ulSignatureSize);
	if (pbtSignature == NULL_PTR)
	{
		printf("Memory allocation for pbtSignature failed! \n");
		break;
	}
	memset(pbtSignature,
		0,
		ulSignatureSize * sizeof(CK_BYTE));

	/* Подписать исходные данные */
	printf("C_Sign step 2");
	rv = pFunctionList->C_Sign(hSession, pbtHash, ulHashSize, NULL_PTR, &ulSignatureSize);
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Распечатать буфер, содержащий подпись */
	printf("Signature buffer is: \n");
	for (i = 0;
		i < ulSignatureSize;
		i++)
	{
		printf("%02X ", pbtSignature[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");
	}

	break;
}


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

Совместные механизмы хеширования и подписи (например, CKM_GOSTR3410_WITH_GOSTR3411) не поддерживаются  C_VerifyInit(). В C_Verify() передаются предварительно прохешированные функцией C_Digest() исходные данные.

Code Block
languagecpp
titleПроверка подписи
while(TRUE)
{
	...

		/* Инициализировать операцию хешированияпроверки подписи */
	printf(" C_VerifyInit");
	rv = pFunctionList->C_DigestInitVerifyInit(hSession, &sha256Mech);
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Определить размер значения хеша данных */
	printf("C_Digest step 1");
	rv = pFunctionList->C_Digest(hSession, pbtData, sizeof(pbtData), NULL_PTR, &ulHashSize);

    	// Хэндл сессии
									 &SigVerMech,	// Механизм подписи
									 hPublicKey);	// Хэндл открытого ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtHash = (CK_BYTE*)malloc(ulHashSize);
	if (pbtHash == NULL_PTR)
	{
		printf("Memory allocation for pbtHash failed! \n");
		break;
	}
	memset(pbtHash,
		0,
		(ulHashSize * sizeof(CK_BYTE)));
	/* СформироватьПроверить хешподпись отдля исходных данных */
	printf(" C_Digest step 2Verify");
	rv = pFunctionList->C_DigestVerify(hSession,  pbtData, sizeof(pbtData), pbtHash, &ulHashSize);
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Инициализировать операцию подписи данных */
	printf("C_SignInit");
	rv = pFunctionList->C_SignInit(hSession, &ecdsaSigVerMech, hPrivateKey); 		// Хэндл сессии
								 pbHash,			// Буфер с значением хеша исходногосообщения
								 ulHashSize,		// Длина буфера
								 pbtSignature,		// Буфер с подписью
								 ulSignatureSize);	// Длина подписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	pbtSignature = (CK_BYTE*)malloc(ulSignatureSize	break;
}
...
if (pbtSignature)
{
	free(pbtSignature);
	if (pbtSignature == NULL_PTR;
}
 
if (pbHash)
	{
		printf("Memory allocation for pbtSignature failed! \n"free(pbHash);
		breakpbHash= NULL_PTR;
	}}

Поточная подпись и проверка подписи

При работе с большим количеством данных бывает удобно отправлять данные токену частями. При таком режиме работе с токеном необходимо использовать функции C_SignInit/C_VerifyInitC_SignUpdate/C_VerifyUpdate и C_SignFinal/C_VerifyFinal

Code Block
languagecpp
titleПример подписи данных в несколько итераций
	/* Инициализировать операцию подписи данных
	memset(pbtSignature,
		0,
		ulSignatureSize * sizeof(CK_BYTE));

	/* Подписать исходные данные */
	printf("C_Sign step 2SignInit");
	rv = pFunctionList->C_SignSignInit( hSession, pbtHash, ulHashSize, NULL_PTR, &ulSignatureSize);		// Хэндл сессии
									&SigVerMech256,	// Механизм подписи (SigVerMech256 или SigVerMech512)
									hPrivateKey ); 	// Хэндл закрытого ключа
	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("for (size_t i=0; i < dataLen; i+=blockLen)
	{
	  size_t len = (dataLen-i) < blockLen? (dataLen-i) : blockLen;
	  // подпись отдельных блоков данных
	  rv = pFunctionList->C_SignUpdate( hSession,			// Хэндл сессии
	  									pData + i,			// Буфер с данными для подписи
	  									len);				// Длина подписываемых данных
											
	  if (rv != CKR_OK)
	  	break;
    }
	
	if (rv != CKR_OK)
	{
	  printf(" -> Failed\n");
	}

	  break;
}

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

Совместные механизмы хеширования и подписи (например, CKM_GOSTR3410_WITH_GOSTR3411) не поддерживаются  C_VerifyInit(). В C_Verify() передаются предварительно прохешированные функцией C_Digest() исходные данные.

Code Block
languagecpp
titleПроверка подписи
while(TRUE)
{
	...
	/* Инициализировать операцию проверки подписи 	}
	printf(" -> OK\n");

	/* Определить размер подписи*/
	printf(" C_VerifyInitSignFinal step 1");
	rv = pFunctionList->C_VerifyInitSignFinal(hSession,    			// Хэндл сессии
									 &SigVerMechpbtSignature,		// Буфер Механизмс подписиподписью
									 hPublicKey&ulSignatureSize);	// ХэндлДлина открытого ключаподписи
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	
	/*pbtSignature Проверить подпись для исходных данных */
= (CK_BYTE*)malloc(ulSignatureSize);
	if (pbtSignature  == NULL)
	{
		printf(" C_VerifyMemory allocation for pbtSignature failed! \n");
	rv = pFunctionList->C_Verify(hSession,   		// Хэндл сессии		break;
	}
	memset( pbtSignature,
			0,
					 pbHash,			// Буфер с значением хеша исходногосообщения
								 ulHashSize,		// Длина буфераulSignatureSize * sizeof(CK_BYTE));

	/* Подписать исходные данные */
	printf("C_SignFinal step 2");
	rv = pFunctionList->C_SignFinal(hSession,			// Хэндл сессии
								 	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_KUZNECHIK_ECB для шифрования алгоритмом Кузнечик в режиме простой замены (ГОСТ 34.13-2018),
  • CKM_KUZNECHIK_CTR_ACPKM для шифрования алгоритмом Кузнечик в режиме гаммирования CTR с мешингом ACPKM (ГОСТ 34.13Р 1323565.1.017-2018),
  • CKM_MAGMA_ECB для шифрования алгоритмом Магма в режиме простой замены (ГОСТ 34.13-2018),
  • CKM_MAGMA_CTR_ACPKM для шифрования алгоритмом Магма в режиме гаммирования CTR с мешингом ACPKM (ГОСТ 34.13Р 1323565.1.017-2018),
  • CKM_RSA_PKCS для шифрования алгоритмом RSA.

...

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

...

Code Block
languagecpp
titleШифрование данных по алгоритмам ГОСТ 28147-89 в режиме гаммирования с обратной связью и ГОСТ Р 34.12-2015 в режиме гаммирования CTR с мешингом ACPKM
/* Данные для шифрования */
CK_BYTE 	pbtData[] 	= { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	                 	 	0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
	                 		0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
	                  		0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00 };
 
CK_BYTE	 	IV[]  	 	= {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,};		// Значение вектора инициализации для алгоритма ГОСТ 28147-89 

// Механизмы CKM_KUZNECHIK_CTR_ACPKM и CKM_MAGMA_CTR_ACPKM используют два параметра: 
// период смены ключа и синхропосылку, длина которой равна половине длины блока.
// Параметры задаются в виде последовательно записанного периода смены ключа 32-битного целого, 
// представленного в BigEndian формате и синхропосылки в виде байтового массива.
// Если период смены ключа установлен в нуль, ключ по алгоритму ACPKM не меняется и механизм совпадает с режимом CTR, описанном в ГОСТ Р 34.13-2018.
CK_BYTE	 	kuznechikEncMechParams[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };	// Параметры алгоритма Кузнечик ГОСТ Р 34.12-2015
CK_BYTE     magmaEncMechParams[]     = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };	                        // Параметры алгоритма Магма ГОСТ Р 34.12-2015


/* Механизм шифрования/расшифрования  */
CK_MECHANISM	EncDecStreamMech   	= 
	{CKM_GOST28147, IV, sizeof(IV)}; // по алгоритму ГОСТ 28147-89 в режиме гаммирования с обратной связью
//    { CKM_KUZNECHIK_CTR_ACPKM, &kuznechikEncMechParams, sizeof(kuznechikEncMechParams) };  // по алгоритму Кузнечик ГОСТ Р 34.12-2015 в режиме гаммирования CTR с мешингом ACPKM
//    { CKM_MAGMA_CTR_ACPKM, &magmaEncMechParams, sizeof(magmaEncMechParams) };              // по алгоритму Магма ГОСТ Р 34.12-2015 в режиме гаммирования CTR с мешингом ACPKM

CK_BYTE_PTR 	pbtEncryptedData 	= NULL_PTR;         // Указатель на буфер, содержащий зашифрованные данные
CK_ULONG 		ulEncryptedDataSize = 0;                // Размер буфера с зашифрованными данными, в байтах 
CK_ULONG 		ulBlockSize 		= 32;				// Размер блока данных, в байтах
CK_ULONG 		ulCurrentPosition 	= 0;				// Текущее начало блока
CK_ULONG 		ulRestLen			= 0;				// Размер оставшегося буфера, в байтах
 
while(TRUE)
{
	...
	/* Инициализировать операцию шифрования */
	printf("C_EncryptInit");
	rv = pFunctionList->C_EncryptInit(hSession,				// Хэндл сессии
			                          &EncDecStreamMech,	// Механизм шифрования
			                          hSecKey);				// Хэндл секретного ключа
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");

	/* Зашифровать открытый текст */
	ulEncryptedDataSize = arraysize(pbtData);
	ulRestLen = arraysize(pbtData);
	pbtEncryptedData = (CK_BYTE*)malloc(ulEncryptedDataSize);
	if (pbtEncryptedData == NULL)
	{
		printf("Memory allocation for pbtEncryptedData failed! \n");
		break;
	}
	memset( pbtEncryptedData,
			0,
			(ulEncryptedDataSize * sizeof(CK_BYTE)));

	while (ulRestLen)
	{
		if (ulBlockSize > ulRestLen)
			ulBlockSize = ulRestLen;
		printf("Block size: %u B (Total: %u of %u) ", ulBlockSize, ulCurrentPosition + ulBlockSize, ulEncryptedDataSize);
		rv = pFunctionList->C_EncryptUpdate(hSession,								// Хэндл сессии
											pbtData + ulCurrentPosition,			// Буфер с блоком данных для шифрования
											ulBlockSize,							// Размер блока, в байтах
											pbtEncryptedData + ulCurrentPosition,	// Буфер с блоком зашифрованных данных
											&ulBlockSize);							// Размер блока, в байтах
		if (rv != CKR_OK)
		{
			printf(" -> Failed\n");
			break;
		}
		printf(" -> OK\n");

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

	printf("Finalizing encryption");
	rv = pFunctionList->C_EncryptFinal( hSession, 					// Хэндл сессии
										NULL_PTR,					// Буфер с последним блоком данных 
										&ulEncryptedDataSize);		// Длина буфера
	if (rv != CKR_OK)
	{
		printf(" -> Failed\n");
		break;
	}
	printf(" -> OK\n");
	/* Распечатать буфер, содержащий зашифрованные данные*/
	printf("Encrypted buffer is:\n");
	for (i = 0;
		 i < ulEncryptedDataSize;
	     i++)
	{
		printf("%02X ", pbtEncryptedData[i]);
		if ((i + 1) % 8 == 0)
			printf("\n");
	}

	break;
}

...