Page tree

Чтобы начать использовать pki-core, необходимо в настройках проекта добавить путь до каталога с заголовочными файлами библиотеки и включить файл rutoken/pki-core-cpp.h. Приложение должно быть слинковано с динамической библиотекой pki-core.

Первым вызовом pki-core-cpp должен быть rutoken::pkicore::initialize с указанием каталога, в котором лежит библиотека rtPKCS11ECP. После завершения работы с pki-core-cpp необходимо вызвать rutoken::pkicore::deinitialize для освобождения ресурсов библиотеки.

Жизненный цикл объектов

Большая часть классов библиотеки, описывает объекты предметной области, которые нельзя копировать (нельзя на устройстве создать копию сертификата или ключа подписи). Такие классы имеют move-конструкторы, которые можно использовать для перемещения объектов.

Базовая работа с устройствами и сертификатами

Библиотека pki-core-cpp позволяет работать с токенами, доступными через библиотеку rtPKCS11ECP.

Для перечисления подключенных устройств необходимо использовать функцию rutoken::pkicore::Pkcs11Device::enumerate, которая возвращает список объектов класса rutoken::pkicore::Pkcs11Device.

Вызов этой функции делает недействительными:

  • объекты Pkcs11Device, полученные в предыдущем вызове,
  • объекты ключей и сертификатов, хранящихся на устройствах.

Для перечисления и использования ключевых пар на токене требуется предварительно авторизоваться с помощью функции rutoken::pkicore::Pkcs11Device::login.

Чтобы сбросить права доступа на токене, необходимо вызвать функцию rutoken::pkicore::Pkcs11Device::logout.

Большинство операций, связанных с PKI, используют объекты классов rutoken::pkicore::ExternalCert, rutoken::pkicore::Pkcs11Cert, rutoken::pkicore::Pkcs11UserCert.

Объекты класса ExternalCert используются, когда необходимо передать какой-либо сертификат, не хранящийся на токене.

Метод rutoken::pkicore::Pkcs11Device::enumerateCerts возвращает все сертификаты, хранящиеся на устройстве.

Метод rutoken::pkicore::Pkcs11Device::enumerateUserCerts возвращает пользовательские сертификаты на устройстве, которые могут использоваться в операциях, требующих наличия соответствующей ключевой пары.


Пример
using namespace rutoken::pkicore;

initialize(".");

auto devices = Pkcs11Device::enumerate();

// Выбор подходящего устройства.

assert(devices.size() == 1);
auto device = std::move(devices.front());

auto certs = device.enumerateCerts();

device.login("12345678");

auto userCerts = device.enumerateUserCerts();

device.logout();

deinitialize();

Формирование запроса PKCS#10

В общем случае для получения пользовательского сертификата, который можно использовать в операциях подписи, необходимо выполнить следующие действия:

  1. Сгенерировать на устройстве ключевую пару, которая будет использоваться для подписи, с помощью метода rutoken::pkicore::Pkcs11Device::generateKeyPair.
  2. Заполнить необходимые поля PKCS#10 запроса на сертификат, используя класс rutoken::pkicore::Pkcs10RequestInfo.
  3. Сформировать PKCS#10 запрос, вызвав функцию rutoken::pkicore::createPkcs10Request.
  4. Получить сертификат в центре сертификации. Для этого передать запрос с предыдущего шага.
  5. Импортировать полученный сертификат на токен, используя метод rutoken::pkicore::Pkcs11Device::importCert.


Пример
using namespace rutoken::pkicore;

auto key = device.generateKeyPair(Pkcs11Device::Gost34102001KeyGenParams());

X500Dn subject;
subject.setRdn(X500Dn::RdnId::commonName, "Ivanov Ivan Ivanovich");

Pkcs10RequestInfo info;
info.setSubject(subject);

auto request = createPkcs10Request(key, info);

// Вызов request.toPem() вернет PKCS#10 запрос на сертификат в формате PEM, который можно передать в центр сертификации для получения пользовательского сертификата.

device.importCert(ExternalCert(pemCert));

Подпись и проверка подписи в формате CMS

Для создания подписанного CMS-сообщения необходимо использовать функцию rutoken::pkicore::cms::signНа данный момент поддерживается подпись объектов с типом Data Content, который может содержать произвольные данные.

Параметры операции подписи задаются через класс rutoken::pkicore::cms::SignParams.

Метод rutoken::pkicore::cms::SignedData::toBer возвращает сообщение в формате BER.

Статический метод rutoken::pkicore::cms::SignedData::parse позволяет получить объект класса SignedData из сообщения в формате BER.

Метод rutoken::pkicore::cms::SignedData::verify позволяет проверить подпись сообщения. Параметры проверки задаются классом rutoken::pkicore::cms::VerifyParams.

 
Пример
using namespace rutoken::pkicore;

std::vector<unsigned char> data(10, 'a');

// В SignParams необходимо передать пользовательский сертификат, для которого на токене содержится соответствующая ключевая пара.
auto signedData = cms::sign(cms::Data(data.begin(), data.end()), cms::SignParams(cert));

// В VerifyParams необходимо передать токен, на котором будет производиться проверка подписи.
auto result = signedData.verify(cms::VerifyParams(device));

assert(result == cms::VerifyResult::success);

Шифрование и расшифрование в формате CMS

Чтобы создания шифрованного CMS-сообщения используется функция rutoken::pkicore::cms::envelop. На данный момент поддерживается шифрование объектов с типом Data Content, который может содержать произвольные данные.

Параметры операции шифрования задаются через класс rutoken::pkicore::cms::EnvelopParams.

Метод rutoken::pkicore::cms::EnvelopedData::toBer возвращает сообщение в формате BER.

Статический метод rutoken::pkicore::cms::EnvelopedData::parse позволяет получить объект класса EnvelopedData из сообщения в формате BER.

Метод rutoken::pkicore::cms::EnvelopedData::decrypt расшифровывает сообщение . Параметры расшифрования задаются классом rutoken::pkicore::cms::EnvelopedData::DecryptParams.

 
Пример
using namespace rutoken::pkicore;

std::vector<unsigned char> data(10, 'a');

// В EnvelopParams необходимо передать токен, на котором будет производиться шифрование, а также сертификат получателя сообщения.
auto envelopedData = cms::envelop(cms::Data(data.begin(), data.end()), cms::EnvelopParams(device, ExternalCert(pemCert)));

// В DecryptParams необходимо передать пользовательский сертификат, для которого на токене содержится соответствующая ключевая пара.
auto message = envelopedData.decrypt(cms::EnvelopedData::DecryptParams(userCert));

// На данный момент поддерживается только содержимое типа Data Content.
assert(message.contentType() == cms::ContentType::data);

// Преобразуем сообщение к действительному типу.
auto result = cms::Data::cast(std::move(message));
auto resultData = result.data();

assert(resultData.size() == data.size());
assert(std::equal(data.begin(), data.end(), resultData.begin()));
  • No labels