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

Подключение к проекту

Заголовочный файл

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

Пример:

#include "jcPKCS11.h"

Линковка

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

Если вы подключаете динамическую библиотеку в Runtime, то в коде подгрузите необходимые функции (более подробно см. в Базовый пример):
  • вызовите функцию C_GetFunctionList() для получения функций стандарта PKCS #11, реализованных в библиотеке. Все они описаны в разделе Функции, входящие в стандарт PKCS #11;

    Пример:

    /// ...
    
    // Список функций, входящих в стандарт 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");
    
    /// ...
    
  • вызовите функцию JC_GetFunctionList() для получения дополнительных функций, реализованных в библиотеке, но не входящих в стандарт PKCS #11. Все они описаны в разделе Функции, не входящие в стандарт PKCS #11.

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

    Пример:

    /// ...
    
    // Список дополнительных функций, не входящих в стандарт 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");
    
    /// ...
    

Общий алгоритм использования

../../../_images/jcpkcs11_base_alg.png

Совет

В разделе Базовый пример представлена полная реализация алгоритма с использованием динамической библиотеки jcPKCS11-2.

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

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

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

    Важно

    Нельзя вызывать C_Initialize() несколько раз подряд без предварительного завершения работы с ней (см. шаг 4 данного раздела).

    Пример:

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

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

    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 smartcards haven't been 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 second call is failed: 0x%08x\n", rv);
      free(slots);
      return;
    }
    printf("C_GetSlotList second call - OK\n");
    
    /// ...
    
    /// После заврешения работы с slots не забудьте очистить память:
    /// free(slots);
    

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

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

    Пример:

    /// ...
    
    // Выбираемый слот
    CK_SLOT_ID slotID;
    
    // Наименование необходимого апплета.
    // В данном случае ищем апплет "Криптотокен 2 ЭП".
    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);
    }
    
    /// ...
    
  3. Выполните все необходимые действия над апплетом.
  4. Завершите работу с библиотекой.

    Для завершения работы вызовите функцию 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 в разделе Общий алгоритм использования).

Совет

В разделе Базовый пример представлена полная реализация алгоритма с использованием динамической библиотеки jcPKCS11-2.

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

../../../_images/jcpkcs11_session.png
  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);
    
    /// ...
    
  2. Для работы с апплетом внутри сеанса используйте идентификатор этого сеанса, полученного на шаге 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 is failed: 0x%08x\n", rv);
    }
    else
    {
      printf("C_Login - OK\n");
    
      // Сбросить предъявление PIN-кода пользователя
      rv = C_Logout(session);
      if(rv != CKR_OK)
      {
        printf("ERROR: C_Logout is failed: 0x%08x\n", rv);
      }
      else
      {
        printf("C_Logout - OK\n");
      }
    }
    
    /// ...
    
  3. Закройте зашифрованный сеанс.
    • Для закрытия конкретного сеанса вызовите C_CloseSession() и передайте ей идентификатор сеанса, полученного на шаге 1.
    • Для закрытия всех сеансов на выбранном слоте вызовите функцию C_CloseAllSessions().

    Пример:

    /// ...
    
    // Закрыть сессию
    rv = C_CloseSession(session);
    if(rv != CKR_OK)
    {
      printf("ERROR: C_CloseSession is 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.