35,01
рейтинг
10 февраля 2015 в 13:36

Разработка → Библиотека для встраивания электронной подписи в приложения С++ tutorial



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

Некоторое время назад мы поддержали Рутокен ЭЦП в 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();

Данный метод возвращает вектор идентификаторов слотов, в которые подключены устройства Рутокен.

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


После получения списка устройств требуется получить информацию о каждом конкретном устройстве. Для этой цели предусмотрен метод:

DeviceInfo getDeviceInfo(unsigned long deviceId);


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

    // серийный номер устройства (совпадает с напечатанным на устройстве)
    std::string serial;

    // модель устройства
    std::string model;  

    // тип устройства
    unsigned int type;  

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

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

Возможные типы устройств Рутокен заданы cледующим образом:

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


Пример базовой работы с устройствами:

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 new request 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 проверяется только подпись под данными, подпись под сертификатом не проверяется.

Для проверки подписи под сертификатом используются корневые сертификаты, хранящиеся на устройстве (импортированные на устройство как корневые). Кроме того, спиcок корневых сертификатов можно расширить, передав в параметре 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
Автор: @cryptoman
Компания «Актив»
рейтинг 35,01

Комментарии (4)

  • +2
    unsigned long deviceId
    

    Странно видеть такое в коде библиотеки C++, больше на C смахивает. Хотя и там тоже можно определить typedef. А typedef желателем, чтобы пользователи не начали передавать в качестве параметров какие-то левые числа, а не полученные от самой библиотеки.
    unsigned long category
    

    Это место прямо-таки просит об enum-е!
  • 0
    А можете рассказать подробнее о «российских криптоалгоритмах»? Есть какие-нибудь научные статьи почитать или всё секретно?
  • +3
    Исходя из
    Комплектация для Windows:
    Статическая библиотека

    и того, что библиотека экспортирует классы и принимает в качестве параметров std::string и std::vector — вопрос: с какими компиляторами «дружит» библиотека?

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка