Бібліотека для вбудовування електронного підпису в додатку С++



Наша компанія продовжує розвивати лінійку бібліотек, які дозволяють вбудовувати електронний підпис з використанням російських криптоалгоритмов в інформаційні системи різного типу.

Деякий час тому ми підтримали Рутокен ЕЦП в openssl, потім випустили багатоплатформовий плагін для браузера, а тепер зробили високорівневу криптобиблиотеку для вбудовування в С++ програми.

Концептуально ці рішення виконані ідентично: використовується апаратна реалізація російських криптоалгоритмов на чіпі Рутокен ЕЦП, забезпечується підтримка цифрових сертифікатів X. 509, запитів на сертифікати PKCS#10, підписаних і зашифрованих повідомлень CMS.

Нова бібліотека стане в нагоді тим, хто пише «товсті клієнти», десктопні програми, свої браузерні плагіни і т. п.

Підтримувані пристрої:
  • USB-токен Рутокен ЕЦП
  • Смарт-карта Рутокен ЕЦП
  • Bluetooth-токен Рутокен ЕЦП
  • Trustscreen-пристрій Рутокен PINPad
  • USB-токен Рутокен WEB (HID)


Основні сценарії застосування бібліотеки з прикладами коду під катом.


Базова робота з пристроями

Бібліотека надає інтерфейс, виконаний у вигляді класу CryptoCore:

class CryptoCore 
{
public: 
CryptoCore(const std::string& pkcs11path); 

~CryptoCore(); 
...
}


Конструктор класу вимагає передачі шляху до бібліотеки PKCS#11.

Будь користувальницький сценарій починається з пошуку підключених до комп'ютера пристроїв Рутокен. Для цієї мети використовується метод:

std::vector<unsigned long> enumerateDevices();

Даний метод повертає вектор ідентифікаторів слотів, які підключені пристрої Рутокен.

Метод має такі особливості:
  • при наступному виклику не гарантується, що ідентифікатори слотів з підключеними пристроями ідентичні ідентифікаторів, отриманим у попередньому виклику;
  • виклик методу «знищує» внутрішній кеш бібліотеки, тобто не гарантується, що раніше отримані хэндлы ключових пар і сертифікатів є дійсними.


Після отримання списку пристроїв необхідно отримати інформацію про кожному конкретному пристрої. Для цієї мети передбачено метод:

DeviceInfo getDeviceInfo(unsigned long deviceId);


struct DeviceInfo
{
// користувальницьке найменування пристрою
std::string label;

// серійний номер пристрою (збігається з надрукованим на пристрої)
std::string serial;

// модель пристрою
std::string model; 

// тип пристрою
unsigned int type; 

// була зроблена авторизація на пристрій 
bool isLoggedIn; 

// знаходиться PIN-код до даного пристрою в постійному кеші бібліотеки
bool isPinCached; 
...
};

Можливі типи пристроїв Рутокен задані наступним чином:

class CryptoCore 
{ 
...
public: 
enum DeviceType { UNKNOWN, RUTOKEN_ECP, RUTOKEN_WEB, RUTOKEN_PINPAD_IN, KAZTOKEN, RUTOKEN_PINPAD_2 };
...
}


Приклад базової роботи з пристроями:

std::string pkcs11path = "./";

std::auto_ptr<CryptoCore> cp(new CryptoCore(pkcs11path));
std::vector<unsigned long> devices = cp->enumerateDevices();
std::cout <<"Found " << devices.size() << " devices" << std::endl;

for (std::vector<unsigned long>::const_iterator it = devices.begin(); it != devices.end(); it++)
{
unsigned int id = *it;
DeviceInfo info = cp->getDeviceInfo(id);
std::cout << "Device ID: " << id << std::endl;
std::cout << "\tLabel: " << info.label << std::endl;
std::cout << "\tSerial: " << info.serial << std::endl;
std::cout << "\tModel: " << info.model << std::endl;
std::cout << "\tType: "; 

switch (info.type)
{ 
case 0: 
std::cout << "Unknown"; break; 
case 1: 
std::cout << "Rutoken ECP"; break; 
case 2: 
std::cout << "Rutoken WEB"; break; 
case 3: 
std::cout << "Rutoken PINPAD IN"; break; 
case 4: 
std::cout << "KAZTOKEN"; break; 
case 5: 
std::cout << "Rutoken PINPAD2"; break;
}

std::cout << std::endl;
}

Слід зазначити, що USB-токен Рутокен ЕЦП і смарт-карта Рутокен ЕЦП мають тип RUTOKEN_ECP. Для більш точної ідентифікації слід уточнити модель пристрою. У випадку смарт-карти буде повернута рядок «Rutoken ECP SC».

Тип RUTOKEN_PINPAD_2 відповідає серійному пристрою Рутокен PINPad. Тип RUTOKEN_PINPAD_IN є застарілим і застосовується тільки для зворотної сумісності.

Авторизація на пристрої
Для авторизації на пристрої потрібно буде ввести PIN-коду.

Для цього призначений метод:
void login(unsigned long deviceId, const std::string& pin);


Для розлогінування пристрою, відповідно:
void logout(unsigned long deviceId);


Для отримання інформації про те, чи було зроблено логін на пристрій, слід використовувати метод:
DeviceInfo getDeviceInfo(unsigned long deviceId);


з опцією isLoggedIn;

Типи об'єктів, можливі операції з об'єктами

Бібліотека підтримує роботу з ключовими парами ГОСТ Р 34.10-2001 і цифровими сертифікатами відкритого ключа ГОСТ Р 34.10-2001 у форматі X. 509.

Для певних операцій з даними об'єктами потрібна авторизація на пристрої, для інших — ні. З допомогою бібліотеки можливі наступні операції з об'єктами:
  1. отримати список сертифікатів, що зберігаються на пристрої
  2. записати сертифікат на пристрій
  3. прочитати сертифікат з пристрою
  4. видалити сертифікат з пристрою
  5. отримати список ключових пар, що зберігаються на пристрої
  6. створити ключову пару на пристрої
  7. видалити ключову пару з пристрою


Операції 1 і 3 не вимагають авторизації на пристрої, операції 2, 4, 5, 6, 7 вимагають.

Робота з ключовими парами
Для отримання списку, наявних на токені ключових пар, використовується метод:
std::vector<std::string> enumerateKeys( unsigned long deviceId, const std::string& marker);


Повертаються хэндлы ключових пар є унікальними і постійними для цієї ключової пари.

Приклад отримання ключових пар, що зберігаються на пристрої:
cp->login(id "12345678");

std::vector<std::string> keys = cp->enumerateKeys(id "Test marker"); 

if (keys.empty())
{
std::cerr << "No keys were found on token" << std::endl;
} 
else
{
std::cerr << "Found " << keys.size() << " key(s) on token" << std::endl; 
for (size_t i = 0; i < keys.size(); i++)
{
std::string kId = keys[i];
std::cerr << "Key with id: " << kId << " on token with label: " << cp->getKeyLabel(id, kId) << std::endl;
}
}


Створити ключову пару на пристрої можна з допомогою функції:
std::string generateKeyPair( unsigned long deviceId, const std::string& params, const std::string& marker, const std::map<std::string, bool>& options);


Params задає параметри відповідно до RFC 4357, які будуть використовуватися для створеного ключа.

Можливі наступні варіанти:
  • A: id-GostR3410-2001-CryptoPro-A-ParamSet
  • «B»: id-GostR3410-2001-CryptoPro-B-ParamSet
  • «C»: id-GostR3410-2001-CryptoPro-C-ParamSet
  • «XA»: id-GostR3410-2001-CryptoPro-XchA-ParamSet
  • «XB»: id-GostR3410-2001-CryptoPro-XchB-ParamSet


Приклад генерації ключової пари на пристрої:
cp->login(id "12345678");

std::map<std::string, bool> keyOptions;
keyOptions["needPin"] = false;

