Перейти к содержимому

Работа с библиотекой

USB-токены и смарт-карты JaCartaAPI и SDKUSB-токены и смарт-карты JaCarta

Для использования API подключите к проекту заголовочный файл jcPKCS11.h.

Пример:

#include "jcPKCS11.h"

Прилинкуйте библиотеку к проекту.

Если вы подключаете динамическую библиотеку в Runtime, то в коде подгрузите необходимые функции:

Пример:

/// ...
// Список функций, входящих в стандарт PKCS #11
CK_FUNCTION_LIST_PTR funcs = NULL_PTR;
// Загрузить функцию для получения списка функций, входящих в стандарт PKCS #11
CK_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 #11
rv = 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");
/// ...

Этот шаг следует выполнять, если Вам требуются функции расширения.

Пример:

/// ...
// Список дополнительных функций, не входящих в стандарт PKCS #11
JC_FUNCTION_LIST_PTR funcsExtra = NULL_PTR;
// Загрузить функцию для получения списка дополнительных функций, не входящих в стандарт PKCS #11
FP_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 #11
rv = JC_GetFunctionList(&funcsExtra);
if (rv != CKR_OK)
{
printf("ERROR: JC_GetFunctionList is failed: 0x%08x\n", rv);
return;
}
printf("JC_GetFunctionList - OK\n");
/// ...
// Использование на примере получения версии библиотеки jcPKCS11-2
JC_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");
/// ...

Общий алгоритм использования Единой библиотеки PKCS11

Рассмотрим каждый шаг более подробно:

  1. Проинициализируйте библиотеку.

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

Пример:

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");
/// ...
  1. Определите слот для дальнейшей работы.

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

    2.1. Получите список доступных слотов при помощи функции C_GetSlotList().

Пример:

/// ...
// Массив слотов
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;
}
// Выделить память под массив слотов размером - slotsCount
slots = (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);

2.2. Выберите необходимое приложение.

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

Пример:

/// ...
// Выбираемый слот
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);
}
/// ...
  1. Выполните все необходимые действия с выбранным приложением.

    • Функции стандарта PKCS #11 описаны в Функции, входящие в стандарт PKCS #11.
    • Дополнительные функции, реализованные в библиотеке, но не входящих в стандарт PKCS #11, описаны в разделе Функции, не входящие в стандарт PKCS #11.
    • Некоторые функции предназначены для управления объектами, входящими в стандарт PKCS #11 (см. Поддерживаемые объекты).
    • Большинство функции требуют наличия зашифрованного сеанса. Работа с сеансами описана в разделе Жизненный цикл сеанса.
  2. Завершите работу с библиотекой.

    Для завершения работы вызовите функцию C_Finalize().

Пример:

/// ...
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");

В большинстве случаев работа с приложением происходит через зашифрованный сеанс. Перед открытием зашифрованного сеанса выберите слот, на котором он будет открыт (см. шаг 2 в разделе Общий алгоритм использования).

Жизненный цикл зашифрованного сеанса:

Жизненный цикл зашифрованного сеанса

  1. Откройте зашифрованный сеанс.

Для этого вызовите функцию C_OpenSession() и передайте ей выбранный ранее слот. В случае успешного выполнения, функция вернет идентификатор зашифрованного сеанса.

Пример:

/// ...
// Открываемая сессия
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);
/// ...
  1. Для работы с приложением внутри сеанса используйте идентификатор этого сеанса, полученного на шаге 1.

Пример: Предъявление PIN-кода пользователя и сброс предъявления

/// ...
// 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");
}
}
/// ...
  1. Закройте зашифрованный сеанс.

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

Пример:

/// ...
// Закрыть сессию
rv = C_CloseSession(session);
if(rv != CKR_OK)
{
printf("ERROR: C_CloseSession failed: 0x%08x\n", rv);
}
else
{
printf("C_CloseSession - OK\n");
}
/// ...

Каждый объект, хранящийся на устройстве JaCarta (или в оперативной памяти ОС), имеет атрибуты, которые в совокупности задают свойства объекта. Атрибуты задают тип объекта, его видимость, его местоположение и т. д.

Стоит различать открытые и закрытые объекты (не путать с открытыми и закрытыми ключами):

  • открытые объекты может читать любой пользователь;
  • закрытые объекты защищены PIN-кодом, т.е. любые действия с данным объектом требуют аутентификации пользователя (см. Режим пользователя).

Типы объектов стандарта PKCS #11, поддерживаемые библиотекой jcPKCS11-2 :

jcPKCS11-2

поддерживаются сертификаты открытого ключа только формата X.509.

Типы объектов стандарта PKCS #11, не поддерживаемые библиотекой jcPKCS11-2 :

  • CKO_MECHANISM;
  • CKO_VENDOR_DEFINED;
  • CKO_HW_FEATURE.

Подробнее о работе с объектами и их свойствах указано в разделе Объекты PKCS #11.