Ключевое слово в защите информации
КЛЮЧЕВОЕ СЛОВО
в защите информации
Получить ГОСТ TLS-сертификат для домена (SSL-сертификат)
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

Опции
К последнему сообщению К первому непрочитанному
Offline miser  
#1 Оставлено : 6 сентября 2016 г. 12:24:20(UTC)
miser

Статус: Активный участник

Группы: Участники
Зарегистрирован: 14.03.2011(UTC)
Сообщений: 152
Мужчина
Откуда: Санкт-Петербург

Сказал «Спасибо»: 1 раз
Поблагодарили: 7 раз в 5 постах
Добрый день.

Linux Ubuntu x64, CentOS x64. КриптоПро CSP 4.0.

Имеется поток зашифрованных данных.

// функция обратного вызова
BOOL WINAPI findRecipients(LPCVOID pvArg, LPBYTE pbData, DWORD cbData, BOOL fFinal);

CMSG_STREAM_INFO streamInfo;
streamInfo.cbContent = CMSG_INDEFINITE_LENGTH;
streamInfo.pfnStreamOutput = findRecipients;
streamInfo.pvArg = NULL;

HCRYPTMSG hMsg = CryptMsgOpenToDecode(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, 0, 0, NULL, &streamInfo);

Читаю поток данных и передаю в функцию CryptMsgUpdate(hMsg, (BYTE*) bytes, len, last).
Функция CryptMsgUpdate отрабатывает без ошибок.
Проблема в том, что нет вызова моей функции.

Как вылечить?

Отредактировано пользователем 6 сентября 2016 г. 12:29:22(UTC)  | Причина: Не указана

Offline miser  
#2 Оставлено : 13 сентября 2016 г. 10:46:54(UTC)
miser

Статус: Активный участник

Группы: Участники
Зарегистрирован: 14.03.2011(UTC)
Сообщений: 152
Мужчина
Откуда: Санкт-Петербург

Сказал «Спасибо»: 1 раз
Поблагодарили: 7 раз в 5 постах
Разработчики КриптоПро, ау!!!
Как вылечить?
Мне очень нужны потоковые функции шифрования и расшифровки.
Под Windows, в КрипоПро CSP 3.x этот функционал работает несколько лет.
Offline Русев Андрей  
#3 Оставлено : 13 сентября 2016 г. 11:29:08(UTC)
Русев Андрей

Статус: Сотрудник

Группы: Администраторы, Участники
Зарегистрирован: 16.04.2008(UTC)
Сообщений: 1,272

Сказал(а) «Спасибо»: 22 раз
Поблагодарили: 446 раз в 325 постах
Поточный enveloped CMS в наших продуктах на *nix работает очень давно, существенных различий с вашим кодом сходу не видно. Разве что, pvArg у нас всегда заполнено. Попробуем разобраться.
Официальная техподдержка. Официальная база знаний.
Offline miser  
#4 Оставлено : 15 сентября 2016 г. 10:37:51(UTC)
miser

Статус: Активный участник

Группы: Участники
Зарегистрирован: 14.03.2011(UTC)
Сообщений: 152
Мужчина
Откуда: Санкт-Петербург

Сказал «Спасибо»: 1 раз
Поблагодарили: 7 раз в 5 постах
На самом деле, у меня все параметры заполнены. В том числе и streamInfo.pvArg.
Я для демонстрации заменил его на NULL.

У меня в коде, в режиме отладки стоит выдача названий вызываемых функции. Примерно так:
Код:
BOOL WINAPI findRecipients(LPCVOID pvArg, LPBYTE pbData, DWORD cbData, BOOL fFinal) {
   fprintf(stderr, "[%d:%s] call %s\n", __LINE__, __FILE__, __PRETTY_FUNCTION__);
   ...
}

В потоке stderr, я вижу вызовы всех своих функций, кроме данной.


Меня еще смущает такое описание CryptMsgGetParam
Цитата:
Функция CryptMsgGetParam получает параметр сообщения после того, как криптографическое сообщение было раскодировано или закодировано. Эта функция вызывается после последнего вызова функции CryptMsgUpdate.

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

Дополню свое сообщение.

Беру за основу пример проекта /opt/cprocsp/src/doxygen/CSP/CodingData
Транслирую и запускаю.
Код:
$ ./CodingData
The original message => Security is our only business
The message to be encoded has been opened. 
Content has been added to the encoded message. 
The length of the data has been calculated. 
Memory has been allocated for the signed message. 
Message encoded successfully. 
The message to decode is open. 

The length of the encoded message is 47.

