Базовый пример

Пример демонстрирует базовое использование библиотеки jcPKCS11-2, описанное в разделе Работа с библиотекой.

Требования к реализации примера:
  • системные:
    1. подключение динамической библиотеки jcPKCS11-2 в Runtime (см. ф-цию main в main.cpp);
    2. поддержка Microsoft Windows (32-/64- бита);
    3. поддержка macOS;
    4. поддержка GNU/Linux (32-/64- бита);
    5. сборка осуществляется с помощью CMake версии не менее 2.8.
  • функциональные (реализованы в main.cpp):
    1. загрузка динамической библиотеки (см. ф-цию main);
    2. выгрузка динамической библиотеки (см. ф-цию main);
    3. загрузка функций, входящих в состав PKCS #11 (см. ф-цию usingJcPKCS11);
    4. загрузка дополнительных функций, не входящих в состав PKCS #11 (см. ф-цию usingJcPKCS11);
    5. получить информацию о библиотеке с помощью функции C_GetInfo(), входящей в состав PKCS #11 (см. ф-цию sample_getLibraryInfo);
    6. получить информацию о библиотеке с помощью дополнительной функции JC_GetVersionInfo(), не входящей в состав PKCS #11 (см. ф-цию sample_getJcPKCS11Version);
    7. для апплета Криптотокен 2 ЭП определить соответствующий ему слот (см. ф-цию sample_getSlotID);
    8. для апплета Криптотокен 2 ЭП: открыть зашифрованный сеанс, предъявить PIN-код пользователя, сбросить предъявление PIN-кода пользователя, закрыть зашифрованный сеанс (см. ф-цию sample_session).

Приложение, реализующее данные требования, носит название jcPKCS11Sample и имеет следующую файловую структуру

../../../_images/base_sample_struct.png
где:
  • src/main.cpp – исходный код приложения, в котором реализованы все функциональные требования;
  • CMakeLists.txt – скрипт, используемый CMake, для генерации соответствующих скриптов сборки под каждую платформу;
  • make.bat и make.sh – скрипты сборки под различные платформы;
  • jcPKCS11/lib – директория, содержащая динамические библиотеки jcPKCS11-2 под различные платформы;
  • jcPKCS11/include – директория, содержащая заголовочные файлы jcPKCS11-2.

Исходный код приложения

main.cpp:

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

#ifdef WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif

#include "jcPKCS11/jcPKCS11.h"

/** Вспомогательные макросы под различные ОС
*/
#ifdef WIN32
  // Windows

  // Тип указателя на библиотеку
  #define LibraryHandle HMODULE

  // Функция загрузки динамической библиотеки
  #define LoadSharedLibrary LoadLibraryA

  // Функция выгрузки динамической библиотеки
  #define FreeSharedLibrary FreeLibrary

  // Функция получения указателя на функцию из библиотеки
  inline void * GetFunction(HMODULE hDll, const char * pFunctionName)
  {
    return GetProcAddress(hDll, pFunctionName);
  }

#else
  // macOS, Linux

  // Тип указателя на библиотеку
  #define LibraryHandle void*

  // Функция загрузки динамической библиотеки
  inline void * LoadSharedLibrary(const char* pFileName)
  {
    return dlopen(pFileName, RTLD_LAZY);
  }

  // Функция выгрузки динамической библиотеки
  #define FreeSharedLibrary dlclose

  // Функция получения указателя на функцию из библиотеки
  #define GetFunction dlsym
#endif


// Объявление функций
void usingJcPKCS11(LibraryHandle libHandle);
void sample_getLibraryInfo(CK_FUNCTION_LIST_PTR funcs);
void sample_getJcPKCS11Version(JC_FUNCTION_LIST_PTR funcsExtra);
CK_BBOOL sample_getSlotID(CK_FUNCTION_LIST_PTR funcs, CK_SLOT_ID* slotID);
void sample_session(CK_SLOT_ID slotID, CK_FUNCTION_LIST_PTR funcs);

