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

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

Пример:

#include "jcPKCS11.h"

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

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

Пример:

<p><br /></p><pre>/// ...
 
// Список функций, входящих в стандарт PKCS #11
CK_FUNCTION_LIST_PTR funcs = NULL_PTR;
 
// Загрузить функцию для получения списка функций, входящих в стандарт PKCS #11
CK_C_GetFunctionList C_GetFunctionList = (CK_C_GetFunctionList) GetFunction(libHandle, &quot;C_GetFunctionList&quot;);
if (C_GetFunctionList == NULL_PTR)
{
printf(&quot;ERROR: C_GetFunctionList hasn&apos;t been found in module\n&quot;);
return;
}
 
// Загрузить функции, входящие в стандарт PKCS #11
rv = C_GetFunctionList(&amp;funcs);
if (rv != CKR_OK)
{
printf(&quot;ERROR: C_GetFunctionList is failed: 0x%08x\n&quot;, rv);
return;
}
printf(&quot;C_GetFunctionList - OK\n&quot;);
 
/// ...
 
// Использование на примере инициализации библиотеки
rv = funcs-&gt;C_Initialize(NULL_PTR);
if (rv != CKR_OK)
{
printf(&quot;ERROR: C_Initialize is failed: 0x%08x\n&quot;, rv);
return;
}
printf(&quot;C_Initialize - OK\n&quot;);
 
/// ...</pre>

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

Пример:

<pre>/// ...
 
// Список дополнительных функций, не входящих в стандарт PKCS #11
JC_FUNCTION_LIST_PTR funcsExtra = NULL_PTR;
 
// Загрузить функцию для получения списка дополнительных функций, не входящих в стандарт PKCS #11
FP_JC_GetFunctionList JC_GetFunctionList = (FP_JC_GetFunctionList) GetFunction(libHandle, &quot;JC_GetFunctionList&quot;);
if (JC_GetFunctionList == NULL_PTR)
{
printf(&quot;ERROR: C_GetFunctionList hasn&apos;t been found in module\n&quot;);
return;
}
 
// Загрузить дополнительные функции, не входящие в стандарт PKCS #11
rv = JC_GetFunctionList(&amp;funcsExtra);
if (rv != CKR_OK)
{
printf(&quot;ERROR: JC_GetFunctionList is failed: 0x%08x\n&quot;, rv);
return;
}
printf(&quot;JC_GetFunctionList - OK\n&quot;);
 
/// ...
 
// Использование на примере получения версии библиотеки jcPKCS11-2
JC_VERSION_INFO versionInfo;
CK_RV rv = funcsExtra-&gt;JC_GetVersionInfo(&amp;versionInfo);
if (rv != CKR_OK)
{
printf(&quot;ERROR: JC_GetVersionInfo is failed: 0x%08x\n&quot;, rv);
return;
}
printf(&quot;JC_GetVersionInfo - OK\n&quot;);
 
/// ...</pre>

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

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

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

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

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

Пример:

<pre>CK_RV rv = CKR_OK;
 
// Проинициализировать библиотеку
rv = C_Initialize(NULL_PTR);
if (rv != CKR_OK)
{
printf(&quot;ERROR: C_Initialize is failed: 0x%08x\n&quot;, rv);
return;
}
printf(&quot;C_Initialize - OK\n&quot;);
 
/// ...</pre>

  2. Определите слот для дальнейшей работы.

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

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

Пример:

<pre>/// ...
 
// Массив слотов
CK_SLOT_ID_PTR slots = NULL;
 
// Размер массива слотов
CK_ULONG slotsCount = 0;
 
// Получите список доступных слотов
// Получить кол-во слотов с подключенными приложениями
rv = C_GetSlotList(CK_TRUE, NULL, &amp;slotsCount);
if(rv != CKR_OK)
{
printf(&quot;ERROR: C_GetSlotList first call is failed: 0x%08x\n&quot;, rv);
return;
}
printf(&quot;C_GetSlotList first call (slotsCount: %d) - OK\n&quot;, slotsCount);
 