The encoded BLOB has been added to the message. 
The decoded message size is 30. 
Memory has been allocated for the decoded message.
The message is Security is our only business.
This program ran to completion without error. 

Пример работает без ошибок.
Добавляю функцию
Код:

BOOL WINAPI findRecipients(LPCVOID pvArg, LPBYTE pbData, DWORD cbData, BOOL fFinal) {
    printf("[%d:%s] call %s\n", __LINE__, __FILE__, __PRETTY_FUNCTION__);
    return TRUE;
}

и меняю код
Код:

// Открытие сообщения для раскодирования.

hMsg = CryptMsgOpenToDecode(
               MY_ENCODING_TYPE,      // тип закодированного сообщения.
               0,                     // Флаги.
               0,                     // Поиск данных сообщения.
               0,                     // Криптографический провайдер.
               NULL,                  // Информация издателя.
               NULL);                 // потоковая информация.

на
Код:

// !!!! Функция обратного вызова !!!
CMSG_STREAM_INFO streamInfo;
streamInfo.cbContent = CMSG_INDEFINITE_LENGTH;
streamInfo.pfnStreamOutput = findRecipients;
streamInfo.pvArg = &streamInfo;

// Открытие сообщения для раскодирования.

hMsg = CryptMsgOpenToDecode(
               MY_ENCODING_TYPE,      // тип закодированного сообщения.
               0,                     // Флаги.
               0,                     // Поиск данных сообщения.
               0,                     // Криптографический провайдер.
               NULL,                  // Информация издателя.
               &streamInfo);                 // потоковая информация.

Запускаю
Код:
$ ./CodingData
The original message => Security is our only business
The message to be encoded has been opened. 
Content has been added to the encoded message. 
The length of the data has been calculated. 
Memory has been allocated for the signed message. 
Message encoded successfully. 
The message to decode is open. 

The length of the encoded message is 47.

Error number     : 0x80091005
Error description: Decode MsgUpdate failed

Отредактировано пользователем 15 сентября 2016 г. 13:31:38(UTC)  | Причина: Пример работы CodingData

Offline simon  
#5 Оставлено : 15 сентября 2016 г. 16:39:50(UTC)
simon

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 11.07.2016(UTC)
Сообщений: 10
Российская Федерация
Откуда: Москва

Схема работы поточного расшифрования такая:
1) Открыть сообщение через CryptMsgOpenToDecode
2) Разобрать заголовок через CryptMsgUpdate (с помощью CryptMsgGetParam(CMSG_ENVELOPE_ALGORITHM_PARAM) и ошибки CRYPT_E_STREAM_MSG_NOT_READY можно определить, что заголовок ещё недочитан)
3) Из заголовка прочитать получателей через вызовы CryptMsgGetParam(CMSG_RECIPIENT_COUNT_PARAM) и CryptMsgGetParam(CMSG_RECIPIENT_INFO_PARAM)
4) Заполнить CMSG_CONTROL_DECRYPT_PARA, положить туда хэндл контейнера с ключом, индекс получателя и т.д.
5) Вызвать CryptMsgControl(CMSG_CTRL_DECRYPT) с заполненной CMSG_CONTROL_DECRYPT_PARA, и тем самым "подключить" коллбэк на обработку потока

Коллбэк первый раз сработает после CryptMsgControl(CMSG_CTRL_DECRYPT) и обработает накопленную часть данных,
после этого коллбэк будет работать при каждом вызове CryptMsgUpdate. Набросал макет кода с примером:

Код:

{
    if (!CryptAcquireCertificatePrivateKey(Cert,
        CRYPT_ACQUIRE_COMPARE_KEY_FLAG | CRYPT_ACQUIRE_CACHE_FLAG,
        NULL, &hCryptProv, &keytype, &should_release_ctx))
    {
        return ErrorHandle(ERR_CERT_PRIVATE_KEY);
    }
    BYTE tbenc[STREAM_BLOCK_SIZE]; /* Исходные данные для расшифрования*/
    FILE *in = ::_tfopen(FilePath, _TEXT("rb"));
    if (!in)
        return ErrorHandle(ERR_COMMON_FILE, FilePath);

    memset(tbenc, 0, STREAM_BLOCK_SIZE);
    cbEncode.cbContent = CMSG_INDEFINITE_LENGTH;
    cbEncode.pfnStreamOutput = CmsgStreamOutputCallback;
    cbEncode.pvArg = NULL;

    hMsg = CryptMsgOpenToDecode(
        X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, /* Encoding type*/
        0,                      /* Flags*/
        0,                      /* Message type (get from message)*/
        0,                      /* Cryptographic provider*/
        NULL,                   /* Recipient information future*/
        &cbEncode);             /* Stream information*/
    if (!hMsg)
        return ErrorHandle(ERR_USE_GetLastError);

    BOOL bFin = FALSE;
    BOOL bFirst = TRUE;
    int n = 0;

    size_t tbenc_offset = 0;
    while (bFin == FALSE)
    {
        cbCnt = fread(tbenc + tbenc_offset, sizeof(char), STREAM_BLOCK_SIZE - tbenc_offset, in);
        if (cbCnt < STREAM_BLOCK_SIZE - tbenc_offset) {
            if (ferror(in)) {
                fclose(in);
                ::CryptMsgClose(hMsg);
                return ErrorHandle(ERR_COMMON_FILE, File1);
            }
            bFin = TRUE;
        }

        ret = CryptMsgUpdate(
            hMsg,               /* Handle to the message*/
            tbenc,              /* Pointer to the encoded blob*/
            (DWORD)cbCnt,       /* Size of the encoded blob*/
            bFin);              /* Last call*/
        if (!ret) {
            ::CryptMsgClose(hMsg);
            return ErrorHandle(ERR_USE_GetLastError);
        }
        if (!bDecryptStarted) {
            /* Тип вложения*/
            ret = CryptMsgGetParam(
                hMsg,                   /* Handle to the message*/
                CMSG_ENVELOPE_ALGORITHM_PARAM, /* Parameter type*/
                0,                      /* Index*/
                NULL,                   /* Address for returned // information*/
                &cbData);               /* Size of the returned // information*/
            if (!ret) {
                if (GetLastError() == (DWORD)CRYPT_E_STREAM_MSG_NOT_READY)
                    continue;
                else {
                    ::CryptMsgClose(hMsg);
                    return ErrorHandle(ERR_USE_GetLastError);
                }
            }

            /* Инициализация структуры CMSG_CONTROL_DECRYPT_PARA */
            memset(&DecryptPara, 0, sizeof(DecryptPara));
            DecryptPara.dwRecipientIndex = (DWORD)-1;
            DecryptPara.cbSize = sizeof(DecryptPara);
            DecryptPara.hCryptProv = hCryptProv; /* Using handle opened in */
            DecryptPara.dwKeySpec = AT_KEYEXCHANGE;

            /* Определим индекс в списке получателей сообщения, соответствующий */
            /* сертификату, заданному для расшифрования*/
            cbData = sizeof(DWORD);
            ret = CryptMsgGetParam(hMsg,
                CMSG_RECIPIENT_COUNT_PARAM,
                0,
                &recip_count,
                &cbData);
            if (!ret) {
                ::CryptMsgClose(hMsg);
                return ErrorHandle(ERR_USE_GetLastError);
            }

            for (i = 0; i < (int)recip_count; i++) {
                ret = CryptMsgGetParam(hMsg,
                    CMSG_RECIPIENT_INFO_PARAM,
                    i,
                    NULL,
                    &cbData);
                if (!ret) {
                    ::CryptMsgClose(hMsg);
                    return ErrorHandle(ERR_USE_GetLastError);
                }

                recip_info = (CERT_INFO*)malloc(cbData);
                if (!recip_info) {
                    ::CryptMsgClose(hMsg);
                    return ErrorHandle(ERR_COMMON_NO_MEM);
                }

                ret = CryptMsgGetParam(hMsg,
                    CMSG_RECIPIENT_INFO_PARAM,
                    i,
                    recip_info,
                    &cbData);
                if (!ret) {
                    ::CryptMsgClose(hMsg);
                    return ErrorHandle(ERR_USE_GetLastError);
                }

                /* Произведем сравнение серийных номеров сертификатов и имен издателей */
                if (CertCompareCertificateName(
                    X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                    &(Cert->pCertInfo->Issuer),
                    &(recip_info->Issuer)) &&
                    CertCompareIntegerBlob(&(Cert->pCertInfo->SerialNumber),
                    &(recip_info->SerialNumber)))
                {
                    free(recip_info);
                    recip_info = NULL;
                    DecryptPara.dwRecipientIndex = i;
                    break;
                }
                free(recip_info);
                recip_info = NULL;
            }
            if (DecryptPara.dwRecipientIndex == (DWORD)-1) {
                ::CryptMsgClose(hMsg);
                return ErrorHandle(ERR_ENCR_NO_SKEY);
            }

            bDecryptStarted = TRUE;

            /* Расшифрование сообщения*/
            ret = CryptMsgControl(
                hMsg,				/* Message handle*/
                0,					/* Flags*/
                CMSG_CTRL_DECRYPT,	/* Control type*/
                &DecryptPara);		/* Address of the parameters*/
            if (!ret) {
                ::CryptMsgClose(hMsg);
                return ErrorHandle(ERR_USE_GetLastError);
            }
        }
    }
    if (pbDecoded)
        free(pbDecoded);
    if (hMsg)
        CryptMsgClose(hMsg);
    if (recip_info)
        free(recip_info);
}
Offline miser  
#6 Оставлено : 15 сентября 2016 г. 17:56:11(UTC)
miser