/**
*/
int main (int argc, char *argv[])
{
  // Подгрузить динамическую библиотеку
  printf("Trying to load %s ...\n", JC_PKCS_LIB_PATH);

  LibraryHandle libHandle = LoadSharedLibrary(JC_PKCS_LIB_PATH);
  if (libHandle == NULL)
  {
    printf("ERROR: Library hasn't been loaded.\n");
    return 1;
  }
  printf("DONE\n\n");

  
  // Пример использования библиотеки jcPKCS11
  usingJcPKCS11(libHandle);
  printf("\n");

  // Выгрузить динамическую библиотеку
  printf("Trying to free the library ...\n");

  FreeSharedLibrary(libHandle);
  printf("DONE\n\n");

  printf("The end\n");
  return 0;
}


/** Пример использования jcPKCS11-2
*/
void usingJcPKCS11(LibraryHandle libHandle)
{
  CK_RV rv = CKR_OK;


  // 1. Загрузить функции, входящие в стандарт PKCS #11

  // Список функций, входящих в стандарт PKCS #11
  CK_FUNCTION_LIST_PTR funcs = NULL_PTR;

  // 1.1. Загрузить функцию для получения списка функций, входящих в стандарт 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;
  }

  // 1.2. Загрузить функции, входящие в стандарт PKCS #11
  rv = C_GetFunctionList(&funcs);
  if (rv != CKR_OK)
  {
    printf("ERROR: C_GetFunctionList is failed: 0x%08lu\n", rv);
    return;
  }
  printf("C_GetFunctionList - OK\n");


  // 2. Загрузить дополнительные функции, не входящие в стандарт PKCS #11

  // Список дополнительных функций, не входящих в стандарт PKCS #11
  JC_FUNCTION_LIST_PTR funcsExtra = NULL_PTR;

  // 2.1. Загрузить функцию для получения списка дополнительных функций, не входящих в стандарт 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;
  }

  // 2.2. Загрузить дополнительные функции, не входящие в стандарт PKCS #11
  rv = JC_GetFunctionList(&funcsExtra);
  if (rv != CKR_OK)
  {
    printf("ERROR: JC_GetFunctionList is failed: 0x%08lu\n", rv);
    return;
  }
  printf("JC_GetFunctionList - OK\n");

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

  // 4. Примеры использования
  sample_getLibraryInfo(funcs);
  printf("\n");

  sample_getJcPKCS11Version(funcsExtra);
  printf("\n");

  CK_SLOT_ID slotID;
  if (sample_getSlotID(funcs, &slotID) == CK_TRUE)
  {
    printf("\n");
    sample_session(slotID, funcs);
    printf("\n");
  }
  
  // 5. Завершить работу с библиотекой
  rv = funcs->C_Finalize(NULL_PTR);
  if (rv != CKR_OK)
  {
    printf("ERROR: C_Finalize is failed: 0x%08lu\n", rv);
    return;
  }
  printf("C_Finalize - OK\n");
}

/** Получить информацию о библиотеке PKCS #11 
    при помощи функции C_GetInfo, 
    входящей в стандарт PKCS #11.
*/
void sample_getLibraryInfo(CK_FUNCTION_LIST_PTR funcs)
{
  printf("sample_getLibraryInfo:\n");

  CK_INFO libraryInfo;
  CK_RV rv = funcs->C_GetInfo(&libraryInfo);
  if (rv != CKR_OK)
  {
    printf("ERROR: C_GetInfo is failed: 0x%08lu\n", rv);
    return;
  }
  printf("C_GetInfo - OK\n");
  
  printf("PKCS #11 revision : %i.%i\n", 
          (int)libraryInfo.cryptokiVersion.major, 
          (int)libraryInfo.cryptokiVersion.minor);

  printf("Manufacturer      : %.32s\n", 
          libraryInfo.manufacturerID);

  printf("Description       : %.32s\n", 
          libraryInfo.libraryDescription);

  printf("Library version   : %i.%i\n", 
          (int)libraryInfo.libraryVersion.major, 
          (int)libraryInfo.libraryVersion.minor);
}