std::string keyId = cp->generateKeyPair(id "A", "Test marker", keyOptions);

std::string keyLabel;
std::cerr << "Please, enter new key label: ";
std::cin >> keyLabel;cp->setKeyLabel(id, keyId, keyLabel);


Для видалення ключової пари використовується метод:
void deleteKeyPair(unsigned long deviceId, const std::string& keyId);


Робота з сертифікатами

Сертифікат видається УЦ за запитом PKCS#10 і може бути записана на пристрій.

Для запису (імпорту) сертифіката на пристрій застосовується метод:
std::string importCertificate(unsigned long deviceId, const std::string& certificate, unsigned long category);

Параметр category визначає атрибут, з яким сертифікат буде збережений на пристрій:
  • інтерфейс (PKCS11_CERT_CATEGORY_USER), це сертифікат, пов'язаний з закритим ключем користувача.
    Застосовуються, наприклад, для підпису CMS/PKCS#7, аутентифікації користувача в протоколі TLS. Якщо сертифікат імпортується як користувальницький, то при імпорті буде проведений пошук в пристрої відповідного йому закритого ключа. Якщо такий ключ знайдено, то сертифікат буде «прив'язаний» до цього ключа. Якщо ключ знайдений не буде, то повернеться помилка.
  • кореневий (PKCS11_CERT_CATEGORY_CA), це сертифікат видавця сертифікатів, застосовується для перевірки підпису під сертифікатами.
    Подібна перевірка підпису (побудова ланцюжка довіри) дозволяє визначити, чи довіряє користувач підпису іншого користувача. Наприклад, у функції verify є режим перевірки сертифіката, на якому було підписано повідомлення. При цьому використовується сховище кореневих сертифікатів на токені, створене імпортом кореневих сертифікатів на токен.
  • інший (PKCS11_CERT_CATEGORY_OTHER), це сертифікат, який не пов'язаний з закритим ключем і не є кореневим.


Отримання списку сертифікатів, що зберігаються на пристрої, виконується за допомогою функції:
std::vector<std::string> enumerateCertificates( unsigned long deviceId, unsigned long category);


Параметр category дозволяє обмежити пошук сертифікатів однієї з перерахованих вище груп.

Приклад пошуку користувальницьких сертифікатів на токені:
std::vector<std::string> certs = cp->enumerateCertificates(id, PKCS11_CERT_CATEGORY_USER); 

// get certificates info by ID 
if (certs.size() > 0)
{
std::cout << "Certificates with USER category(" << certs.size() << "): " << std::endl; 
for (size_t i = 0; i < certs.size(); i++)
{
printCertInfo(cp.get(), id, certs.at(i));
}
}


У поданому вище прикладі функція printCertInfo візуалізує сертифікат, використовуючи для цього метод:
CertFields parseCertificate( unsigned long deviceId, const std::string& certId);

Спочатку візуалізується інформація про видавця сертифіката (issuer), потім про власника (subject). Крім того, відображається термін дії сертифіката та його серійний номер. Метод так само дозволяє розібрати такі розширення сертифіката keyUsage, extendedKeyUsage, certificatePolicies.

Приклад візуалізації сертифіката:
typedef std::vector<std::map<std::string, std::string> > DnList;
typedef std::map<std::string, std::vector<std::string> > ExtensionsMap;

struct CertFields
{
DnList issuer;
DnList subject;
std::string serialNumber;
std::string validNotBefore;
std::string validNotAfter;
ExtensionsMap extensions;
std::string certificateText;
};