Статус: Активный участник

Группы: Участники
Зарегистрирован: 14.03.2011(UTC)
Сообщений: 152
Мужчина
Откуда: Санкт-Петербург

Сказал «Спасибо»: 1 раз
Поблагодарили: 7 раз в 5 постах
Я обязательно проверю работу предложенного вами кода.
Будет чудесно, если он будет работать.

Главное отличие примера CodingData от вашего, это отсутствие вызова CryptAcquireCertificatePrivateKey в начале.
В начале надо определить идентификатор сертификата получателя. И если у пользователя есть закрытый ключ, то уже потом, надо вызвать CryptAcquireCertificatePrivateKey, перед первым вызовом CryptMsgControl.

Я это написал, а теперь у меня возникли сомнения. В примере CodingData нет вызова функции CryptMsgControl, а сообщение расшифровано. Идет вызов CryptMsgGetParam с параметром CMSG_CONTENT_PARAM и показываются расшифрованные данные.
В чем подвох?

И честно, не понял, почему, подставив параметр callback функции, пример выдает ошибку 0x80091005.
Offline simon  
#7 Оставлено : 16 сентября 2016 г. 15:46:33(UTC)
simon

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 11.07.2016(UTC)
Сообщений: 10
Российская Федерация
Откуда: Москва

Есть несколько типов cmsg сообщений, если перечислять по доступным в CryptMsgOpenToEncode:
CMSG_DATA - "Голые" данные в CMSG (незашифрованные, неподписанные, нехэшированные).
CMSG_HASHED - Хэшированные данные в CMSG.
CMSG_SIGNED - Подписанные данные в CMSG.
CMSG_ENVELOPED - Шифрованные данные в CMSG.
CMSG_SIGNED_AND_ENVELOPED - Подписанные и шифрованные данные в CMSG.

В примере CodingData используется CMSG_DATA, а значит это пример для "голых" данных, там не требуется ни ключ, ни сертификат - CryptMsgUpdate просто "упаковывает" данные в CMSG и затем их оттуда достаёт по второй части примера.

В моём примере используется поточное расшифрование (хотя Message type определяется из сообщения, в поточном режиме). Для него нужен CryptMsgControl(CMSG_CTRL_DECRYPT), из msdn CryptMsgOpenToDecode:
Цитата:
Note Streamed decoding of an enveloped message queues the ciphertext in memory until CryptMsgControl is called to start the decryption. The application must initiate decrypting in a timely manner so that the data can be saved to disk or routed elsewhere before the accumulated ciphertext becomes too large and the system runs out of memory.

Соответственно, если вы хотите модифицировать пример на поточную работу, CryptMsgControl вам не нужен.
А какую практическую задачу вы решаете?
Offline miser  
#8 Оставлено : 19 сентября 2016 г. 10:23:15(UTC)
miser

Статус: Активный участник

Группы: Участники
Зарегистрирован: 14.03.2011(UTC)
Сообщений: 152
Мужчина
Откуда: Санкт-Петербург

Сказал «Спасибо»: 1 раз
Поблагодарили: 7 раз в 5 постах
Есть Java приложение, которое использует нативный КриптоПро CSP. Программа работает уже очень давно, под Windows. Потоки - это Java InputStream, OutputStream. Надо отладить работу под Linux системами.
Offline simon  
#9 Оставлено : 20 сентября 2016 г. 15:57:56(UTC)
simon

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 11.07.2016(UTC)
Сообщений: 10
Российская Федерация
Откуда: Москва

Хорошо, переформулирую вопрос. Чтобы дать вам больше информации, дать нужный пример работы - необходимо знать какой тип сообщения вы используете. Как вы можете видеть из предыдущего сообщения, например, CMSG_ENVELOPED требует дополнительно CryptMsgControl(CMSG_CTRL_DECRYPT) для инициализации коллбэка, поэтому тип сообщения определяет, как нужно работать с потоком. Поэтому не могли бы вы уточнить, какой тип(ы) сообщения вы используете в своей задаче?
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
Guest
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.