/** Получить версию библиотеки jcPKCS11-2 
    при помощи функции JC_GetVersionInfo, 
    не входящей в стандарт PKCS #11.
*/
void sample_getJcPKCS11Version(JC_FUNCTION_LIST_PTR funcsExtra)
{
  printf("sample_getJcPKCS11Version:\n");

  JC_VERSION_INFO versionInfo;
  CK_RV rv = funcsExtra->JC_GetVersionInfo(&versionInfo);
  if (rv != CKR_OK)
  {
    printf("ERROR: JC_GetVersionInfo is failed: 0x%08lu\n", rv);
    return;
  }
  printf("JC_GetVersionInfo - OK\n");
  
  printf("jcPKCS11 version : %lu.%lu.%lu.%lu\n", 
          versionInfo.ulMajor, 
          versionInfo.ulMinor, 
          versionInfo.ulRelease, 
          versionInfo.ulBuild);
}


/** Получить идентификатор слота
*/
CK_BBOOL sample_getSlotID(CK_FUNCTION_LIST_PTR funcs, CK_SLOT_ID* slotID)
{
  printf("sample_getSlotID:\n");

  CK_RV rv = CKR_OK;

  // Массив слотов
  CK_SLOT_ID_PTR slots = NULL;

  // Размер массива слотов
  CK_ULONG slotsCount = 0;

  // Получить кол-во слотов с подключенными апплетами 
  // и Антифрод-терминалы без вставленной смарт-карты
  rv = funcs->C_GetSlotList(CK_TRUE, NULL, &slotsCount);
  if(rv != CKR_OK)
  {
    printf("ERROR: C_GetSlotList first call is failed: 0x%08lu\n", rv);
    return CK_FALSE;
  }
  printf("C_GetSlotList first call (slotsCount: %lu) - OK\n", slotsCount);

  // Есть ли слоты?
  if(slotsCount == 0)
  {
    printf("ERROR: JaCarta smartcards haven't been found.\n");
    return CK_FALSE;
  }

  // Выделить память под массив слотов размером - slotsCount
  slots = (CK_SLOT_ID_PTR) malloc(sizeof(CK_SLOT_ID) * slotsCount);
  if(slots == NULL_PTR)
  {
    printf("ERROR: Memory issue.\n");
    return CK_FALSE;
  }

  // Заполнить массив слотов
  rv = funcs->C_GetSlotList(CK_TRUE, slots, &slotsCount);
  if(rv != CKR_OK)
  {
    printf("ERROR: C_GetSlotList second call is failed: 0x%08lu\n", rv);
    free(slots);
    return CK_FALSE;
  }
  printf("C_GetSlotList second call - OK\n");


  // Выбор необходимого апплета

  // Наименование необходимого апплета.
  // В данном случае ищем апплет "Криптотокен 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 = funcs->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;
    }
  }

  // Очистить память
  free(slots);

  if(slotFound == CK_FALSE)
  {
    printf("ERROR: Slot hasn't been found\n");
  }
  else
  {
    printf("Slot has been found: %lu\n", *slotID);
  }

  return slotFound;
}

/** Пример работы с зашифрованным сеансом.
*/
void sample_session(CK_SLOT_ID slotID, CK_FUNCTION_LIST_PTR funcs)
{
  printf("sample_session:\n");

  CK_RV rv = CKR_OK;

  // PIN-код пользователя
  CK_CHAR pin[] = "1234567890";

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

  // Предъявить PIN-код пользователя
  rv = funcs->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%08lu\n", rv);
  }
  else
  {
    printf("C_Login - OK\n");

    // Сбросить предъявление PIN-кода пользователя
    rv = funcs->C_Logout(session);
    if(rv != CKR_OK)
    {
      printf("ERROR: C_Logout is failed: 0x%08lu\n", rv);
    }
    else
    {
      printf("C_Logout - OK\n");
    }
  }

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

Скрипт для CMake

CMakeLists.txt:

cmake_minimum_required(VERSION 2.6)
project(jcPKCS11Sample)

if(MSVC)
  SET(JC_OS "Win")
  if ("${CMAKE_GENERATOR}" MATCHES "(Win64|IA64)")
    SET(JC_OS_BITNESS "64")
  else()
    SET(JC_OS_BITNESS "32")
  endif()
elseif(APPLE)
  SET(JC_OS "Apple")
else()
  SET(JC_OS "Linux")
  if(CMAKE_SIZEOF_VOID_P EQUAL 8)
    SET(JC_OS_BITNESS "64")
  else()
    SET(JC_OS_BITNESS "32")
  endif()
endif()

message(STATUS "JC_OS: ${JC_OS}")
message(STATUS "JC_OS_BITNESS: ${JC_OS_BITNESS}")

get_filename_component(JC_PROJECT_DIR "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE)

if(${JC_OS} MATCHES "Win")
  set(JC_FLAGS "/wd4514 /wd4710 /wd4820 /wd4350 /wd4668 /wd4711 /wd4365 /wd4100")
  set(JC_DEFINITIONS "-DCK_Win32 -D_CRT_SECURE_NO_WARNINGS")

  if(${JC_OS_BITNESS} MATCHES "64")
    set(JC_PKCS_LIB_PATH "${JC_PROJECT_DIR}/jcPKCS11/lib/Win64/jcPKCS11-2.dll")
    set(JC_BIN_DIR "${JC_PROJECT_DIR}/_bin/Win/x64")
  else()
    set(JC_PKCS_LIB_PATH "${JC_PROJECT_DIR}/jcPKCS11/lib/Win32/jcPKCS11-2.dll")
    set(JC_BIN_DIR "${JC_PROJECT_DIR}/_bin/Win/x86")
  endif()

elseif("${JC_OS}" MATCHES "Apple")
  set(JC_FLAGS "-Wno-write-strings -fpermissive")

  set(JC_PKCS_LIB_PATH "${JC_PROJECT_DIR}/jcPKCS11/lib/macOS/jcPKCS11-2")
  set(JC_BIN_DIR "${JC_PROJECT_DIR}/_bin/macOS")

elseif("${JC_OS}" MATCHES "Linux")
  set(JC_FLAGS "-Wno-write-strings -fpermissive")

  if(${JC_OS_BITNESS} MATCHES "64")
    set(JC_PKCS_LIB_PATH "${JC_PROJECT_DIR}/jcPKCS11/lib/linux-x86_64/libjcPKCS11-2.so")
    set(JC_BIN_DIR "${JC_PROJECT_DIR}/_bin/linux/x86_64")
  else()
    set(JC_PKCS_LIB_PATH "${JC_PROJECT_DIR}/jcPKCS11/lib/linux-i386/libjcPKCS11-2.so")
    set(JC_BIN_DIR "${JC_PROJECT_DIR}/_bin/linux/i386")
  endif()

else()
  message(FATAL_ERROR "Unknown operation system")
endif()

message(STATUS "JC_PKCS_LIB_PATH: ${JC_PKCS_LIB_PATH}")
message(STATUS "JC_BIN_DIR: ${JC_BIN_DIR}")
message(STATUS "JC_FLAGS: ${JC_FLAGS}")
message(STATUS "JC_DEFINITIONS: ${JC_DEFINITIONS}")


add_definitions("-Wall")
add_definitions("-DJC_PKCS_LIB_PATH=\"${JC_PKCS_LIB_PATH}\"")
add_definitions("${JC_FLAGS}")
add_definitions("${JC_DEFINITIONS}")

include_directories("${JC_PROJECT_DIR}/jcPKCS11/include")

set(SOURCES src/main.cpp)

add_executable(jcPKCS11Sample ${SOURCES})

if(${JC_OS} MATCHES "Win")
  target_link_libraries(jcPKCS11Sample "Ws2_32.lib")
elseif(${JC_OS} MATCHES "Linux")
  target_link_libraries(jcPKCS11Sample dl)
endif()

set_target_properties(jcPKCS11Sample PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${JC_BIN_DIR}")

Скрипты сборки

Все скрипты сборки реализуют следующий алгоритм:
  1. в корне проекта создают каталог _out;
  2. в каталоге _out запускают CMake для генерации необходимых скриптов под текущую платформу;
  3. запускают CMake для сборки проекта. Результаты сборки находятся в каталоге _bin в корне проекта.

Microsoft Windows

make.bat:

@echo off

if not exist ./_out md _out

cd _out

cmake ..
if %ERRORLEVEL% GTR 0 exit /b %ERRORLEVEL%

cmake --build . --config Debug
if %ERRORLEVEL% GTR 0 exit /b %ERRORLEVEL%

cd ..

macOS и GNU/Linux

make.sh:

#!/bin/bash

if [ ! -d _out ]; then
  mkdir _out
fi

cd _out

cmake -G "Unix Makefiles" .. || exit 1
cmake --build . --config Debug || exit 1

cd ..