Re: Вопросы по работе с OpenSSL C++

Binger пишет:

Павел Анфимов, функция CMS_encrypt на вход требует BIO *in в качестве данных, которые будут шифроваться. Павел, каким образом я могу зашифровать данные прямо из массива байт unsigned char*? Т.е. не читая их из локального файла, а работая уже с массивом байт.

using bioPtr = std::unique_ptr<BIO, void(*)(BIO*)>;

bioPtr inBio(nullptr, BIO_free_all);

inBio.reset(BIO_new_mem_buf(data, dataSize));
CHECK("  BIO_new_mem_buf", inBio != NULL, free_sk_certs);

...
cmsCntInfo = CMS_encrypt(certs, inBio.get(), cipher, 0);
CHECK("  CMS_encrypt", cms != NULL, free_sk_certs);

Re: Вопросы по работе с OpenSSL C++

Binger пишет:

Павел Анфимов, подскажите ещё, пожалуйста, возможно ли работать одновременно с OpenSSL и библиотекой PKCS11, подключая одновременно хэдеры и либы этих двух инструментов в проект? Не будет никаких конфликтов имён или тому подобных? У меня возникли конфликты какие-то, и я бы хотел этот вопрос уточнить:
https://forum.rutoken.ru/uploads/images/2019/11/6d6e4526c73e89c439e7823ab5db8a6c.png
Можно ли как-то решить данную проблему?

Конечно, все должно работать. Покажите вашу секцию include.
Судя по ошибкам у вас какой-то лишний символ закрался, и парсиг сломался в целом.

(2019-11-29 11:45:51 отредактировано Binger)

Re: Вопросы по работе с OpenSSL C++

Павел Анфимов,

#include "stdafx.h"
#include "CryptoModule.h"
#include "AddressFilter.h"


#include <Common.h> //от PKCS11
#include <codecvt>
#include "Utils.h"

#include "resource.h"
#include "EnterPinDialog.h"

#include <CommonOpenSSL.h> //Common.h от OpenSSL
#include <openssl/pem.h>
#include <openssl/cms.h>

В проекте подключены ещё пользовательские хэдеры. Если вы говорите, что всё должно быть норм, значит уже у нас где-то несостыковка происходит.

(2019-11-29 12:30:01 отредактировано Павел Анфимов)

Re: Вопросы по работе с OpenSSL C++

Не имея проекта невозможно понять что не так.
Но должны быть подключено следующее:

#include <rtpkcs11.h>

#include <openssl/pem.h>
#include <openssl/cms.h>

#include <openssl/engine.h>
#ifdef __APPLE__
#include <rtengine/engine.h>
#else
#include <rutoken/engine/engine.h>
#include <rutoken/engine/call.h>
#endif

Re: Вопросы по работе с OpenSSL C++

Павел Анфимов, понимаю. Разобрался уже у себя там... Спасибо за помощь))

(2019-11-29 12:41:42 отредактировано Binger)

Re: Вопросы по работе с OpenSSL C++

Upd.
Павел Анфимов, что я делаю не так? Хочу добавить все найденные сертификаты в получателей шифрованного сообщения. Во время выполнения кода

r = sk_X509_push(certs, cert);
CHECK("  sk_X509_push", r == 1, free_sk_certs);

