Введение
Функциональность устройств Рутокен охватывает широкий спектр задач обеспечения информационной безопасности. Устройства Рутокен могут быть применены для строгой двухфакторной аутентификации, защиты электронной переписки, установления защищенных соединений, безопасного проведения финансовых транзакций и криптографической защиты информации.
Рутокен S
Рутокен S – это носитель ключевой информации и устройство для авторизации в компьютерных системах и защиты персональных данных с реализацией отечественного стандарта шифрования "на борту".
Через интерфейс PKCS#11 доступны следующие возможности Рутокен S:
- создание, запись, чтение, изменение, удаление двоичных файлов,
- генерация и импорт ключей шифрования ГОСТ 28147-89,
- шифрование данных по алгоритму ГОСТ 28147-89 в режимах простой замены, гаммирования и гаммирования с обратной связью,
- вычисление имитовставки по алгоритму ГОСТ 28147-89 длиной 32 бит,
- генерация последовательности случайных чисел длиной 256 бит.
Ограничения Rutoken S
- Рутокен S для работы требует установки драйверов: для Windows, для Linux, для macOS.
- Рутокен S не поддерживается библиотекой rtpkcs11ecp.
- Рутокен S в ОС Windows поддерживается устаревшей библиотекой rtPKCS11.dll и очень ограниченно поддерживается библиотекой opensc-pkcs11 из состава OpenSC.
Рутокен Lite
Рутокен Lite – это ключевой носитель для безопасного хранения ключей шифрования и электронной подписи, паролей и других данных во встроенной защищенной памяти устройства.
Через интерфейс PKCS#11 доступны следующие возможности Рутокен 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 бит, генерация ключевых пар с настраиваемой проверкой качества, импорт ключевых пар, формирование электронной подписи.
- генерация последовательности случайных чисел требуемой длины.
Рутокен ЭЦП 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.
После работы с библиотекой ее нужно выгрузить из памяти.
/* Имя библиотеки 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; }
Инициализация и деинициализация библиотеки
После загрузки библиотеки ее нужно инициализировать вызовом функции C_Initialize()
. Параметр NULL
при вызове данной функции означает, что функции библиотеки не будут вызываться из нескольких потоков, в противном случае в параметре должен быть передан указатель на структуру типа CK_INITIALIZE_ARGS
.
Для завершения работы с библиотекой ее нужно деинициализировать вызовом функции C_Finalize()
.
... /* Инициализировать библиотеку */ printf("Initializing library"); rv = pFunctionList->C_Initialize(NULL_PTR); if (rv != CKR_OK) printf(" -> Failed\n"); else printf(" -> OK\n"); ... /* Деинициализировать библиотеку */ if (pFunctionList) { printf("Finalizing library"); rvTemp = pFunctionList->C_Finalize(NULL_PTR); if (rvTemp != CKR_OK) printf(" -> Failed\n"); else printf(" -> OK\n"); pFunctionList = NULL_PTR; }
Определение подключенных устройств
Доступ к каждому подключенному устройству осуществляется с помощью идентификатора слота, к которому оно подключено. Для получения списка всех слотов предназначена функция C_GetSlotList()
. Значение первого параметра указывает, должен ли список включать слоты только с подключенным токенами (CK_TRUE
) или все слоты (CK_FALSE
).
CK_SLOT_ID_PTR aSlots = NULL_PTR; // Указатель на массив идентификаторов слотов CK_ULONG ulSlotCount = 0; // Количество идентификаторов слотов в массиве while(TRUE) { ... /* Получить количество слотов c подключенными токенами */ printf(" Getting number of connected slots"); rv = pFunctionList->C_GetSlotList(CK_TRUE, NULL_PTR, &ulSlotCount); if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); aSlots = (CK_SLOT_ID*)malloc(ulSlotCount * sizeof(CK_SLOT_ID)); if (aSlots == NULL) { printf("Memory allocation for aSlots failed! \n"); break; } memset(aSlots, 0, (ulSlotCount * sizeof(CK_SLOT_ID))); /* Получить список слотов c подключенными токенами */ printf(" Getting list of connected slots"); rv = pFunctionList->C_GetSlotList(CK_TRUE, aSlots, &ulSlotCount); if (rv != CKR_OK) { printf(" -> Failed %X\n", (int)rv); break; } printf(" -> OK\n"); printf(" Slots available: 0x%8.8X\n", (int)ulSlotCount); ... break; } if (aSlots) { free(aSlots); aSlots = NULL_PTR; }
Для получения актуальной информации о состоянии конкретного слота вызывается функция C_GetSlotInfo()
, в которую передается идентификатор слота. Ее вызов запускает обновление информации обо всех слотах. Если токен извлечь из разъема и затем снова вставить в тот же самый разъем, то он может подключиться к любому свободному слоту, а не обязательно к тому же самому.
Мониторинг событий в слоте
Для мониторинга событий извлечения и подключения токенов для всех слотов используется функция C_WaitForSlotEvent()
, запущенная в отдельном потоке.
При вызове C_WaitForSlotEvent()
с флагом CKF_DONT_BLOCK
функция возвращает код CKR_NO_EVENT
при отсутствии событий или код CKR_OK
при его наличии (вместе с идентификатором соответствующего событию слота).
При вызове C_WaitForSlotEvent()
с флагом 0 выполнение функции блокируется до возникновения события и функция возвращает код CKR_OK
и номер соответствующего слота.
/* Количество потоков, одновременно ожидающих события в каком-либо слоте*/ #define MONITORING_THREADS_NUMBER 1 /* Структура данных, содержащая параметры работы для функции ожидания событий в слотах */ typedef struct _MONITORING_THREADS_PARAMS { CK_FUNCTION_LIST_PTR m_pFunctionList; CK_FLAGS m_flags; DWORD m_dwThread_Number; } MONITORING_THREADS_PARAMS, * PMONITORING_THREADS_PARAMS; /* Запустить поток, ожидающий событие в слоте. До наступления события выполнение потока заблокировано */ void Monitoring_Slots(IN void* param) // Указатель на структуру данных типа MONITORING_THREADS_PARAMS с параметрами для запуска потоков { CK_FUNCTION_LIST_PTR pFunctionList = NULL_PTR; // Указатель на список функций PKCS#11, хранящийся в структуре CK_FUNCTION_LIST CK_SLOT_ID slotID = 0xFFFFFFFF; // Идентификатор слота, в котором произошло событие CK_SLOT_INFO slotInfo; // Структура данных типа CK_SLOT_INFO с информацией о слоте CK_FLAGS ckFlags = 0; // Вспомогательная переменная для хранения флагов, передаваемых в функцию C_ cWaitForSlotEvent DWORD dwThreadNumber = 0; // Вспомогательная переменная для хранения порядкового номера запущенного потока CK_RV rv = CKR_OK; // Вспомогательная переменная для хранения кода возврата /* Получить из структуры данных типа MONITORING_THREADS_PARAMS параметры для дальнейшей работы*/ PMONITORING_THREADS_PARAMS pMonitoring_Threads_Param = (PMONITORING_THREADS_PARAMS)param; pFunctionList = pMonitoring_Threads_Param->m_pFunctionList; ckFlags = pMonitoring_Threads_Param->m_flags; dwThreadNumber = pMonitoring_Threads_Param->m_dwThread_Number; while (TRUE) { /* Ожидать событие в некотором слоте, режим работы функции C_WaitForSlotEvent зависит от значения флагов ckFlags */ slotID = 0xFFFFFFFF; rv = pFunctionList->C_WaitForSlotEvent(ckFlags, // 0 для блокирования потока и CKF_DONT_BLOCK для неблокирования &slotID, // Идентификатор слота, в котором произошло событие NULL_PTR); if (rv == CKR_CRYPTOKI_NOT_INITIALIZED) { printf("Work with PKCS#11 has been finished.\n"); break; } if (rv == CKR_NO_EVENT) { printf(" -> Failed \n" "No more slot events...\n"); break; } if (rv != CKR_OK) { printf(" -> Failed\n"); break; } memset(&slotInfo, 0, sizeof(CK_SLOT_INFO)); /* Получить информацию о слоте */ rv = pFunctionList->C_GetSlotInfo(slotID, &slotInfo); if (rv != CKR_OK) { printf(" -> Failed\n"); break; } /* Распечатать информацию о номере потока и событии в слоте */ printf("\n Monitoring thread: 0x%8.8x \n", (int)dwThreadNumber); printf(" Slot ID: 0x%8.8x \n", (int)slotID); if (slotInfo.flags & CKF_TOKEN_PRESENT) printf(" Token has been attached!\n"); else printf(" Token has been detached!\n"); } printf("Exiting from thread: 0x%8.8x \n\n", (int)dwThreadNumber); } int main(int argc, char* argv[]) { DWORD i = 0; // Вспомогательная переменная. Счетчик цикла ... while (TRUE) { ... printf("\nPlease attach or detach Rutoken and press Enter...\n"); getchar(); i = 1; while (TRUE) { printf("Events counter: 0x%8.8x \n", (int)i); /* Получить все события в слотах, не блокируя поток */ printf("C_WaitForSlotEvent"); rv = pFunctionList->C_WaitForSlotEvent(CKF_DONT_BLOCK, // Не блокировать поток &slotID, // Идентификатор слота, в котором произошло событие NULL_PTR); // Зарезервировано, должно быть NULL_PTR if (rv == CKR_NO_EVENT) { printf(" -> OK\n"); printf("No more slots events.\n"); break; } if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); } if ((rv != CKR_NO_EVENT) && (rv != CKR_OK)) break; /* Запустить поток, ожидающих событие в каком-либо слоте. До наступления события выполнение запущенного потока заблокировано. Первое событие разблокирует выполнение ожидающего потока */ while (TRUE) { MONITORING_THREADS_PARAMS aThreads_With_Blocking[MONITORING_THREADS_NUMBER]; uintptr_t aThreads[MONITORING_THREADS_NUMBER]; for (i = 0; i < MONITORING_THREADS_NUMBER; i++) { printf("Starting monitoring thread number 0x%8.8X \n", (int)i); memset(&aThreads_With_Blocking[i], 0, sizeof(MONITORING_THREADS_PARAMS)); aThreads_With_Blocking[i].m_pFunctionList = pFunctionList; aThreads_With_Blocking[i].m_flags = 0; aThreads_With_Blocking[i].m_dwThread_Number = i; aThreads[i] = CreateProc(&aThreads[i], NULL_PTR, &Monitoring_Slots, &aThreads_With_Blocking[i]); } printf("\n\nPlease attach or detach Rutoken or press Enter to exit.\n"); getchar(); break; } break; } }
Определение типа устройств
Стандарт PKCS#11 предлагает определять тип устройства по параметрам, возвращаемым функциями C_GetSlotInfo()
и C_GetTokenInfo()
. Функция C_GetSlotInfo()
возвращает структуру типа
, содержащую в поле CK_SLOT_INFO
slotDescription
имя считывателя и в поле manufacturerID
производителя устройства (Aktiv Co.
). Функция
C_GetTokenInfo()
возвращает структуру типа CK_TOKEN_INFO
, содержащую в поле model
наименование модели устройства. Имя считывателя или наименование устройства обычно позволяет однозначно идентифицировать тип подключенного устройства. В таблице приведены значения для каждого из устройств Рутокен.
Модель Рутокен | Значение slotDescription структуры CK_SLOT_INFO | Значение model структуры CK_TOKEN_INFO |
---|---|---|
Рутокен S | Aktiv Co. ruToken 0 | Rutoken S 64K, где 64K - размер памяти |
Рутокен Lite | Aktiv Rutoken lite 0 | Rutoken lite |
Рутокен ЭЦП | Aktiv Rutoken ECP 0 | Rutoken ECP |
Более удобным способом получить информацию о подключенном к слоту токене можно с помощью функции расширения C_EX_GetTokenInfoExtended()
, которая возвращает расширенные данные в виде структуры типа CK_TOKEN_INFO_EXTENDED
. Поля ulTokenClass
и ulTokenType
содержат информацию о классе и типе устройства соответственно и могут быть использованы для их определения.
CK_C_EX_GetFunctionListExtended pfGetFunctionListEx = NULL_PTR; // Указатель на функцию C_EX_GetFunctionListExtended CK_FUNCTION_LIST_EXTENDED_PTR pFunctionListEx = NULL_PTR; // Указатель на список функций расширения PKCS#11, хранящийся в структуре CK_FUNCTION_LIST_EXTENDED CK_TOKEN_INFO_EXTENDED tokenInfoEx; // Структура данных типа CK_TOKEN_INFO_EXTENDED с информацией о токене while(TRUE) { ... printf("Determining token type"); /* Получить адрес функции запроса структуры с указателями на функции расширения */ pfGetFunctionListEx = (CK_C_EX_GetFunctionListExtended)GetProcAddress(hModule, "C_EX_GetFunctionListExtended"); if (pfGetFunctionListEx == NULL_PTR) { printf(" -> Failed\n"); break; } /* Получить структуру с указателями на функции расширения */ rv = pfGetFunctionListEx(&pFunctionListEx); if (rv != CKR_OK) { printf(" -> Failed\n"); break; } memset(&tokenInfoEx, 0, sizeof(CK_TOKEN_INFO_EXTENDED)); tokenInfoEx.ulSizeofThisStructure = sizeof(CK_TOKEN_INFO_EXTENDED); /* Получить расширенную информацию о подключенном токене */ rv = pFunctionListEx->C_EX_GetTokenInfoExtended(aSlots[0], // Идентификатор слота, к которому подключен токен &tokenInfoEx); // Буфер для помещения информации о токене if (rv != CKR_OK) { printf(" -> Failed\n"); break; } /* Определить класс токена */ switch (tokenInfoEx.ulTokenClass) { case TOKEN_CLASS_S: printf(": Rutoken / Rutoken S\n"); case TOKEN_CLASS_ECP: printf(": Rutoken ECP\n"); case TOKEN_CLASS_LITE: printf(": Rutoken Lite\n"); default: printf(": undefined\n"); } break; }
Открытие и закрытие сессии
Большинство функций PKCS#11 требует наличие открытой сессии между токеном и приложением.
Для открытия сессии используется функция C_OpenSession()
, для закрытия сессии – C_CloseSession()
, для закрытия всех открытых сессий – C_CloseAllSessions().
Сессия может быть открыта только для чтения объектов на токене или для чтения и записи (флаг
CKF_RW_SESSION
). После открытия сессии приложение получает доступ к публичным объектам на токене. Для доступа к приватным объектом пользователь должен получить доступ Пользователя или Администратора функций C_Login()
.
При закрытии сессии все сессионные объекты уничтожаются, даже если приложение использует эти объекты в других сессиях.
CK_SESSION_HANDLE hSession = NULL_PTR; // Хэндл открытой сессии ... /* Открыть RW сессию в первом доступном слоте */ printf("Opening Session"); rv = pFunctionList->C_OpenSession(aSlots[0], // Идентификатор слота CKF_SERIAL_SESSION | CKF_RW_SESSION, // Флаги сессии NULL_PTR, NULL_PTR, &hSession); // Хэндл сессии if (rv != CKR_OK) printf(" -> Failed\n"); else printf(" -> OK\n"); ... /* Закрыть все открытые сессии в слоте */ printf("C_CloseAllSession"); rv = pFunctionList->C_CloseAllSessions(aSlots[0]); if (rvTemp != CKR_OK) printf(" -> Failed\n"); else printf(" -> OK\n"); hSession = NULL_PTR;
Получение и сброс прав доступа
В PKCS#11 доступны две глобальные роли: CKU_USER
– пользователь Рутокен, CKU_SO
– администратор Рутокен. Помимо них, устройства Рутокен ЭЦП имеют локальные роли, про которые можно ознакомиться в разделе Установка и смена локального PIN-кода.
Для аутентификации предварительно необходимо открыть сессию.
/* DEMO PIN-код Пользователя Рутокен */ CK_UTF8CHAR USER_PIN[] = {'1', '2', '3', '4', '5', '6', '7', '8'}; ... /* Выполнить аутентификацию Пользователя */ printf("Logging in"); rv = pFunctionList->C_Login(hSession, // Хэндл сессии CKU_USER, // Тип пользователя USER_PIN, // PIN-код пользователя sizeof(USER_PIN)); // Длина PIN-кода if (rv != CKR_OK) printf(" -> Failed\n"); else printf(" -> OK\n"); ... /*Сбросить права доступа */ printf("Logging out"); rv = pFunctionList->C_Logout(hSession); if ((rv == CKR_OK) || (rv == CKR_USER_NOT_LOGGED_IN)) printf(" -> OK\n"); else printf(" -> Failed\n");
Управление объектами на токене
Общие атрибуты объектов
Стандарт PKCS#11 различает несколько классов объектов. Объекты содержат набор атрибутов, каждый из которых имеет только одно определенное значение.
Атрибут CKA_TOKEN
определяет, будет ли созданный объект храниться только в рамках текущей сессии (значение CK_FALSE
), или будет сохранен в памяти Рутокен (значение CK_TRUE
). Сессионный объект будет уничтожен автоматически при закрытии сессии, а сохраненный на токене доступен вплоть до физического удаления объекта.
Атрибут CKA_PRIVATE
определяет доступность объекта. Пользователь не имеет доступа к приватному объекту (значение CK_TRUE
) до тех пор, пока не выполнит аутентификацию на токене. Публичный же объект доступен без аутентификации (значение CK_FALSE
).
Атрибут CKA_MODIFIABLE
отвечает за возможность изменения атрибутов объекта после его создания. По умолчанию атрибут принимает значение CK_TRUE
, при котором редактирование атрибутов объекта становится возможным. Значение CK_FALSE
означает, что созданный объект будет доступен «только для чтения» и значения атрибутов объекта после его создания не могут быть изменены.
Сгенерированные устройствами Рутокен закрытые и секретные ключевые объекты в целях безопасности является неизвлекаемыми. Это означает, что значение ключа (значение атрибута CKA_VALUE
) невозможно получить через функцию
C_GetAttributeValue()
. Все криптографические операции с такими ключами производятся внутри устройства без передачи значения ключа наружу.
Создание объекта на токене
Для создания объектов на токене предназначена функция C_CreateObject()
. В нее передается предварительно созданный шаблон с атрибутами создаваемого объекта, размер шаблона и хэндл открытой сессии с правами Пользователя. Функция возвращает хэндл созданного с указанными атрибутами объекта.
Устройства Рутокен, сертифицированные ФСБ, не поддерживают создание (импорт) ключей функцией C_CreateObject
по алгоритмам ГОСТ 28147-89, ГОСТ 34.10-2001 и ГОСТ 34.10-2012 в долговременную память (с флагом CKA_TOKEN = TRUE
).
CK_OBJECT_CLASS ocData = CKO_DATA; CK_UTF8CHAR label[] = "Data object sample"; CK_UTF8CHAR application[] = "Rutoken Control Panel"; CK_BYTE data[] = "Sample data"; CK_BBOOL true = CK_TRUE; CK_ATTRIBUTE attrDataTmpl[] = { {CKA_CLASS, &ocData, sizeof(ocData)}, {CKA_LABEL, label, sizeof(label)-1}, {CKA_VALUE, data, sizeof(data)}, {CKA_APPLICATION, application, sizeof(application)-1}, {CKA_TOKEN, &true, sizeof(true )} }; CK_OBJECT_HANDLE hData; // Хэндл созданного объекта типа CKO_DATA ... printf("Create object"); rv = pFunctionList->C_CreateObject( hSession, // Открытая с правами Пользователя сессия attrDataTmpl, // Шаблон с атрибутами создаваемого объекта arraysize(attrDataTmpl),// Размер шаблона &hData); // Возвращаемый хэндл созданного объекта if (rv != CKR_OK) printf(" -> Failed\n"); else printf(" -> OK\n");
Импорт объектов на токен
Для импорта объектов также используется функция C_CreateObject()
. В нее передается предварительно созданный шаблон с атрибутами импортируемого объекта (в том числе и значением CKA_VALUE
), размер шаблона и хэндл открытой сессии с правами Пользователя. Функция возвращает хэндл созданного с указанными атрибутами объекта.
Устройства Рутокен, сертифицированные ФСБ, не поддерживают создание (импорт) ключей функцией C_CreateObject
по алгоритмам ГОСТ 28147-89, ГОСТ 34.10-2001 и ГОСТ 34.10-2012 в долговременную память (с флагом CKA_TOKEN = TRUE
).
/* Значение сертификата ключа подписи */ CK_BYTE cbCertificate[] = { 0x30, 0x82, 0x03, 0x36, 0x30, 0x82, 0x02, 0xe5, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0a, 0x24, 0xa5, 0x24, 0x63, 0x00, 0x02, 0x00, 0x01, 0xa7, 0x15, 0x30, 0x08, 0x06, 0x06, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x03, 0x30, 0x65, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x11, 0x69, 0x6e, 0x66, 0x6f, 0x40, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x70, 0x72, 0x6f, 0x2e, 0x72, 0x75, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x52, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x43, 0x52, 0x59, 0x50, 0x54, 0x4f, 0x2d, 0x50, 0x52, 0x4f, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x20, 0x43, 0x52, 0x59, 0x50, 0x54, 0x4f, 0x2d, 0x50, 0x52, 0x4f, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x31, 0x30, 0x31, 0x33, 0x34, 0x32, 0x5a, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x30, 0x30, 0x34, 0x30, 0x37, 0x30, 0x39, 0x34, 0x31, 0x5a, 0x30, 0x65, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x07, 0x49, 0x76, 0x61, 0x6e, 0x6f, 0x66, 0x66, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x52, 0x55, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x0b, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x31, 0x32, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0e, 0x69, 0x76, 0x61, 0x6e, 0x6f, 0x76, 0x40, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x72, 0x75, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x06, 0x4d, 0x6f, 0x73, 0x63, 0x6f, 0x77, 0x30, 0x63, 0x30, 0x1c, 0x06, 0x06, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x13, 0x30, 0x12, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01, 0x03, 0x43, 0x00, 0x04, 0x40, 0xfb, 0x3c, 0xdc, 0x59, 0xc3, 0x9c, 0x4a, 0x43, 0x89, 0x87, 0xc7, 0xd7, 0xfe, 0x50, 0x19, 0xb3, 0x0c, 0x8b, 0x76, 0x97, 0xa9, 0xdf, 0xb7, 0xca, 0x2c, 0x6c, 0x3b, 0xa9, 0x13, 0xf4, 0xe0, 0x69, 0x02, 0x59, 0x92, 0x47, 0x21, 0x1a, 0xef, 0x90, 0x61, 0x91, 0x40, 0x30, 0xdd, 0x7c, 0xb0, 0x4f, 0x64, 0x5f, 0x24, 0x9a, 0xf1, 0xd6, 0x0f, 0xa9, 0xf0, 0x86, 0xd9, 0x35, 0x2b, 0x3e, 0xf2, 0xf3, 0xa3, 0x82, 0x01, 0x73, 0x30, 0x82, 0x01, 0x6f, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x04, 0xf0, 0x30, 0x26, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x1f, 0x30, 0x1d, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x22, 0x06, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x04, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x8a, 0x24, 0x35, 0x74, 0x6b, 0xf7, 0x91, 0x17, 0x92, 0xb2, 0xcf, 0x8f, 0x63, 0x87, 0xb7, 0x69, 0x06, 0xe1, 0x71, 0xf2, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x6d, 0x8f, 0x5e, 0x05, 0xd9, 0x5f, 0xac, 0x91, 0x17, 0x94, 0x1e, 0x95, 0x9a, 0x05, 0x30, 0x38, 0x37, 0x7a, 0x10, 0x2a, 0x30, 0x55, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x4e, 0x30, 0x4c, 0x30, 0x4a, 0xa0, 0x48, 0xa0, 0x46, 0x86, 0x44, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x70, 0x72, 0x6f, 0x2e, 0x72, 0x75, 0x2f, 0x43, 0x65, 0x72, 0x74, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x54, 0x65, 0x73, 0x74, 0x25, 0x32, 0x30, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x25, 0x32, 0x30, 0x43, 0x52, 0x59, 0x50, 0x54, 0x4f, 0x2d, 0x50, 0x52, 0x4f, 0x28, 0x32, 0x29, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xa0, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0x93, 0x30, 0x81, 0x90, 0x30, 0x33, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x70, 0x72, 0x6f, 0x2e, 0x72, 0x75, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x6e, 0x63, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73, 0x72, 0x66, 0x30, 0x59, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x4d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x70, 0x72, 0x6f, 0x2e, 0x72, 0x75, 0x2f, 0x43, 0x65, 0x72, 0x74, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x70, 0x6b, 0x69, 0x2d, 0x73, 0x69, 0x74, 0x65, 0x5f, 0x54, 0x65, 0x73, 0x74, 0x25, 0x32, 0x30, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x25, 0x32, 0x30, 0x43, 0x52, 0x59, 0x50, 0x54, 0x4f, 0x2d, 0x50, 0x52, 0x4f, 0x28, 0x32, 0x29, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x08, 0x06, 0x06, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x03, 0x03, 0x41, 0x00, 0x2b, 0xd2, 0xfe, 0x64, 0x54, 0x3a, 0xe1, 0xf6, 0x89, 0x75, 0xfe, 0xbb, 0xa6, 0x29, 0xed, 0x0b, 0x92, 0xc0, 0xa4, 0x84, 0x15, 0x59, 0x23, 0x12, 0x08, 0xbb, 0xd3, 0xab, 0x8e, 0x2e, 0x75, 0xb9, 0xbf, 0x9e, 0xd1, 0x9d, 0x1e, 0xf9, 0x6a, 0x24, 0xed, 0xb8, 0x58, 0x15, 0x1f, 0x03, 0x11, 0xfa, 0xd3, 0x85, 0xf1, 0x34, 0x96, 0xac, 0x20, 0x8e, 0xdd, 0xad, 0x4e, 0xae, 0x55, 0x3e, 0x8d, 0xd1, 0xff, }; /* Шаблон для импорта сертификата ключа подписи */ CK_ATTRIBUTE CertTmpl[] = { { CKA_CLASS, &ocCert, sizeof(ocCert)}, // Объект сертификата { CKA_ID, &KeyPairIDGOST1, sizeof(KeyPairIDGOST1) - 1}, // Идентификатор сертификата { CKA_TOKEN, &bTrue, sizeof(bTrue)}, // Сертификат является объектом токена { CKA_PRIVATE, &bFalse, sizeof(bFalse)}, // Сертификат доступен без авторизации на токене { CKA_VALUE, &cbCertificate, sizeof(cbCertificate)} // Значение сертификата }; CK_OBJECT_HANDLE hCert; // Хэндл сертификата printf("Import certificate"); rv = pFunctionList->C_CreateObject( hSession, // Открытая с правами Пользователя сессия CertTmpl, // Шаблон с атрибутами создаваемого объекта arraysize(CertTmpl),// Размер шаблона &hCert); // Возвращаемый хэндл созданного объекта if (rv != CKR_OK) printf(" -> Failed\n"); else printf(" -> OK\n");
Поиск объектов на токене
Для поиска объектов на токене предназначены три функции C_FindObjectsInit()
, C_FindObjects()
и C_FindObjectsFinal()
. Сначала операция поиска инициализируется функцией C_FindObjectsInit()
, в которую передается указатель шаблон атрибутов для поиска объекта и его размер, затем функция C_FindObjects()
выполняет поиск объектов согласно заданным атрибутам и возвращает хэндлы всех найденных объектов в массиве. C_FindObjectsFinal()
завершает процедуру поиска.
/* Шаблон для поиска симметричного ключа ГОСТ 28147-89 */ CK_ATTRIBUTE attrGOST28147SecKey[] = { { CKA_ID, &SecKeyID, sizeof(SecKeyID) - 1} // Критерий поиска - идентификатор ключа }; CK_OBJECT_HANDLE_PTR phObject = NULL_PTR; // Указатель на массив хэндлов объектов, соответствующих критериям поиска CK_ULONG ulObjectCount = 0; // Количество хэндлов объектов в массиве CK_RV rvTemp = CKR_OK; // Вспомогательная переменная для хранения кода возврата while (TRUE) { ... while(TRUE) { /* Инициализировать операцию поиска */ printf("C_FindObjectsInit"); rv = pFunctionList->C_FindObjectsInit(hSession, // Хэндл сессии, открытой с правами Пользователя attrGOST28147SecKey, // Шаблон для поиска с атрибутами объекта arraysize(attrGOST28147SecKey)); // Количество строк в шаблоне if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); /* Найти все объекты, соответствующие критериям поиска */ printf("C_FindObjects"); // Считаем, что максимальное количество объектов не превышает 100 phObject = (CK_OBJECT_HANDLE*)malloc(100 * sizeof(CK_OBJECT_HANDLE)); if (phObject == NULL) { printf("Memory allocation for aSlots failed! \n"); break; } memset(phObject, 0, (100 * sizeof(CK_OBJECT_HANDLE))); rv = pFunctionList->C_FindObjects(hSession, // Хэндл открытой сессии phObject, // Указатель на массив хэндлов найденных объектов 100, // Максимальное количество хэндлов найденных объектов pulObjectCount); // Фактическое количество найденных объектов if (rv != CKR_OK) printf(" -> Failed\n"); else printf(" -> OK\n"); break; } /* Деинициализировать операцию поиска */ printf("C_FindObjectsFinal"); rvTemp = pFunctionList->C_FindObjectsFinal(hSession); // Хэндл открытой сессии if (rvTemp != CKR_OK) printf(" -> Failed\n"); else printf(" -> OK\n"); if (rv == CKR_OK) printf("Search has been completed.\n" "Objects found: %d \n", (int)ulObjectCount); else { printf("Search failed!\n"); if (phObject) { free(phObject); phObject = NULL_PTR; ulObjectCount = 0; } } break; }
Чтение и изменение объектов
Для чтения атрибутов созданного объекта используется функция C_GetAttributeValue()
. Если объекты или его атрибуты не являются защищенными от записи (например, значение атрибута CKA_MODIFIABLE
равно CK_TRUE
), то значения атрибутов могут быть изменены функцией C_SetAttributeValue()
.
Сгенерированные устройствами Рутокен закрытые и секретные ключевые объекты в целях безопасности является неизвлекаемыми. Это означает, что значение ключа (значение атрибута CKA_VALUE
) невозможно получить через функцию
C_GetAttributeValue()
. Все криптографические операции с такими ключами производятся внутри устройства без извлечения ключа и передачи его значения наружу.
CK_BYTE new_data[] = "Sample Rutoken data"; CK_ATTRIBUTE attrDataReadTmpl[] = {CKA_VALUE, NULL_PTR, NULL}; CK_ATTRIBUTE attrDataEditTmpl[] = {CKA_VALUE, new_data, sizeof(new_data)}; while(true) { ... /* Изменить значение объекта */ printf("Setting object value"); rv = pFunctionList->C_SetAttributeValue(hSession, // Хэндл открытой с правами Пользователя сессии hData, // Хэндл объекта ключа &attrDataEditTmpl, // Шаблон изменения значения атрибута 1); // Количество атрибутов в шаблоне if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); /* Получить размер буфера для хранения значения атрибута CKA_VALUE*/ printf("Getting object value size"); rv = pFunctionList->C_GetAttributeValue(hSession, // Хэндл открытой с правами Пользователя сессии hData, // Хэндл объекта ключа &attrDataReadTmpl, // Шаблон получения значения атрибута 1); // Количество атрибутов в шаблоне if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); /* Выделить необходимое количество памяти для значения атрибута */ attrDataReadTmpl.pValue = (CK_BYTE*)malloc(attrDataReadTmpl.ulValueLen); if (attrDataReadTmpl.pValue == NULL) { printf("Memory allocation for attrDataReadTmpl failed! \n"); break; } memset(attrDataReadTmpl.pValue, 0, (attrDataReadTmpl.ulValueLen * sizeof(CK_BYTE))); /* Получить значение объекта */ printf("Getting object value"); rv = pFunctionList->C_GetAttributeValue(hSession, // Хэндл открытой с правами Пользователя сессии hData, // Хэндл объекта ключа &attrDataReadTmpl, // Шаблон получения значения атрибута 1); // Количество атрибутов в шаблоне if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); /* Распечатать буфер со значением объекта */ printf("Data is:\n"); for (i = 0; i < attrDataReadTmpl.ulValueLen; i++) printf("%s", attrDataReadTmpl.pValue[i]); break; } if (attrDataReadTmpl.pValue) { free(attrDataReadTmpl.pValue); attrDataReadTmpl.pValue = NULL_PTR; attrDataReadTmpl.ulValueLen= 0; }
Удаление объектов на токене
Для удаления объекта на токене необходимо знать его хэндл, который следует передать в функцию C_DestroyObject()
(предварительно должна быть открыта сессия с правами Пользователя). Если хэндл объекта неизвестен, то по любому известному атрибуту объекта можно получить его хэндл через группу функций C_FindObjectsInit()
, C_FindObjects() и
C_FindObjectsFinal()
.
/* Удалить все найденные объекты */ for (i = 0; i < ulObjectCount; i++) { printf(" C_DestroyObject %d", (int)(i + 1)); rv = pFunctionList->C_DestroyObject(hSession, // Открытая с правами Пользователя сессия phObject[i]); // Массив хэндлов объектов для удаления if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); } if (ulObjectCount != 0) printf("Destruction objects has been completed successfully.\n"); free(phObject); phObject = NULL_PTR; ulObjectCount = 0;
Генерация ключевой пары
Атрибуты ключевых объектов
Все поддерживаемые устройствами Рутокен атрибуты объектов представлены в разделе Объекты PKCS #11.
Поддерживаемые типы ключей
Устройства Рутокен поддерживают следующие типы ключей асимметричной криптографии (CK_KEY_TYPE
) :
CKK_GOSTR3410
для ключей ГОСТ Р 34.10-2001 и ГОСТ Р 34.10-2012 (256 бит),CKK_GOSTR3410_512
для ключей ГОСТ Р 34.10-2012 (512 бит),CKK_RSA
для ключей RSA.
Примеры шаблонов ключей ГОСТ Р 34.10-2012
Ниже представлены примеры шаблонов закрытого и открытого ключа ГОСТ Р 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-2012(256) */ CK_BYTE parametersGostR3410_2012_256[] = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01 }; /* Набор параметров КриптоПро A алгоритма ГОСТ Р 34.10-2012(512) */ CK_BYTE parametersGostR3410_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)}, // Параметры алгоритма { 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, parametersGostR3411_2012_512, sizeof(parametersGostR3411_2012_512)} // Параметры алгоритма ГОСТ Р 34.11-2012(512) };
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-2012(256) */ CK_BYTE parametersGostR3410_2012_256[] = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01 }; /* Набор параметров КриптоПро A алгоритма ГОСТ Р 34.10-2012(512) */ CK_BYTE parametersGostR3410_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_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, parametersGostR3410_2012_512, sizeof(parametersGostR3410_2012_512)}, // Параметры алгоритма { CKA_GOSTR3411_PARAMS, parametersGostR3411_2012_512, sizeof(parametersGostR3411_2012_512)} // Параметры алгоритма ГОСТ Р 34.11-2012(512) };
Поддерживаемые механизмы генерации ключей
Устройства Рутокен поддерживают следующие механизмы генерации ключевой пары:
CKM_GOSTR3410_KEY_PAIR_GEN
для генерации ключевой пары ГОСТ Р 34.10.2001 и ГОСТ Р 34.10.2012 с длиной ключа 256 бит,CKM_GOSTR3410_512_KEY_PAIR_GEN
для генерации ключевой пары ГОСТ Р 34.10.2012 с длиной ключа 512 бит,CKM_RSA_PKCS_KEY_PAIR_GEN
для генерации ключевой пары RSA.
Пример генерации ключевой пары
Предварительно должна быть открыта сессия чтения/записи с авторизацией с правами пользователя Рутокен.
/* Вычисление размера массива */ #define arraysize(a) (sizeof(a)/sizeof(a[0])) CK_MECHANISM gostR3410_256KeyPairGenMech = {CKM_GOSTR3410_KEY_PAIR_GEN, NULL_PTR, 0}; // Механизм генерации ключевой пары ГОСТ Р 34.10-2012(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");
Генерация секретного ключа
Атрибуты ключевых объектов
Все поддерживаемые устройствами Рутокен атрибуты объектов представлены в разделе Объекты секретных ключей.
Поддерживаемые типы ключей
Устройства Рутокен поддерживают следующие типы секретных ключей (CK_KEY_TYPE
) :
CKK_GENERIC_SECRET
для абстрактных ключей произвольной длины,CKK_GOST28147
для ключей ГОСТ 28147-89.
Примеры шаблона секретного ключа
CK_OBJECT_CLASS ocSecKey = CKO_SECRET_KEY; CK_UTF8CHAR SecKeyLabel[] = {"GOST Secret Key"}; CK_BYTE SecKeyID[] = {"GOST Secret Key"}; CK_KEY_TYPE KeyType = CKK_GOST28147; CK_BBOOL bTrue = CK_TRUE; CK_BBOOL bFalse = CK_FALSE; /* Набор параметров КриптоПро A алгоритма ГОСТ 28147-89 */ CK_BYTE GOST28147params[] = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01 }; CK_ATTRIBUTE attrGOST28147_89SecKey[] = { { CKA_CLASS, &ocSeckey, sizeof(ocSeckey)}, // Объект секретного ключа ГОСТ 28147-89 { CKA_LABEL, &SecKeyLabel, sizeof(SecKeyLabel) - 1}, // Метка ключа { CKA_ID, &SecKeyID, sizeof(SecKeyID) - 1}, // Идентификатор ключа { CKA_KEY_TYPE, &KeyType, sizeof(KeyType)}, // Тип ключа { CKA_ENCRYPT, &bTrue, sizeof(bTrue)}, // Ключ предназначен для зашифрования { CKA_DECRYPT, &bTrue, sizeof(bTrue)}, // Ключ предназначен для расшифрования { CKA_TOKEN, &bTrue, sizeof(bTrue)}, // Ключ является объектом токена { CKA_PRIVATE, &bFalse, sizeof(bFalse)}, // Ключ доступен без авторизации на токене { CKA_GOST28147_PARAMS, GOST28147params, sizeof(GOST28147params)} // Параметры алгоритма };
Поддерживаемые механизмы генерации ключей
Устройства Рутокен поддерживают следующие механизмы генерации секретного ключа:
CKM_GOST28147_KEY_GEN
для генерации секретного ключа ГОСТ 28147-89 (библиотекой rtPKCS11ECP),CKM_GOST_KEY_GEN
для генерации секретного ключа ГОСТ 28147-89 (библиотекой rtPKCS11).
Пример генерации секретного ключа
Для генерации секретного ключа предназначена функция C_GenerateKey()
, в которую передается механизм генерации и шаблон ключа.
Предварительно должна быть открыта сессия чтения/записи с авторизацией с правами Пользователя.
/* Вычисление размера массива */ #define arraysize(a) (sizeof(a)/sizeof(a[0])) CK_MECHANISM KeyGenMech = {CKM_GOST28147_KEY_GEN, NULL_PTR, 0}; // Генерация ключа ГОСТ 28147-89 CK_OBJECT_HANDLE hSecKey = NULL_PTR; // Хэндл cекретного ключа ... printf("\n Generating key"); rv = pFunctionList->C_GenerateKey(hSession, // Хэндл открытой сессии &KeyGenMech, // Используемый механизм генерации ключа attrGOST28147_89SecKey, // Шаблон для создания секретного ключа arraysize(attrGOST28147_89SecKey), // Размер шаблона секретного ключа &hSecKey); // Хэндл секретного ключа if (rv != CKR_OK) printf(" -> Failed\n"); else printf(" -> OK\n");
Выработка сеансового симметричного ключа
Функция C_DeriveKey
позволяет получить одинаковый симметричный ключ для сеансовой связи на каждой из сторон, имея закрытый ключ одной стороны и открытый ключ другой стороны.()
Устройства Рутокен поддерживают следующие механизмы согласования ключей:
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 может быть возвращен в одном из следующих форматов:
- не диверсифицированный Key Encryption Key (KEK)
- KEK, диверсифицированный по RFC-4357, п.6.5.
Выработанный общий ключ согласно VKO GOST R 34.10-2012 может быть возвращен только в формате не диверсифицированного Key Encryption Key (KEK).
Для получения сеансового ключ (CEK), зашифрованного на KEK с имитовставкой CEK, необходимо после получения ключа функцией C_DeriveKey()
использовать функцию маскирования C_WrapKey()
.
Для выработки сеансового ключ (CEK) в оперативной памяти токена, зашифрованного на KEK с имитовставкой CEK, необходимо использовать функцию расширения C_EX_WrapKey()
.
Параметры механизма согласования ключей задаются отдельной структурой типа CK_GOSTR3410_DERIVE_PARAMS
, которая содержит:
- флаг формата возвращаемого ключа (
CKD_NULL
илиCKD_CPDIVERSIFY_KD
), - значение открытого ключа второй стороны и его размер,
- вектор синхронизации и его длину.
Пример выработки общего ключа парной связи по алгоритму VKO GOST R 34.10-2012
#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, // Хэндл объекта /************************************************************************* * Поместить в структуру типа CK_MECHANISM параметры, необходимые * * для выработки ключа обмена * *************************************************************************/ 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(deriveParameters2012_256); /* Выработать общий ключ ГОСТ 28147-89 на основании закрытого ключа отправителя и открытого ключа получателя */ printf("C_DeriveKey"); rv = pFunctionList->C_DeriveKey(hSession, // Хэндл открытой с правами Пользователя сессии &gostR3410_12DerivationMech, // Механизм ключевого обмена hPrivateKey, // Хэндл закрытого ключа отправителя attrGOST28147DerivedKey, // Шаблон создания общего ключа arraysize(attrGOST28147DerivedKey), // Размер шаблона &hDerivedKey_1); // Хэндл общего выработанного ключа 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_1, // Хэндл общего ключа &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_1, // Хэндл общего ключа &attrDerivedKeyValue, // Шаблон получения значения атрибута 1); // Количество атрибутов в шаблоне if (rv != CKR_OK) { printf(" -> Failed\n"); goto 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"); } 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()
для обратной процедуры.
В этом примере мы генерируем случайным образом сессионный ключ (CEK), а затем маскируем его общим ключом KEK, полученным из функции C_Derive()
.
/* Размер симметричного ключа ГОСТ 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 < arraysize(attrGOST28147KeyToWrap); i++) if (attrGOST28147KeyToWrap[i].type == CKA_VALUE) { attrGOST28147KeyToWrap[i].pValue = pbtSessionKey; attrGOST28147KeyToWrap[i].ulValueLen = GOST_28147_KEY_SIZE; break; } /************************************************************************* * Маскирование ключа * *************************************************************************/ /* Создать ключ, который будет маскирован */ 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"); goto 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"); goto wrap_exit; } printf(" -> OK\n"); pbtWrappedKey = (CK_BYTE*)malloc(ulWrappedKeySize); if (pbtWrappedKey == NULL) { 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, // Хэндл сессии, открытой с правами Пользователя &ckmWrapMech, // Механизм маскирования hDerivedKey_1, // Хэндл ключа, которым будет маскироваться ключ hTempKey, // Хэндл ключа, который будет маскирован pbtWrappedKey, // Указатель на буфер с маскированным ключом &ulWrappedKeySize); // Размер маскированного ключа if (rv != CKR_OK) { printf(" -> Failed\n"); goto 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; } 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, // Хэндл сессии, открытой с правами Пользователя &ckmWrapMech, // Механизм маскирования hDerivedKey_2, // Хэндл ключа, которым был маскирован ключ pbtWrappedKey, // Указатель на буфер с маскированным ключом ulWrappedKeySize, // Размер буфера с маскированным ключом attrGOST28147UnwrappedKey, // Указатель на шаблон для демаскированного ключа arraysize(attrGOST28147UnwrappedKey), // Размер шаблона для демаскированного ключа &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) { 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 (i = 0; i < attrValue.ulValueLen; i++) { printf("%02X ", 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_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"); exit: printf("Finish");
Вычисление значения хеш-функции
Поддерживаемые механизмы
Устройства Рутокен поддерживают следующие механизмы хеширования:
CKM_MD2
для хеширования алгоритмом MD2 (только программно),CKM_MD5
для хеширования алгоритмом MD5 (только программно),CKM_SHA_1
для хеширования алгоритмом SHA-1 (только программно),CKM_GOSTR3411
для хеширования алгоритмом ГОСТ Р 34.11.94 (программно и аппаратно),CKM_GOSTR3411_12_256
для хеширования алгоритмом ГОСТ Р 34.11.2012 с длиной значения 256 бит (только аппаратно),CKM_GOSTR3411_12_512
для хеширования алгоритмом ГОСТ Р 34.11.2012 с длиной закрытого ключа 512 бит (только аппаратно).
Хеширование данных
Для хеширования данных служат функции C_DigestInit()
и C_Digest()
. Сначала операцию хеширования нужно инициализировать через C_DigestInit()
, передав в нее идентификатор сессии и ссылку на механизм хеширования. Затем размер буфера хешированных данных можно определить, вызвав
, и выполнить хеширование данных, вызвав C_Digest
()
второй раз.C_Digest
()
Предварительно должна быть открыта сессия чтения/записи.
Пример хеширования данных по алгоритму ГОСТ Р 34.11-94 и ГОСТ Р 34.11-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 }; /* Механизм хеширования ГОСТ Р 34.11-94 */ CK_MECHANISM gostR3411_946HashMech = {CKM_GOSTR3411, NULL_PTR, 0}; /* Механизм хеширования ГОСТ Р 34.11-2012(256) */ CK_MECHANISM gostR3411_12_256HashMech = {CKM_GOSTR3411_12_256, NULL_PTR, 0}; /* Механизм хеширования ГОСТ Р 34.11-2012(512) */ CK_MECHANISM gostR3411_12_512HashMech = {CKM_GOSTR3411_12_512, NULL_PTR, 0 }; CK_BYTE_PTR pbtHash = NULL_PTR; // Указатель на буфер для значения хеша данных CK_ULONG ulHashSize = 0; // Размер буфера в байтах while(TRUE) { ... /* Инициализировать операцию хеширования */ printf("C_DigestInit"); rv = pFunctionList->C_DigestInit(hSession, // Хэндл сессии &gostR3411_946HashMech ); // Механизм хеширования: необходимо выбрать соответствующий из // gostR3411_946HashMech, gostR3411_12_256HashMech или gostR3411_12_512HashMech if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); /* Определить размер значения хеша данных */ printf("C_Digest step 1"); rv = pFunctionList->C_Digest( hSession, // Хэндл сессии pbtData, // Буфер с данными для хеширования arraysize(pbtData), // Размер данных для хеширования pbtHash, // Буфер для вычисленного значения хеша &ulHashSize); // Размер значения хеша if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); pbtHash = (CK_BYTE*)malloc(ulHashSize); if (pbtHash == NULL) { 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, // Буфер с данными для хеширования arraysize(pbtData), // Размер данных для хеширования pbtHash, // Буфер для вычисленного значения хеша &ulHashSize); // Размер значения хеша 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
и подписи на ключе длиной 512 бит,CKM_RSA_PKCS
для подписи алгоритмом RSA.
Подпись данных
Для вычисления подписи сообщения служат функции C_SignInit()
и C_Sign()
. Сначала операцию подписи нужно инициализировать через C_SignInit()
, передав в нее идентификатор сессии, механизма и закрытого ключа. Затем размер буфера для подписанных данных можно определить, вызвав C_Sign()
с указателем на длину буфера, равной нулю, и подписать данные, вызвав C_Sign()
второй раз.
Функция C_Sign(),
как и некоторые другие функции интерфейса PKCS#11, возвращает значения различной длины и подчиняется соответствующему соглашению вызова для подобных функций, описанному в пункте 11.2 стандарта PKCS#11. Краткая его суть сводится к тому, что существует два способа вызова таких функций. В первом случае функция может быть вызвана с пустым указателем для возвращаемого буфера (NULL_PTR
), в таком случае функция вернет размер возвращаемого буфера в байтах в соответствующую переменную. Во втором случае функция может быть вызвана с непустым указателем для возвращаемого буфера и указанным значением длины буфера, в таком случае функция вернет результат выполнения криптографической операции в соответствующую переменную при условии достаточности размера буфера. Если указанная длина буфера недостаточна для возвращения всего результат, функция вернет ошибку CKR_BUFFER_TOO_SMALL
. При успешном выполнении функция возвращает код ошибки CKR_OK
.
При использовании совместных алгоритмов хеширования и подписи (например, CKM_GOSTR3410_WITH_GOSTR3411)
в C_Sign()
передается открытый текст для подписи, при использовании только алгоритма подписи (например, CKM_GOSTR3410
) – уже прохешированные данные.
При использовании совместных алгоритмов хеширования и подписи в механизме должны быть заданы параметры алгоритма хеширования.
В качестве данных на подпись может быть передан запрос на сертификат, представленный в байт-коде.
Подпись данных отдельными механизмами хеширования и подписи
При использовании отдельных механизмов хеширования и подписи сообщение сначала хешируется функциями C_DigestInit()
и C_Digest()
, а затем значение хеша подписывается функциями C_SignInit
()
и C_Sign
()
.
Пример подписи данных по алгоритму ГОСТ Р 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_MECHANISM HashMech256 = {CKM_GOSTR3411_12_256, NULL_PTR, 0}; /* Механизм хеширования ГОСТ Р 34.11-2012(512) */ CK_MECHANISM HashMech512 = {CKM_GOSTR3411_12_512, NULL_PTR, 0}; /* Механизм подписи/проверки подписи по алгоритму ГОСТ Р 34.10-2012, 256 бит */ CK_MECHANISM SigVerMech256 = {CKM_GOSTR3410, NULL_PTR, 0}; /* Механизм подписи/проверки подписи по алгоритму ГОСТ Р 34.10-2012, 512 бит */ CK_MECHANISM SigVerMech512 = {CKM_GOSTR3410_512, NULL_PTR, 0}; CK_BYTE_PTR pbtHash = NULL_PTR; // Указатель на буфер для значения хеша данных CK_ULONG ulHashSize = 0; // Размер буфера в байтах CK_BYTE_PTR pbtSignature = NULL_PTR; // Указатель на буфер, содержащий подпись для исходных данных CK_ULONG ulSignatureSize = 0; // Размер буфера, содержащего подпись для исходных данных, в байтах while(TRUE) { ... /* Инициализировать операцию хеширования */ printf("C_DigestInit"); rv = pFunctionList->C_DigestInit(hSession, // Хэндл сессии &HashMech256); // Механизм хеширования (HashMech256 или HashMech512) if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); /* Определить размер значения хеша данных */ printf("C_Digest step 1"); rv = pFunctionList->C_Digest( hSession, // Хэндл сессии pbtData, // Буфер с данными для хеширования arraysize(pbtData), // Размер данных для хеширования pbtHash, // Буфер для вычисленного значения хеша &ulHashSize); // Размер значения хеша if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); pbtHash = (CK_BYTE*)malloc(ulHashSize); if (pbtHash == NULL) { 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, // Буфер с данными для хеширования arraysize(pbtData), // Размер данных для хеширования pbtHash, // Буфер для вычисленного значения хеша &ulHashSize); // Размер значения хеша if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); /* Инициализировать операцию подписи данных */ printf("C_SignInit"); rv = pFunctionList->C_SignInit( hSession, // Хэндл сессии &SigVerMech256, // Механизм подписи (SigVerMech256 или SigVerMech512) hPrivateKey ); // Хэндл закрытого ключа if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); /* Определить размер подписи*/ printf("C_Sign step 1"); rv = pFunctionList->C_Sign( hSession, // Хэндл сессии pbtHash, // Буфер с данными для подписи ulHashSize, // Длина подписываемых данных pbtSignature, // Буфер с подписью &ulSignatureSize); // Длина подписи if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); pbtSignature = (CK_BYTE*)malloc(ulSignatureSize); 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, // Хэндл сессии pbHash, // Буфер с данными для подписи ulHashSize, // Длина подписываемых данных pbtSignature, // Буфер с подписью &ulSignatureSize); // Длина подписи if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); /* Распечатать буфер, содержащий подпись */ printf("Signature buffer is: \n"); for (i = 0; i < ulSignatureSize; i++) { printf("%02X ", pbtSignature[i]); if ((i + 1) % 8 == 0) printf("\n"); } break; }
Подпись совместным механизмом хеширования и подписи
При использовании совместного механизма и хеширование, и подпись выполняются функцией C_Sign()
. Сначала в функцию C_SignInit()
передается совместный механизм (например, CKM_GOSTR3410_WITH_GOSTR3411
), а затем в функцию C_Sign()
– сообщение.
Пример подписи данных по алгоритму ГОСТ Р 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) { ... /* Инициализировать операцию подписи данных */ 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_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; }
Проверка подписи
Для проверки подписи данных служат функции C_Verify
и Init()
C_Verify()
. Сначала операцию подписи нужно инициализировать через C_VerifyInit()
, передав в нее идентификатор сессии, механизма и открытого ключа. Затем можно проверить подпись функцией C_Verify()
.
Совместные механизмы хеширования и подписи (например, CKM_GOSTR3410_WITH_GOSTR3411)
не поддерживаются C_VerifyInit().
В
C_Verify
передаются предварительно прохешированные функцией ()
C_Digest()
исходные данные.
while(TRUE) { ... /* Инициализировать операцию проверки подписи */ printf(" C_VerifyInit"); rv = pFunctionList->C_VerifyInit(hSession, // Хэндл сессии &SigVerMech, // Механизм подписи hPublicKey); // Хэндл открытого ключа if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); /* Проверить подпись для исходных данных */ printf(" C_Verify"); rv = pFunctionList->C_Verify(hSession, // Хэндл сессии pbHash, // Буфер с значением хеша исходногосообщения ulHashSize, // Длина буфера pbtSignature, // Буфер с подписью ulSignatureSize); // Длина подписи if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); break; } ... if (pbtSignature) { free(pbtSignature); pbtSignature = NULL_PTR; } if (pbHash) { free(pbHash); pbHash= NULL_PTR; }
Шифрование и расшифрование
Поддерживаемые механизмы
Устройства Рутокен поддерживают следующие механизмы шифрования:
CKM_GOST28147_ECB
для шифрования алгоритмом ГОСТ 28147-89 в режиме простой замены,CKM_GOST28147
для шифрования алгоритмом ГОСТ 28147-89 в режиме гаммирования с обратной связью, (программное и аппаратное)CKM_RSA_PKCS
для шифрования алгоритмом RSA.
Внимание!
Так как в режиме простой замены (механизм CKM_GOST28147_ECB
) шифрование каждого блока данных осуществляется одним и тем же ключом, этот механизм должен применяться только для данных небольшого размера (например, ключей). В противном случае стойкость алгоритма снижается.
Для того, чтобы шифрование/расшифрование было выполнено аппаратно самим устройством, ключ должен находиться на токене (то есть атрибут CKA_TOKEN
используемого для шифрования ключа должен быть равен значению TRUE
). Если атрибут CKA_TOKEN
ключа, который используется для шифрования/расшифрования, равен FALSE
, то операция будет выполняться программно.
Шифрование данных
Для шифрования данных одним блоком служат функции C_EncryptInit()
и C_Encrypt()
, для поточного – С_EncryptInit(), C_EncryptUpdate()
и C_EncryptFinal().
Сначала операцию шифрования нужно инициализировать вызовом функции C_EncryptInit()
, передав в нее идентификатор сессии, механизма и секретного ключа. В параметрах механизма для ГОСТ 28147-89 можно задать вектор инициализации и его длину.
Далее шифрование можно выполнить для всего блока данных целиком, вызвав функцию C_Encrypt()
, или по частям, вызывая C_EncryptUpdate()
для каждого непоследнего блока и C_EncryptFinal()
для завершающего блока. Если в C_EncryptFinal()
передать пустой блок данных, то функция вернет суммарный размер всех блоков зашифрованных данных.
Пример шифрования данных по алгоритму ГОСТ 28147-89
/* Данные для шифрования */ CK_BYTE pbtData[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00 }; /* Механизм шифрования/расшифрования по алгоритму ГОСТ 28147-89 в режиме простой замены */ CK_MECHANISM EncDecMech = {CKM_GOST28147_ECB, NULL_PTR, 0}; CK_BYTE_PTR pbtEncryptedData = NULL_PTR; // Указатель на буфер, содержащий зашифрованные данные CK_ULONG ulEncryptedDataSize = 0; // Размер буфера с зашифрованными данными, в байтах while(TRUE) { ... /* Инициализировать операцию шифрования */ printf("C_EncryptInit"); rv = pFunctionList->C_EncryptInit(hSession, // Хэндл сессии &EncDecMech, // Механизм шифрования hSecKey); // Хэндл секретного ключа // Если атрибут CKA_TOKEN равен TRUE, то шифрование будет выполнено аппаратно, // в противном случае - программно. if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); /* Получить размер зашифрованного текста */ printf("Getting encrypted data size"); rv = pFunctionList->C_Encrypt(hSession, // Хэндл сессии pbtData, // Буфер с открытыми данными для шифрования arraysize(pbtData), // Длина буфера с открытыми данными, в байтах NULL, // Буфер с зашифрованными данными &ulEncryptedDataSize); // Длина буфера с зашифрованными данными if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); pbtEncryptedData = (CK_BYTE*)malloc(ulEncryptedDataSize); if (pbtEncryptedData == NULL) { printf("Memory allocation for pbtEncryptedData failed! \n"); break; } memset(pbtEncryptedData, 0, (ulEncryptedDataSize * sizeof(CK_BYTE))); /* Зашифровать открытый текст */ printf("C_Encrypt"); rv = pFunctionList->C_Encrypt(hSession, // Хэндл сессии pbtData, // Буфер с открытыми данными для шифрования arraysize(pbtData), // Длина буфера с открытыми данными, в байтах pbtEncryptedData, // Буфер с зашифрованными данными &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; }
/* Данные для шифрования */ 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[] = {1, 2, 3, 4, 5, 6, 7, 8}; // Значение вектора инициализации CK_ULONG ulIVLen = 8; // Длина вектора инициализации /* Механизм шифрования/расшифрования по алгоритму ГОСТ 28147-89 в режиме гаммирования с обратной связью */ CK_MECHANISM EncDecStreamMech = {CKM_GOST28147, IV, ulIVLen}; CK_BYTE_PTR pbtEncryptedData = NULL_PTR; // Указатель на буфер, содержащий зашифрованные данные CK_ULONG ulEncryptedDataSize = 0; // Размер буфера с зашифрованными данными, в байтах CK_ULONG ulBlockSize = 32; // Размер блока данных, в байтах 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; }
Расшифрование данных
Для расшифрования данных служат функции C_DecryptInit()
и C_Decrypt()
для любого режима шифрования.
Сначала операцию расшифрования нужно инициализировать вызовом функции C_DecryptInit()
, передав в нее идентификатор сессии, механизма и секретного ключа. В параметрах механизма для ГОСТ 28147-89 можно задать вектор инициализации и его длину.
Далее расшифрование выполняется вызовом функции C_Decrypt()
с передачей в нее зашифрованные данные. Размер расшифрованных данных можно узнать, вызвав C_Decrypt()
с пустым указателем вместо указателя на буфера для расшифрованных данных.
Пример расшифрования данных по алгоритму ГОСТ 28147-89
while(TRUE) { ... /* Инициализировать операцию расшифрования */ printf("C_DecryptInit"); rv = pFunctionList->C_DecryptInit(hSession, // Хэндл сессии &EncDecStreamMech, // Механизм расшифрования hSecKey); // Хэндл секретного ключа if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); /* Расшифровать шифротекст */ printf("Getting decrypted data size"); rv = pFunctionList->C_Decrypt(hSession, // Хэндл сессии pbtEncryptedData, // Буфер с зашифрованными данными ulEncryptedDataSize, // Размер зашифрованных данных NULL_PTR, // Буфер с расшифрованными данными &ulDecryptedDataSize); // Размер расшифрованных данных if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); pbtDecryptedData = (CK_BYTE*)malloc(ulDecryptedDataSize); if (pbtDecryptedData == NULL) { printf("Memory allocation for pbtDecryptedData failed! \n"); break; } memset(pbtDecryptedData, 0, (ulDecryptedDataSize * sizeof(CK_BYTE))); printf("C_Decrypt"); rv = pFunctionList->C_Decrypt(hSession, // Хэндл сессии pbtEncryptedData, // Буфер с зашифрованными данными ulEncryptedDataSize, // Размер зашифрованных данных pbtDecryptedData, // Буфер с расшифрованными данными &ulDecryptedDataSize); // Размер расшифрованных данных if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); /* Распечатать буфер, содержащий расшифрованный текст */ printf("Decrypted buffer is:\n"); for (i = 0; i < ulDecryptedDataSize; i++) { printf("%02X ", pbtDecryptedData[i]); if ((i + 1) % 8 == 0) printf("\n"); } break; } if (pbtEncryptedData) { free(pbtEncryptedData); pbtEncryptedData = NULL_PTR; } if (pbtDecryptedData) { free(pbtDecryptedData); pbtDecryptedData = NULL_PTR; }
Управление устройством
Форматирование токена
Форматирование токена возможно двумя функциями: функцией расширения C_EX_InitToken()
, которая полностью очищает память токена и сбрасывает все настройки и PIN-коды, и стандартной функцией C_InitToken()
, которая удаляет только объекты PKCS#11.
Все параметры форматирования задаются структурой типа CK_RUTOKEN_INIT_PARAM
, указатель на которую вместе с PIN-кодом Администратора передаются функции C_EX_InitToken()
.
/* Максимальное количество попыток ввода PIN-кода для Администратора */ #define MAX_ADMIN_RETRY_COUNT 10 /* Максимальное количество попыток доступа для Пользователя */ #define MAX_USER_RETRY_COUNT 10 /* Новый DEMO PIN-код Пользователя Рутокен */ static CK_UTF8CHAR NEW_USER_PIN[] = {'5', '5', '5', '5', '5', '5', '5', '5'}; /* DEMO PIN-код Администратора Рутокен */ static CK_UTF8CHAR SO_PIN[] = {'8', '7', '6', '5', '4', '3', '2', '1'}; /* DEMO метка Рутокен */ static CK_CHAR TOKEN_STD_LABEL[] = {"!!!Sample Rutoken label!!!"}; CK_RUTOKEN_INIT_PARAM initInfo_st; // Структура данных типа CK_RUTOKEN_INIT_PARAM, содержащая параметры для работы функции C_EX_InitToken CK_BBOOL bIsRutokenECP = FALSE; // Вспомогательная переменная для хранения признака типа токена /* Заполнение полей структуры CK_RUTOKEN_INIT_PARAM */ memset(&initInfo_st, 0, sizeof(CK_RUTOKEN_INIT_PARAM)); initInfo_st.ulSizeofThisStructure = sizeof(CK_RUTOKEN_INIT_PARAM); // Размер структуры initInfo_st.UseRepairMode = 0; // 0 - для форматирования требуются права Администратора, !0 - нет initInfo_st.pNewAdminPin = SO_PIN; // Новый PIN-код Администратора initInfo_st.ulNewAdminPinLen = sizeof(SO_PIN); // Длина нового PIN-кода Администратора initInfo_st.pNewUserPin = NEW_USER_PIN; // Новый PIN-код Пользователя initInfo_st.ulNewUserPinLen = sizeof(NEW_USER_PIN); // Длина нового PIN-кода Администратора initInfo_st.ulMinAdminPinLen = bIsRutokenECP ? 6 : 1; // Минимальная длина PIN-кода Администратора initInfo_st.ulMinUserPinLen = bIsRutokenECP ? 6 : 1; // Минимальная длина PIN-кода Администратора initInfo_st.ChangeUserPINPolicy = // Политика смены PIN-кода Пользователя - Пользователь и Администратор (TOKEN_FLAGS_ADMIN_CHANGE_USER_PIN | TOKEN_FLAGS_USER_CHANGE_USER_PIN); initInfo_st.ulMaxAdminRetryCount = MAX_ADMIN_RETRY_COUNT; // Максимальное количество неудачных попыток аутентификации Администратора initInfo_st.ulMaxUserRetryCount = MAX_USER_RETRY_COUNT; // Максимальное количество неудачных попыток аутентификации Пользователя initInfo_st.pTokenLabel = TOKEN_STD_LABEL; // Метка токена initInfo_st.ulLabelLen = sizeof(TOKEN_STD_LABEL); // Длина метки токена /* Инициализация токена */ printf("Initializing token"); rv = pFunctionListEx->C_EX_InitToken(aSlots[0], // Идентификатор слота с подключенным токеном SO_PIN, // Текущий PIN-кода Администратора arraysize(SO_PIN), // Длина текущего PIN-кода Администратора &initInfo_st); // Указатель на структуру с параметрами форматирования if (rv != CKR_OK) printf(" -> Failed\n"); else printf(" -> OK\n");
При форматировании стандартной функцией C_InitToken()
возможно задать только новый PIN-код Администратора и метку токена. После форматирования следует задать PIN-код Пользователя функцией C_InitPIN()
, поскольку после форматирования через C_InitToken()
его значение остается незаданным.
/* DEMO метка Rutoken */ static CK_CHAR TOKEN_LABEL[] = { 'M', 'y', ' ', 'R', 'u', 't', 'o', 'k', 'e', 'n', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }; while(TRUE) { ... /* Инициализировать токен */ printf("C_InitToken"); rv = pFunctionList->C_InitToken(aSlots[0], // Идентификатор слота с подключенным токеном SO_PIN, // Новый PIN-код Администратора sizeof(SO_PIN), // Длина нового PIN-кода Администратора TOKEN_LABEL); // Новая метка токена if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); ... /* Инициализировать PIN-код Пользователя */ printf(" Initializing User PIN "); rv = pFunctionList->C_InitPIN(hSession, // Хэндл открытой сессии с правами Администратора USER_PIN, // Новый PIN-код пользователя sizeof(USER_PIN)); // Длина нового PIN-кода пользователя if (rv != CKR_OK) { printf(" -> Failed\n"); break; } printf(" -> OK\n"); break; }
Установка и смена локального PIN-кода
Помимо двух глобальных ролей Администратора и Пользователя, устройства Рутокен поддерживают до 29 PIN-кодов Локальных Пользователей для разных технических нужд.
Установка или смена локального PIN-кода должна выполняться Пользователем Рутокен и не требует открытой сессии и авторизации – PIN-код Пользователя передается прямо в функцию C_EX_SetLocalPin()
.
CK_SLOT_ID slotID; // Идентификатор слота, к которому подключен токен CK_UTF8CHAR_PTR pUserPin; // PIN-код Пользователя Рутокен CK_ULONG ulUserPinLen; // Длина PIN-кода Пользователя Рутокен CK_UTF8CHAR_PTR pNewLocalPin; // Локальный PIN-код CK_ULONG ulNewLocalPinLen; // Длина локального PIN-кода CK_ULONG ulLocalID; // Идентификатор локального PIN-кода printf("Setting Local PIN-code"); rv = pfGetFunctionListEx->C_EX_SetLocalPIN( slotID, // Идентификатор слота, к которому подключен Рутокен pUserPin, // Текущий PIN-код Пользователя Рутокен ulUserPinLen, // Длина текущего PIN-кода Пользователя Рутокен pNewLocalPin, // Новый локальный PIN-код &ulNewLocalPinLen, // Длина нового локального PIN-кода ulLocalID); // Идентификатор локального PIN-кода if (rv != CKR_OK) printf(" -> Failed\n"); else printf(" -> OK\n");
Разблокировка PIN-кода Пользователя
В целях безопасности для пользовательского PIN-кода введен счетчик попыток неправильного ввода. При превышении заданного максимума попыток PIN-код блокируется и дальнейшая аутентификация с правами Пользователя становится невозможной. Сброс счетчика попыток неудачного входа осуществляется функцией C_EX_UnblockUserPIN()
, в которую передается хэндл сессии с предварительно выполненным входом с правами Администратора.
Счетчик автоматически сбрасывается при вводе правильного PIN-кода Пользователя, если не был превышен заданный максимум попыток.
/* Разблокировать PIN-код Пользователя */ printf("Unlock User PIN"); rv = pFunctionListEx->C_EX_UnblockUserPIN(hSession); // Хэндл открытой с правами Администратора сессии if (rv != CKR_OK) printf(" -> Failed\n"); else printf(" -> OK\n");
Управление памятью Рутокен ЭЦП Flash
Получение объема флеш-памяти
Получить весь объем внешней флеш-памяти можно с помощью функции расширения C_EX_GetDriveSize()
, передав в нее идентификатор слота с подключенным токеном и указатель на буфер, в который будет возвращен полученный объем памяти в Мб.
CK_ULONG ulDriveSize = 0; // Общий объем флеш-памяти printf("Get Flash memory size"); rv = pFunctionListEx->C_EX_GetDriveSize(aSlots[0], // Идентификатор слота с подключенным токеном &ulDriveSize); // Возвращаемый размер флеш-памяти в Мб if (rv != CKR_OK) printf(" -> Failed\n"); else { printf(" -> OK\n"); printf("Memory size: %d Mb\n", (int)ulDriveSize); }
Создание разделов флеш-памяти
Флеш-память Рутокен ЭЦП Flash может быть разбита на несколько независимых разделов с разными правами доступа к ним. Минимальное количество разделов – 1, максимальное – 8.
Рутокен ЭЦП Flash поддерживает следующие права доступа к разделам: для чтения и записи (ACCESS_MODE_RW
), только для чтения (ACCESS_MODE_RO
), скрытый раздел, защищенный от отображения в операционной системе, чтения, записи и любого другого типа доступа (ACCESS_MODE_HIDDEN
) и раздел, эмулирующий CD-ROM (ACCESS_MODE_CD
).
Владельцем раздела может выступать Администратор Рутокен (CKU_SO
), Пользователь Рутокен (CKU_USER
), а также локальный пользователь (с идентификатором в пределах от 0x03 до 0x1F) с предварительно заданным функцией C_EX_SetLocalPin()
PIN-кодом.
Для разметки флеш-памяти на разделы предназначена функция C_EX_FormatDrive()
. Вся информация о разделах (объем памяти, права доступа, владелец и флаги) задается в массиве структур типа CK_VOLUME_FORMAT_INFO_EXTENDED
, который затем вместе в PIN-кодом Администратора и идентификатором слота, к которому подключен Рутокен, передается в функцию C_EX_FormatDrive()
.
CK_ULONG VolumeRWSize = 0; // Размер раздела для чтения и записи CK_ULONG VolumeROSize = 0; // Размер раздела только для чтения CK_ULONG VolumeCDSize = 0; // Размер раздела CD-ROM CK_ULONG VolumeHISize = 0; // Размер раздела скрытого раздела CK_ULONG CKU_LOCAL_1 = 0x03; // Идентификатор локального пользователя CK_ULONG CKU_LOCAL_2 = 0x1E; // Идентификатор локального пользователя /* Шаблон для разметки разделов */ CK_VOLUME_FORMAT_INFO_EXTENDED InitParams[] = { { VolumeRWSize, ACCESS_MODE_RW, CKU_USER, 0 }, { VolumeROSize, ACCESS_MODE_RO, CKU_SO, 0 }, { VolumeHISize, ACCESS_MODE_HIDDEN, CKU_LOCAL_1, 0 }, { VolumeCDSize, ACCESS_MODE_CD, CKU_LOCAL_2, 0 } }; ... InitParams[0].ulVolumeSize = ulDriveSize / 2; InitParams[1].ulVolumeSize = ulDriveSize / 4; InitParams[2].ulVolumeSize = ulDriveSize / 8; InitParams[3].ulVolumeSize = ulDriveSize - (ulDriveSize / 2) - (ulDriveSize / 4) - (ulDriveSize / 8); printf("\nFormatting flash memory"); rv = pFunctionListEx->C_EX_FormatDrive( aSlots[0], // Идентификатор слота с подключенным токеном CKU_SO, // Форматирование выполняется только с правами Администратора SO_PIN, // Текущий PIN-код Администратора sizeof(SO_PIN), // Длина PIN-кода Администратора InitParams, // Массив с информацией о разделах arraysize(InitParams)); // Размер массива if (rv != CKR_OK) printf(" -> Failed\n"); else printf(" -> OK\n");
Получение информации о разделах флеш-памяти
Получить информацию о существующих на флеш-памяти разделах можно с помощью функции C_EX_GetVolumesInfo()
, передав в нее идентификатор слота, к которому подключен токен, указатель на буфер и его размер, куда будет возвращен массив структур типа CK_VOLUME_INFO_EXTENDED
с информацией о разделах (идентификатор раздела, его размер, права доступа, владелец и флаги). Размер буфера можно определить, вызвав C_EX_GetVolumesInfo()
с пустым указателем на буфер.
CK_VOLUME_INFO_EXTENDED_PTR pVolumesInfo = NULL_PTR; // Указатель на массив структур с информацией о разделах CK_ULONG ulVolumesInfoCount = 0; // Размер массива с информацией о разделах printf("\nGetting volumes info"); rv = pFunctionListEx->C_EX_GetVolumesInfo( aSlots[0], // Идентификатор слота с подключенным токеном NULL_PTR, // Указатель на массив с возвращаемой информацией о разделах &ulVolumesInfoCount);// Размер массива pVolumesInfo = (CK_VOLUME_INFO_EXTENDED*)malloc(ulVolumesInfoCount * sizeof(CK_VOLUME_INFO_EXTENDED)); memset(pVolumesInfo, 0, (ulVolumesInfoCount * sizeof(CK_ULONG))); rv = pFunctionListEx->C_EX_GetVolumesInfo(aSlots[0], // Идентификатор слота с подключенным токеном pVolumesInfo, // Указатель на массив с возвращаемой информацией о разделах &ulVolumesInfoCount); // Размер массива if (rv != CKR_OK) { printf(" -> Failed\n"); } else { printf(" -> OK\n"); for (i = 0; i < (int)ulVolumesInfoCount; i++) { printf("\nPrinting volume %1.2d info:\n", (int)i+1); printf(" Volume id: %2.2d \n", pVolumesInfo[i].idVolume); // Идентификатор раздела printf(" Volume size: %d Mb \n", pVolumesInfo[i].ulVolumeSize); // Объем раздела printf(" Access mode: %2.2d \n", pVolumesInfo[i].accessMode); // Права доступа к разделу printf(" Volume owner: %2.2d \n", pVolumesInfo[i].volumeOwner); // Владелец раздела printf(" Flags: 0x%8.8X \n", pVolumesInfo[i].flags); // Флаги раздела } }
Изменение атрибутов разделов флеш-памяти
Работа с защищенными разделами (с правами доступа только для чтения, скрытым и CD-ROM разделам) сводится к тому, что владелец раздела на время работы с разделом меняет к нему права доступа. Доступно два способа изменения атрибутов раздела: временное и постоянное. Временное изменение меняет права доступа до первого отключения устройства из USB-порта, после чего права доступа сбрасываются на прежние. Постоянное изменение атрибутов доступа действует вплоть до следующего изменения атрибутов.
Для обоих способов изменения атрибутов используется одна функция C_EX_ChangeVolumeAttributes()
, в которую передается идентификатор слота с подключенным токеном, владелец раздела и его PIN-код, а также новые права доступа к разделу и флаг временности/постоянности изменения.
printf("\nChanging volume attributes"); rv = pFunctionListEx->C_EX_ChangeVolumeAttributes(aSlots[0], // Идентификатор слота с подключенным токеном CKU_SO, // Владелец раздела SO_PIN, // PIN-код владельца раздела sizeof(SO_PIN), // Длина PIN-кода владельца раздела VolumeRO, // Идентификатор раздела ACCESS_MODE_RW, // Новые права доступа к разделу CK_TRUE); // CK_TRUE - постоянное изменение атрибутов, CK_FALSE - временное изменение атрибутов if (rv != CKR_OK) printf(" -> Failed\n"); else printf(" -> OK\n");