// Есть ли слоты?
if(slotsCount == 0)
{
printf(&quot;ERROR: JaCarta not found.\n&quot;);
return;
}
 
// Выделить память под массив слотов размером - slotsCount
slots = (CK_SLOT_ID_PTR) malloc(sizeof(CK_SLOT_ID) * slotsCount);
if(slots == NULL_PTR)
{
printf(&quot;ERROR: Memory issue.\n&quot;);
return;
}
 
// Заполнить массив слотов
rv = C_GetSlotList(CK_TRUE, slots, &amp;slotsCount);
if(rv != CKR_OK)
{
printf(&quot;ERROR: C_GetSlotList failed: 0x%08x\n&quot;, rv);
free(slots);
return;
}
printf(&quot;C_GetSlotList - OK\n&quot;);
 
/// ...
 
/// После завершения работы с slots не забудьте очистить память:
/// free(slots);</pre>

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

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

Пример:

<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 &lt; slotsCount; ++i)
{
CK_SLOT_ID tmpSlotID = slots[i];
CK_TOKEN_INFO tokenInfo = {};
 
// Получить информацию о токене в слоте
rv = C_GetTokenInfo(tmpSlotID, &amp;tokenInfo);
if(rv != CKR_OK)
{
continue;
}
printf(&quot;C_GetTokenInfo (slotID: %lu) - OK\n&quot;, 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(&quot;ERROR: Slot hasn&apos;t been found\n&quot;);
}
else
{
printf(&quot;Slot has been found: %d\n&quot;, slotID);
}
 
/// ...</pre>

  3. Выполните все необходимые действия с выбранным приложением.

  4. Завершите работу с библиотекой.

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

Пример:

<pre>/// ...
 
rv = C_Finalize(NULL_PTR);
if (rv != CKR_OK)
{
printf(&quot;ERROR: C_Finalize is failed: 0x%08x\n&quot;, rv);
return;
}
printf(&quot;C_Finalize - OK\n&quot;);</pre>

2.1.3 Жизненный цикл сеанса

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

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

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

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

Пример:

<pre>/// ...
 
// Открываемая сессия
CK_SESSION_HANDLE session = CK_INVALID_HANDLE;
 
// Откройте зашифрованный сеанс
rv = C_OpenSession(slotID, (CKF_SERIAL_SESSION | CKF_RW_SESSION), NULL_PTR, NULL_PTR, &amp;session);
if(rv != CKR_OK)
{
printf(&quot;ERROR: C_OpenSession (slotID: %d) is failed: 0x%08x\n&quot;, slotID, rv);
return;
}
printf(&quot;C_OpenSession (slotID: %d) - OK\n&quot;, slotID);
printf(&quot;session : %d\n&quot;, session);
 
/// ...</pre>

  2. Для работы с приложением внутри сеанса используйте идентификатор этого сеанса, полученного на шаге 1.

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

<pre>/// ...
 
// PIN-код пользователя
CK_CHAR pin[] = &quot;1234567890&quot;;
 
// Предъявить PIN-код пользователя
rv = C_Login(session, CKU_USER, (CK_CHAR_PTR)pin, (CK_ULONG)strlen((const char*)pin));
if (rv != CKR_OK)
{
printf(&quot;ERROR: C_Login failed: 0x%08x\n&quot;, rv);
}
else
{
printf(&quot;C_Login - OK\n&quot;);
 
// Сбросить предъявление PIN-кода пользователя
rv = C_Logout(session);
if(rv != CKR_OK)
{
printf(&quot;ERROR: C_Logout failed: 0x%08x\n&quot;, rv);
}
else
{
printf(&quot;C_Logout - OK\n&quot;);
}
}
 
/// ...</pre>

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

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

Пример:

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

Один слот может иметь несколько одновременно открытых сеансов.

2.1.4 Поддерживаемые объекты

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

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

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

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

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

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

  • CKO_MECHANISM;
  • CKO_VENDOR_DEFINED;
  • CKO_HW_FEATURE.

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