void printCertInfo(CryptoCore* cp, unsigned int tokenId, std::string certId)
{
CertFields info = cp->parseCertificate(tokenId, certId);
DnList& dn = info.issuer;

std::cout << "Certificate ID: " << certId << std::endl << "\tIssuer: "; 
for (DnList::iterator it = dn.begin(); it != dn.end(); it++)
{
std::map<std::string, std::string>& rdn = *it; 
if (it != dn.begin())
std::cout << ", ";std::cout << rdn[ "rdn"] << "=" << rdn["value"];
}

std::cout << std::endl;dn = info.subject;std::cout << "\tSubject: "; 
for (DnList::iterator it = dn.begin(); it != dn.end(); it++)
{
std::map<std::string, std::string>& rdn = *it; 
if (it != dn.begin())
std::cout << ", ";std::cout << rdn[ "rdn"] << "=" << rdn["value"];
}
std::cout << std::endl;
std::cout << "\tSerialNumber: " << info.serialNumber << std::endl;
std::cout << "\tValid Not Before: " << info.validNotBefore << std::endl;
std::cout << "\tValid Not After: " << info.validNotAfter << std::endl; 
}

Читання сертифіката з пристрою (експорт) здійснюється в PEM-форматі з допомогою методу:
std::string getCertificate(unsigned long deviceId, const std::string& certId);


Вийде приблизно такий рядок:
----- BEGIN CERTIFICATE-----
MIIBmjCCAUegAwIBAgIBATAKBgYqhQMCagmfadbumqswcqydvqqgewjsvtepma0g
A1UEBxMGTW9zY293MSIwIAYDVQQKFBlPt08gikdhcmfudc1qyxjrlvrlbgvjb20i
MRAwDgYDVQQDEwdUZXN0IENBMB4XDTE0mtiymje2nteynvoxdte1mtiymje2ntey
NVowEDEOMAwGA1UEAxMFZmZmZmYwYzAcbgyqhqmcahmwegyhkoudagijaqyhkoud
AgIeAQNDAARADKA/O1Zw50PzMpcNkWnW39mAJcTehAhkQ2Vg7bhkiwidf7zpe2px
HyAr6lH+stqdACK6sFYmkZ58cBjzL0WBwaNEMEIwjqydvr0lbb4whayikwybbquh
AwIGCCsGAQUFBwMEBgYpAQEBAQIwCwYDvr0pbaqdagkkmawga1udeweb/wQCMAAw
CgYGKoUDAgIDBQADQQD5TY55KbwADGKJrk+bwCGZw24sdIyayIX5dn9hrKkNrZsW
detWY3KJFylSulykS/dfJ871IT+8dXPU5A7WqG4+
-----END CERTIFICATE-----

Для видалення сертифіката з пристрою існує метод:
void deleteCertificate(unsigned long deviceId, const std::string& certId);


Перейдемо тепер до закінченим користувальницьким сценаріями роботи в системі.

Реєстрація в системі

Сертифікат, що використовується для автентифікації користувача, може видаватися як безпосередньо в системі (при наявності УЦ), так і зовнішнім УЦ.

Якщо сертифікат видається безпосередньо в системі, то реєстрація проходить за поданою схемою:



Приклад генерації ключа та формування запиту PKCS#10:
std::string pkcs11path = "./";

cp = new CryptoCore(pkcs11path);

std::vector<unsigned long> devices = cp->enumerateDevices();
std::cerr << "Found " << devices.size() << " devices" << std::endl;

if (devices.empty())
{
std::cerr << "can't find any device" << std::endl;
return 1;
}

unsigned long id = devices.front();

DeviceInfo info = cp->getDeviceInfo(id);
std::cerr << "Device ID: " << id << std::endl;
std::cerr << "\tLabel: " << info.label << std::endl;
std::cerr << "\tSerial: " << info.serial << std::endl;
std::cerr << std::endl;

cp->login(id "12345678");

std::vector<std::string> keys = cp->enumerateKeys(id "Test marker");
if (keys.empty())
{
std::cerr << "No keys were found on token" << std::endl;
}
else
{
std::cerr << "Found " << keys.size() << " key(s) on token" << std::endl;
for (size_t i = 0; i < keys.size(); i++)
{
std::string kId = keys[i];
std::cerr << "Key with id: " << kId << " on token with label: " << cp->getKeyLabel(id, kId) << std::endl;
}
}

std::map<std::string, bool> keyOptions;
keyOptions["needPin"] = false;
std::string keyId;

