2.1.1 Подключение к проекту
Для использования API подключите к проекту заголовочный файл jcPKCS11.h.
Пример:
#include "jcPKCS11.h"Прилинкуйте библиотеку к проекту.
Если вы подключаете динамическую библиотеку в Runtime, то в коде подгрузите необходимые функции:
- вызовите функцию C_GetFunctionList() для получения функций стандарта PKCS #11, реализованных в библиотеке. Все они описаны в разделе Функции, входящие в стандарт PKCS #11.
Пример:
<p><br /></p><pre>/// ...// Список функций, входящих в стандарт PKCS #11CK_FUNCTION_LIST_PTR funcs = NULL_PTR;// Загрузить функцию для получения списка функций, входящих в стандарт PKCS #11CK_C_GetFunctionList C_GetFunctionList = (CK_C_GetFunctionList) GetFunction(libHandle, "C_GetFunctionList");if (C_GetFunctionList == NULL_PTR){ printf("ERROR: C_GetFunctionList hasn't been found in module\n"); return;}// Загрузить функции, входящие в стандарт PKCS #11rv = C_GetFunctionList(&funcs);if (rv != CKR_OK){ printf("ERROR: C_GetFunctionList is failed: 0x%08x\n", rv); return;}printf("C_GetFunctionList - OK\n");/// ...// Использование на примере инициализации библиотекиrv = funcs->C_Initialize(NULL_PTR);if (rv != CKR_OK){ printf("ERROR: C_Initialize is failed: 0x%08x\n", rv); return;}printf("C_Initialize - OK\n");/// ...</pre>- вызовите функцию JC_GetFunctionList() для получения дополнительных функций, реализованных в библиотеке, но не входящих в стандарт PKCS #11. Все они описаны в разделе Функции, не входящие в стандарт PKCS #11.
Этот шаг следует выполнять, если Вам требуются функции расширения.
Пример:
<pre>/// ...// Список дополнительных функций, не входящих в стандарт PKCS #11JC_FUNCTION_LIST_PTR funcsExtra = NULL_PTR;// Загрузить функцию для получения списка дополнительных функций, не входящих в стандарт PKCS #11FP_JC_GetFunctionList JC_GetFunctionList = (FP_JC_GetFunctionList) GetFunction(libHandle, "JC_GetFunctionList");if (JC_GetFunctionList == NULL_PTR){ printf("ERROR: C_GetFunctionList hasn't been found in module\n"); return;}// Загрузить дополнительные функции, не входящие в стандарт PKCS #11rv = JC_GetFunctionList(&funcsExtra);if (rv != CKR_OK){ printf("ERROR: JC_GetFunctionList is failed: 0x%08x\n", rv); return;}printf("JC_GetFunctionList - OK\n");/// ...// Использование на примере получения версии библиотеки jcPKCS11-2JC_VERSION_INFO versionInfo;CK_RV rv = funcsExtra->JC_GetVersionInfo(&versionInfo);if (rv != CKR_OK){ printf("ERROR: JC_GetVersionInfo is failed: 0x%08x\n", rv); return;}printf("JC_GetVersionInfo - OK\n");/// ...</pre>2.1.2 Общий алгоритм использования
Рассмотрим каждый шаг более подробно:
- Проинициализируйте библиотеку.
Перед началом работы необходимо проинициализировать библиотеку при помощи функции C_Initialize(). Ее всегда необходимо вызывать перед вызовом остальных функций.
Важно: Нельзя вызывать C_Initialize() несколько раз подряд без предварительного завершения работы с ней (см. шаг 4 данного раздела).
Пример:
<pre>CK_RV rv = CKR_OK;// Проинициализировать библиотекуrv = C_Initialize(NULL_PTR);if (rv != CKR_OK){ printf("ERROR: C_Initialize is failed: 0x%08x\n", rv); return;}printf("C_Initialize - OK\n");/// ...</pre>2. Определите слот для дальнейшей работы.
После инициализации библиотеки необходимо определить слот приложения (см. Работа с приложениями), с которым в дальнейшем будете работать.
2.1. Получите список доступных слотов при помощи функции C_GetSlotList().
Пример:
<pre>/// ...// Массив слотовCK_SLOT_ID_PTR slots = NULL;// Размер массива слотовCK_ULONG slotsCount = 0;// Получите список доступных слотов// Получить кол-во слотов с подключенными приложениямиrv = C_GetSlotList(CK_TRUE, NULL, &slotsCount);if(rv != CKR_OK){ printf("ERROR: C_GetSlotList first call is failed: 0x%08x\n", rv); return;}printf("C_GetSlotList first call (slotsCount: %d) - OK\n", slotsCount);// Есть ли слоты?if(slotsCount == 0){ printf("ERROR: JaCarta not found.\n"); return;}// Выделить память под массив слотов размером - slotsCountslots = (CK_SLOT_ID_PTR) malloc(sizeof(CK_SLOT_ID) * slotsCount);if(slots == NULL_PTR){ printf("ERROR: Memory issue.\n"); return;}// Заполнить массив слотовrv = C_GetSlotList(CK_TRUE, slots, &slotsCount);if(rv != CKR_OK){ printf("ERROR: C_GetSlotList failed: 0x%08x\n", rv); free(slots); return;}printf("C_GetSlotList - OK\n");/// .../// После завершения работы с slots не забудьте очистить память:/// free(slots);</pre>2.2. Выберите необходимое приложение.
Для выбора приложения необходимо для каждого слота вызвать функцию C_GetTokenInfo() и в полученной информации о приложении сравнить название модели (аргумент CK_TOKEN_INFO.model) с интересующим названием приложения:
- JC_MODEL_CRYPTOTOKEN_2 – для приложения GOST;
- JC_MODEL_LASER – для приложения PKI;
- JC_MODEL_DATASTORE – для приложения LT;
- JC_MODEL_BIOMANAGER - для приложения Biomanager;
Пример:
<pre>/// ...// Выбираемый слотCK_SLOT_ID slotID;// Наименование необходимого приложения.// В данном случае ищем приложение GOST.const char* modelName = JC_MODEL_CRYPTOTOKEN_2;// Длина наименования необходимого апплетаsize_t modelLength = strlen(modelName);CK_BBOOL slotFound = CK_FALSE;for (CK_ULONG i = 0; i < slotsCount; ++i){ CK_SLOT_ID tmpSlotID = slots[i]; CK_TOKEN_INFO tokenInfo = {}; // Получить информацию о токене в слоте rv = C_GetTokenInfo(tmpSlotID, &tokenInfo); if(rv != CKR_OK) { continue; } printf("C_GetTokenInfo (slotID: %lu) - OK\n", tmpSlotID); // Поиск модели приложения, совпадающей с modelName int res = !memcmp((const char*)tokenInfo.model, modelName, modelLength); if (res == 0) { slotID = tmpSlotID; slotFound = CK_TRUE; break; }}/// Если slots больше не нужен, то очистите память:/// free(slots);if(slotFound == CK_FALSE){ printf("ERROR: Slot hasn't been found\n");}else{ printf("Slot has been found: %d\n", slotID);}/// ...</pre>3. Выполните все необходимые действия с выбранным приложением.
- Функции стандарта PKCS #11 описаны в Функции, входящие в стандарт PKCS #11.
- Дополнительные функции, реализованные в библиотеке, но не входящих в стандарт PKCS #11, описаны в разделе Функции, не входящие в стандарт PKCS #11.
- Некоторые функции предназначены для управления объектами, входящими в стандарт PKCS #11 (см. Поддерживаемые объекты).
- Большинство функции требуют наличия зашифрованного сеанса. Работа с сеансами описана в разделе Жизненный цикл сеанса.
4. Завершите работу с библиотекой.
Для завершения работы вызовите функцию C_Finalize().
Пример:
<pre>/// ...rv = C_Finalize(NULL_PTR);if (rv != CKR_OK){ printf("ERROR: C_Finalize is failed: 0x%08x\n", rv); return;}printf("C_Finalize - OK\n");</pre>2.1.3 Жизненный цикл сеанса
В большинстве случаев работа с приложением происходит через зашифрованный сеанс. Перед открытием зашифрованного сеанса выберите слот, на котором он будет открыт (см. шаг 2 в разделе Общий алгоритм использования).
Жизненный цикл зашифрованного сеанса:
- Откройте зашифрованный сеанс.
Для этого вызовите функцию C_OpenSession() и передайте ей выбранный ранее слот. В случае успешного выполнения, функция вернет идентификатор зашифрованного сеанса.
Пример:
<pre>/// ...// Открываемая сессияCK_SESSION_HANDLE session = CK_INVALID_HANDLE;// Откройте зашифрованный сеансrv = C_OpenSession(slotID, (CKF_SERIAL_SESSION | CKF_RW_SESSION), NULL_PTR, NULL_PTR, &session);if(rv != CKR_OK){ printf("ERROR: C_OpenSession (slotID: %d) is failed: 0x%08x\n", slotID, rv); return;}printf("C_OpenSession (slotID: %d) - OK\n", slotID);printf("session : %d\n", session);/// ...</pre>2. Для работы с приложением внутри сеанса используйте идентификатор этого сеанса, полученного на шаге 1.
Пример: Предъявление PIN-кода пользователя и сброс предъявления
<pre>/// ...// PIN-код пользователяCK_CHAR pin[] = "1234567890";// Предъявить PIN-код пользователяrv = C_Login(session, CKU_USER, (CK_CHAR_PTR)pin, (CK_ULONG)strlen((const char*)pin));if (rv != CKR_OK){ printf("ERROR: C_Login failed: 0x%08x\n", rv);}else{ printf("C_Login - OK\n"); // Сбросить предъявление PIN-кода пользователя rv = C_Logout(session); if(rv != CKR_OK) { printf("ERROR: C_Logout failed: 0x%08x\n", rv); } else { printf("C_Logout - OK\n"); }}/// ...</pre>3. Закройте зашифрованный сеанс.
- Для закрытия конкретного сеанса вызовите C_CloseSession() и передайте ей идентификатор сеанса, полученного на шаге 1.
- Для закрытия всех сеансов на выбранном слоте вызовите функцию C_CloseAllSessions().
Пример:
<pre>/// ...// Закрыть сессиюrv = C_CloseSession(session);if(rv != CKR_OK){ printf("ERROR: C_CloseSession failed: 0x%08x\n", rv);}else{ printf("C_CloseSession - OK\n");}/// ...</pre>Один слот может иметь несколько одновременно открытых сеансов.
2.1.4 Поддерживаемые объекты
Каждый объект, хранящийся на устройстве JaCarta (или в оперативной памяти ОС), имеет атрибуты, которые в совокупности задают свойства объекта. Атрибуты задают тип объекта, его видимость, его местоположение и т. д.
Стоит различать открытые и закрытые объекты (не путать с открытыми и закрытыми ключами):
- открытые объекты может читать любой пользователь;
- закрытые объекты защищены PIN-кодом, т.е. любые действия с данным объектом требуют аутентификации пользователя (см. Режим пользователя).
Типы объектов стандарта PKCS #11, поддерживаемые библиотекой jcPKCS11-2 :
- объекты данных (CKO_DATA);
- объекты сертификатов (CKO_CERTIFICATE);
- закрытые ключи (CKO_PRIVATE_KEY);
- открытые ключи (CKO_PUBLIC_KEY);
- секретные ключи (CKO_SECRET_KEY).
Типы объектов стандарта PKCS #11, не поддерживаемые библиотекой jcPKCS11-2 :
CKO_MECHANISM;CKO_VENDOR_DEFINED;CKO_HW_FEATURE.
Подробнее о работе с объектами и их свойствах указано в разделе Объекты PKCS #11.