на второй итерации цикла происходит вылет в free_sk_certs.

    /*************************************************************************
        * Создание контейнера сертификатов                                       *
        *************************************************************************/
        certs = sk_X509_new_null();
        CHECK("  sk_X509_new_null", certs != NULL, free_cert);

        rv = m_functionList->C_FindObjectsInit(session, certificateTemplate, arraysize(certificateTemplate));
        CHECK("    C_FindObjectsInit", rv == CKR_OK, close_session);

        certHandles.clear();
        certHandles.resize(10000);

        rv = m_functionList->C_FindObjects(session, certHandles.data(), (CK_ULONG)certHandles.size(), &certCount);
        CHECK("    C_FindObjects", rv == CKR_OK, close_session);


        for (size_t i = 0; i < certCount; ++i) 
        {
            CK_ATTRIBUTE certValueAttr = { CKA_VALUE, NULL_PTR, 0 };

            rv = m_functionList->C_GetAttributeValue(session, certHandles[i], &certValueAttr, 1);
            CHECK("    C_GetAttributeValue", rv == CKR_OK, close_session);

            certValue.resize(certValueAttr.ulValueLen);
            certValueAttr.pValue = certValue.data();

            rv = m_functionList->C_GetAttributeValue(session, certHandles[i], &certValueAttr, 1);
            CHECK("    C_GetAttributeValue", rv == CKR_OK, close_session);

            pemCerts.emplace_back(certToPem(certValue));

            cert = certToX509(pemCerts[i]);
            CHECK("  certToX509", cert != NULL, free_in_bio);

            /*************************************************************************
            * Добавление сертификата в контейнер                                     *
            *************************************************************************/
            r = sk_X509_push(certs, cert);
            CHECK("  sk_X509_push", r == 1, free_sk_certs);
        }

        rv = m_functionList->C_FindObjectsFinal(session);
        CHECK("    C_FindObjectsFinal", rv == CKR_OK, close_session);    

Подскажите, пожалуйста, как правильно добавить сертификат в получатели? Спасибо.

Re: Вопросы по работе с OpenSSL C++

Binger, вам нужно передать владение сертификатом контейнеру хранения сертификатов STACK_OF(X509) * certs.

Для этого у unique_ptr есть метод release():

r = sk_X509_push(certs, cert.release());

Re: Вопросы по работе с OpenSSL C++

Павел Анфимов, подскажите, пожалуйста, как шифрованное сообщение не выводить через поток вывода, а передать байты сообщения в массив unsigned char?

       /*************************************************************************
        * Открытие поточного вывода в файл                                       *
        *************************************************************************/
        outBio = BIO_new_file("cms_encrypted.pem", "wb");
        CHECK("  BIO_new_file", outBio != NULL, free_cms);

        /*************************************************************************
        * Запись CMS структуры в файл                                            *
        *************************************************************************/
        r = PEM_write_bio_CMS(outBio, cms);
        CHECK("  PEM_write_bio_CMS", r == 1, free_out_bio);

Re: Вопросы по работе с OpenSSL C++

std::vector<uint8_t> readMemBio(BIO* bio) {
    char* ptr;
    int len = BIO_get_mem_data(bio, &ptr);
    if (len < 0) return {};

    std::vector<uint8_t> result(ptr, ptr + len);

    // normally returns 1 for success and 0 or -1 for failure.
    // File BIOs are an exception, they return 0 for success and -1 for failure.
    int r = BIO_reset(bio);
    if (r <= 0) return {};

    return result;
}
...

bioPtr outBio(nullptr, BIO_free_all);
std::vector<uint8_t> cmsBuf;
...
cmsCntInfo = CMS_encrypt(certs, inBio.get(), cipher, 0);
CHECK("  CMS_encrypt", cms != NULL, free_sk_certs);

outBio.reset(BIO_new(BIO_s_mem()));
CHECK("  BIO_new", outBio != NULL, free_cms);

r = PEM_write_bio_CMS(outBio.get(), cmsCntInfo);
CHECK("  PEM_write_bio_CMS", r > 0, free_cms);

cmsBuf = readMemBio(outBio.get());

Re: Вопросы по работе с OpenSSL C++

Павел Анфимов, есть ли аналогия PEM_write_bio_CMS(outBio.get(), cms) для DER формата?

Re: Вопросы по работе с OpenSSL C++

Вместо

r = PEM_write_bio_CMS(outBio.get(), cmsCntInfo);
CHECK("  PEM_write_bio_CMS", r > 0, free_cms);

на

r = i2d_CMS_bio(outBio.get(), cmsCntInfo);
CHECK("  i2d_CMS_bio", r > 0, free_cms);

Re: Вопросы по работе с OpenSSL C++

Павел Анфимов, попробовал расшифровать зашифрованный файл, используя ключ сертификата, которого нет в списке получателей и  никаких ошибок не было в работе программы. Так и должно разве быть? Я просто в конечном итоге получил неверные данные расшифрованные:

ЎП\жH€s№вlMсAуПSP”e—8¦µўSЮy
ЯЛё‹–

вместо:

1 2 3 4 5 
ONE TWO THREE FOUR FIVE

Павел, как можно данную ситуацию превратить в ошибку?

(2019-11-29 18:33:45 отредактировано Binger)

Re: Вопросы по работе с OpenSSL C++

Павел Анфимов, хотя может быть я не так поступал: я изменил значение CK_BYTE g_keyPairIdGost2012RtEngine[] = { "1234567890" } в Common.h,
где 1234567890- CKA_ID ключевой пары на токене.
Или каким образом можно выбрать ключевую пару для расшифрования?

Re: Вопросы по работе с OpenSSL C++

Ориентируйтесь на возвращаемое значение CMS_decrypt https://www.openssl.org/docs/man1.0.2/m … crypt.html

Особенности функции decrypt: она всегда будет возвращать какие-то данные, даже когда на входе не соответствующий CMS сертификат. Это гарантирует одинаковое время выполнения функции для защиты от MMA атак.

(2019-12-03 11:35:59 отредактировано Binger)

Re: Вопросы по работе с OpenSSL C++

Павел Анфимов, добрый день. Подскажите, пожалуйста, что я не так делаю? Хотел получить сертификаты из cms для дальнейшей работы с ними и получения информации о серийном номере. Однако функция CMS_get1_certs() возвращает NULL.

#include "stdafx.h"

#define HARDWARE_KEYS

#include <assert.h>
#include <CommonOpenSSL.h>
#include <openssl/cms.h>


int main(void)
{
    EVP_PKEY* key;                                        // Описатель ключевой пары
    ENGINE* rtEngine;                                     // rtengine
    BIO* inBio;                                           // Описатель потока ввода
    BIO* outBio;                                          // Описатель потока вывода
    STACK_OF(X509) * certs = NULL;
    CMS_ContentInfo* cms;                                 // Описатель CMS структуры

    int r;                                                // Код возврата
    int errorCode = 1;                                    // Флаг ошибки

    printf("Sample has started.\n\n");
    /*************************************************************************
    * Создание rtengine и регистрация его в OpenSSL                          *
    *************************************************************************/
    r = rt_eng_init();
    CHECK("  rt_eng_init", r == 1, exit);

    /*************************************************************************
    * Получение rtengine                                                     *
    *************************************************************************/
    rtEngine = rt_eng_get0_engine();
    assert(rtEngine);

    /*************************************************************************
    * Установка rtengine реализацией по умолчанию                            *
    *************************************************************************/
    r = ENGINE_set_default(rtEngine, ENGINE_METHOD_ALL - ENGINE_METHOD_RAND);
    CHECK("  ENGINE_set_default", r == 1, finalize_engine);

    /*************************************************************************
    * Получение ключевой пары                                                *
    *************************************************************************/
    printf("  get_key_pair...\n");
    key = get_key_pair();
    CHECK("  get_key_pair", key != NULL, unregister_engine);

    /*************************************************************************
    * Открытие поточного ввода из файла                                      *
    *************************************************************************/
    inBio = BIO_new_file("cms_encrypted.pem", "rb");
    CHECK("  BIO_new_file", inBio != NULL, free_key);

    /*************************************************************************
    * Чтение CMS структуры из файла                                          *
    *************************************************************************/
    cms = PEM_read_bio_CMS(inBio, NULL, NULL, NULL);
    CHECK("  PEM_read_bio_CMS", cms != NULL, free_in_bio);

   certs = CMS_get1_certs(cms);
    
   for (int i = 0; certs && i < sk_X509_num(certs); i++) 
  {
    X509 *cert = sk_X509_value(certs, i);
    const ASN1_INTEGER *serial = X509_get_serialNumber(cert);
  }
    ...

https://forum.rutoken.ru/uploads/images/2019/12/5d78454af5ae52dfc84092bada5c49e8.png