// key generation
keyId = cp->generateKeyPair(id "A", "Test marker", keyOptions);

std::string keyLabel;
std::cerr << "Please, enter new key label: ";
std::cin >> keyLabel;
cp->setKeyLabel(id, keyId, keyLabel);

std::cerr << "Creating PKCS#10 request on key with ID: " << keyId << std::endl;

std::string str;
std::vector<std::map<std::string, std::string> > subject;

typedef std::map<std::string, std::string> RdnType;
RdnType rdn;

// country name
for (;; )
{
std::cerr << "Please, enter request new country name (2 symbol): ";
std::cin >> str;
if (str.length() != 2)
{
std::cerr << "try again" << std::endl;
continue;
}
else
{
rdn["rdn"] = "countryName";
rdn["value"] = str;
subject.push_back(rdn);
break;
}
}

// commonName
std::cerr << "Please, enter new request commonName: ";
std::cin >> str;

rdn.clear();
rdn["rdn"] = "commonName";
rdn["value"] = str;
subject.push_back(rdn);

// stateOrProvince
std::cerr << "Please, enter new request stateOrProvinceName: ";
std::cin >> str;
rdn.clear();
rdn["rdn"] = "stateOrProvinceName";
rdn["value"] = str;
subject.push_back(rdn);

// locality
std::cerr << "Please, enter new request localityName: ";
std::cin >> str;
rdn.clear();
rdn["rdn"] = "localityName";
rdn["value"] = str;
subject.push_back(rdn);

// organization
std::cerr << "Please, enter new request organizationName: ";
std::cin >> str;
rdn.clear();
rdn["rdn"] = "organizationName";
rdn["value"] = str;
subject.push_back(rdn);
std::cerr << "Please, enter new request organizationalUnitName: ";
std::cin >> str;
rdn.clear();

// organizationalUnit
rdn["rdn"] = "organizationalUnitName";
rdn["value"] = str;
subject.push_back(rdn);
std::map<std::string, std::vector<std::string> > extensions;

std::cout << "PKCS10 request: "<< std::endl<< cp->createPkcs10(id, keyId, subject, extensions, true);

cp->logout(id);


Приклад імпорту користувальницького сертифіката:
std::string pkcs11path = "./";
std::auto_ptr<CryptoCore> cp(new CryptoCore(pkcs11path));

std::vector<unsigned long> devices = cp->enumerateDevices();
if (devices.empty())
{
std::cout << "can't find any device" << std::endl;
return 1;
}


std::cout << "Found " << devices.size() << " devices" << std::endl;

unsigned long id = devices.front();

DeviceInfo info = cp->getDeviceInfo(id);

std::cout << "Device ID: " << id << std::endl;
std::cout << "\tLabel: " << info.label << std::endl;
std::cout << "\tSerial: " << info.serial << std::endl;
std::cout << "\tModel: " << info.model << std::endl;

cp->login(id "12345678");

std::ifstream certFile(file, std::ios::in | std::ios::binary);
std::string certBody((std::istreambuf_iterator<char>(certFile)),
std::istreambuf_iterator<char>());

CertFields certInfo = cp->parseCertificateFromString(certBody); 

DnList& dn = certInfo.subject;
std::cout << "Importing certificate: " << std::endl << "\tSubject: ";
for (DnList::iterator it = dn.begin(); it != dn.end(); it++) 
{
std::map<std::string, std::string>& rdn = *it;
if (it != dn.begin())
std::cout << ", ";
std::cout << rdn["rdn"] << "=" << rdn["value"];
}
std::cout << std::endl;

std::string certId = cp->importCertificate(id, certBody, PKCS11_CERT_CATEGORY_USER));

std::cout << "Certificate imported with ID: " << certId << std::endl;


Якщо ж реєстрація відбувається за сертифікатом, що вже є у користувача, то застосовується наступна схема:


Приклад формування аутентификационной підписи:
std::string pkcs11path = "./";

cp = new CryptoCore(pkcs11path);

std::vector<unsigned long> devices = cp->enumerateDevices();
std::cerr << "Found " << devices.size() << " devices" << std::endl;

if (devices.empty())
{
std::cerr << "can't find any device" << std::endl;
return 1;
}

unsigned long id = devices.front();
DeviceInfo info = cp->getDeviceInfo(id);
std::cerr << "Device ID: " << id << std::endl;
std::cerr << "\tLabel: " << info.label << std::endl;
std::cerr << "\tSerial: " << info.serial << std::endl;
std::cerr << std::endl;

std::vector<std::string> certs = cp->enumerateCertificates(id, PKCS11_CERT_CATEGORY_USER); 

// get certificates info by ID 
if (certs.size() > 0)
{
std::cout << "Certificates with USER category(" << certs.size() << "): " << std::endl; 
for (size_t i = 0; i < certs.size(); i++)
{
printCertInfo(cp.get(), id, certs.at(i));
}
}

cp->login(id "12345678");

// serverSalt - random string from server
std::string authSignature = cp->authenticate(id, certs.front(), serverSalt);


Отримана з сервера рядок випадкових даних усередині методу
std::string authenticate(unsigned long deviceId, const std::string& certId, const std::string& salt);

доповнюється випадковими даними довжиною 32 символу і підписується у форматі CMS attached.

На виході виходить повідомлення наступного змісту:

Перевіривши на сервері підпис під повідомленням з використанням знаходиться в ньому сертифіката, слід взяти дані, від'єднати serverSalt і звірити її.

Після цього потрібно зареєструвати користувача в системі за сертифікатом.

Електронний підпис

В бібліотеці підтримуються два види підписи:
  • у форматі CMS з хешуванням даних по ГОСТ Р 34.11-94 і з обчисленням підписи за ГОСТ Р 34.10-2001;
  • «сира» підпис ГОСТ Р 34.10-2001 з хешуванням даних по ГОСТ Р 34.11-94.


Для підпису у форматі CMS використовується метод:
std::string sign(unsigned long deviceId, const std::string& certId, const std::string& data, const std::map<std::string, bool>& options);


Опції підпису у форматі CMS:
  • addUserCertificate — додавати або не додавати сертифікат користувача повідомлення
  • addSignTime — додавати час підпису (системне) як підписаний атрибут
  • detached додавати до повідомлення підписуються дані («з'єднана» або «від'єднаний» підпис)


Для «сирої» підпису використовується метод:
std::string rawSign(unsigned long deviceId, const std::string& keyId, const std::string& data, const std::map<std::string, bool>& options);


В якості параметра data можуть виступати як безпосередньо дані в hex-поданні, так і предвычисленный хеш ГОСТ Р 34.11-94 від даних. У другому випадку потрібно встановити опцію calculateHash в false.

Опція useHardwareHash застосовується в обох функціях і задає необхідність апаратного обчислення хеш по ГОСТ Р 34.11-94. Якщо дана опція встановлена в false, то застосовується швидка програмна реалізація обчислення геш-функції ГОСТ Р 34.11-94.

Для перевірки «сирої» підписи на сервері використовується відкритий ключ. Отримати відкриту частину ключової пари можна за допомогою методу:
std::string getPublicKeyValue(unsigned long deviceId, const std::string& keyId, const std::map<std::string, bool>& options);


Приклад підписи даних у форматі CMS:
std::auto_ptr<CryptoCore> cp( new CryptoCore(pkcs11path));

std::vector< unsigned long> devices = cp->enumerateDevices();

std::cerr << "Found " << devices.size() << " devices" << std::endl; 

unsigned long id = devices.front();

DeviceInfo info = cp->getDeviceInfo(id);
std::cerr << "Device ID: " << id << std::endl;
std::cerr << "\tLabel: " << info.label << std::endl;
std::cerr << "\tSerial: " << info.serial << std::endl;
std::cerr << "\tModel: " << info.model << std::endl; 

std::vector<std::string> certs = cp->enumerateCertificates(id, PKCS11_CERT_CATEGORY_USER); 
if(certs.size() > 0)
{
cp->login(id "12345678");

std::map<std::string, bool> options;
options[ "addUserCertificate"] = true;
options[ "addSignTime"] = true;
options[ "useHardwareHash"] = false; 

std::string cms = cp->sign(id, certs.front(), data, options);
std::cout << "----- BEGIN CMS-----" << std::endl;
std::cout << cms;
std::cout << "----- END CMS-----" << std::endl;
}

Для перевірки підпису використовується метод:
bool verify(unsigned long deviceId, const std::string& cms, const std::string& data, const std::vector<std::string> userCerts, const std::vector<std::string> ca, const std::vector<std::string> crl, const std::map<std::string, bool>& options)


Метод приймає «отсоединенную» або «приєднану» підпис у форматі CMS. У разі «від'єднаного» підписи в параметрі data повинні бути передані дані. Перевірка підпису здійснюється у два етапи за встановленою true опції verifyCertificate: спочатку перевіряється підпис під даними за допомогою відкритого ключа сертифіката (який знаходиться в CMS, або передається через параметр userCerts), потім перевіряється підпис під сертифікатом відкритого ключа з кореневого сертифіката. У разі встановленої оцпии verifyCertificate в false перевіряється тільки підпис під даними, підпис під сертифікатом не перевіряється.

Для перевірки підпису під сертифікатом використовуються кореневі сертифікати, що зберігаються на пристрої (імпортовані на пристрій як кореневі). Крім того, список кореневих сертифікатів можна розширити, передавши в параметрі ca масив додаткових кореневих сертифікатів (PEM).

Для перевірки того, не відкликаний сертифікат, на якому проводиться перевірка підпису, передбачений параметр crl, в якому передається масив CRL (список відкликання), кожен у форматі PEM.

При встановленні опції useHardwareHash в true при перевірці підпису буде використовуватись апаратне обчислення геш-функції ГОСТ Р 34.11-94.

Шифрування

Для того, щоб забезпечити конфіденційність обміну даними, в бібліотеці передбачено шифрування/розшифрування даних з використанням ГОСТ 28147-89.

Дані шифруються в форматі CMS. Щоб зашифрувати дані у форматі CMS, потрібно сертифікат відкритого ключа «адресата». При цьому розшифрувати таке повідомлення зможе тільки власник закритого ключа, відповідного сертифікату.

Для зберігання сертифіката «адресата» на пристрої, його слід записати на пристрій за допомогою виклику методу importCertificate, при цьому в якості параметра category слід передати PKCS11_CERT_CATEGORY_OTHER.

Шифрування даних проводиться методом:
std::string cmsEncrypt(unsigned long deviceId, const std::string& certId, const std::string& recipientCert, const std::string& data, const std::map<std::string, bool>& options);


Для використання в методі потрібно отримати тіло сертифіката «адресата», або прочитавши його з токена c допомогою методу getCertificate, або отримавши в PEM-форматі безпосередньо з інфосистеми. Щоб використовувати апаратне шифрування ГОСТ 28147-89, потрібно встановити опцію useHardwareEncryption в true. Інакше буде використана швидка програмна реалізація ГОСТ 28147-89.

Для розшифрування даних слід використовувати метод:
std::string cmsDecrypt(unsigned long deviceId, const std::string& keyId, const std::string& cmsData, const std::map<std::string, bool>& options);


У параметрі keyId задається ідентифікатор ключової пари, що відповідає сертифікату, з використанням якого респондент шифрував повідомлення.

Де взяти бібліотеку
Бібліотека буде поширюватися в складі Рутокен SDK. Зараз її можна отримати, написавши листа на info@rutoken.ru.

Комплектація для Windows:
  • Статична бібліотека
  • Заголовковий файл
  • Приклади використання


Для роботи даної бібліотеки потрібно актуальна версія бібліотеки PKCS#11, яку можна взяти на сторінці www.rutoken.ru/support/download/pkcs/

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.