Вызов криптоапи случайным образом дает ошибку
От: s_aa Россия  
Дата: 14.04.21 13:50
Оценка:
Вот выделил пример, вызов CryptSignMessage выдает иногда (случайным образом) ошибку 0xC0000225.
Что подскажете. Если что я не настоящий плюсовик, нужда заставила немножко пописать на C++.

#define CRYPT_SIGN_MESSAGE_PARA_HAS_CMS_FIELDS
#include <iostream>
#include <Windows.h>
#include <io.h>
#include "PureExampleC0000225.h"

#define ERROR_BUFFER_SIZE 2048
#define MAX_SIZE_CERT_NAME 1024
#define MY_MSG "CryptoAPI is a good way to handle security"

static BYTE* msg = nullptr;
static LPWSTR errmsg = nullptr;
static LPCWSTR HandleError(DWORD err, LPCWSTR custMsg);
static PCCERT_CONTEXT
FindCertificate(
    const HCERTSTORE hStore,
    const WCHAR* CertSearchString);
static DWORD Sign(BYTE* message, DWORD cbmess_len,
    BYTE** signMessage, LPCWSTR cert, LPCWSTR* errMessage, BOOL Detached);

int main()
{
    BYTE* pbMessage = (BYTE*)MY_MSG;
    DWORD cbMessage = (strlen((CHAR*)pbMessage) + 1);
    BYTE* signMessage = nullptr;
    LPCWSTR errMessage = nullptr;
    DWORD result = Sign(pbMessage, cbMessage, &signMessage, L"Common name (CN) сертификата",
        &errMessage, TRUE);
    if (result != -1)
    {
        std::wcout << L"Все хорошо!\n";
        ClearSignMemory();
    }
    else
    {
        std::wcout << errMessage << "\n";
        ClearErrorMemory();
    }
}

static DWORD Sign(BYTE* message, DWORD cbmess_len,
    BYTE** signMessage, LPCWSTR cert, LPCWSTR* errMessage, BOOL Detached)
{
    DWORD cbsignMessage = 0;
    HCERTSTORE certStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
        X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
        NULL, CERT_STORE_OPEN_EXISTING_FLAG |
        CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG, L"MY");
    if (certStore != nullptr)
    {
        PCCERT_CONTEXT certContext = FindCertificate(certStore, cert);
        if (certContext != nullptr)
        {
            CRYPT_OBJID_BLOB hashParam;
            hashParam.cbData = 0;
            hashParam.pbData = nullptr;
            CRYPT_ALGORITHM_IDENTIFIER hashAlg;
            hashAlg.pszObjId = (LPSTR)"1.2.643.7.1.1.2.2";
            hashAlg.Parameters = hashParam;
            CRYPT_ALGORITHM_IDENTIFIER hashEncryptionAlg;
            hashEncryptionAlg.pszObjId = (LPSTR)"1.2.643.7.1.1.1.1";
            hashEncryptionAlg.Parameters = hashParam;
            CRYPT_SIGN_MESSAGE_PARA signPara;
            signPara.cbSize = sizeof(signPara);
            signPara.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
            signPara.pSigningCert = certContext;
            signPara.HashAlgorithm = hashAlg;
            signPara.pvHashAuxInfo = nullptr;
            signPara.cMsgCert = 1;
            signPara.rgpMsgCert = &certContext;
            signPara.cMsgCrl = 0;
            signPara.cAuthAttr = 0;
            signPara.rgAuthAttr = NULL;
            signPara.cUnauthAttr = 0;
            signPara.dwFlags = 0;
            signPara.dwInnerContentType = 0;
            signPara.HashEncryptionAlgorithm = hashEncryptionAlg;
            signPara.pvHashEncryptionAuxInfo = 0;

            const BYTE* MessageArray[] = { message };
            DWORD_PTR MessageSizeArray[1];
            MessageSizeArray[0] = cbmess_len;

            // !!!!!!!!!! вот здесь бывает ошибка, 1 раз в 10 запусков примерно, случайным образом.
            if (!CryptSignMessage(
                &signPara,
                Detached,
                1,
                MessageArray,
                MessageSizeArray,
                NULL,
                &cbsignMessage))
            {
                *errMessage = HandleError(GetLastError(), L"Calculation size of buffer is failed!!!.");
                CertFreeCertificateContext(certContext);
                CertCloseStore(certStore, 0);
                return -1;
            }

            msg = new BYTE[cbsignMessage];

            if (!CryptSignMessage(
                &signPara,
                Detached,
                1,
                MessageArray,
                MessageSizeArray,
                msg,
                &cbsignMessage))
            {
                *errMessage = HandleError(GetLastError(), L"Sign message is failed.");
                delete[] msg;
                CertFreeCertificateContext(certContext);
                CertCloseStore(certStore, 0);
                return -1;
            }

            *signMessage = msg;
            CertFreeCertificateContext(certContext);
            CertCloseStore(certStore, 0);
            return cbsignMessage;
        }
        else
        {
            *errMessage = HandleError(GetLastError(), L"Find certificate is failed.");
            CertCloseStore(certStore, 0);
            return -1;
        }
    }
    else
    {
        *errMessage = HandleError(GetLastError(), L"Find certificate store is failed.");
        return -1;
    }
}

static PCCERT_CONTEXT
FindCertificate(
    const HCERTSTORE hStore,
    const WCHAR* CertSearchString)
{
    PCCERT_CONTEXT capiCertificate = NULL;
    WCHAR certname[MAX_SIZE_CERT_NAME] = { 0 };

    for (;;)
    {
        capiCertificate = CertEnumCertificatesInStore(hStore, capiCertificate);
        if (NULL == capiCertificate)
        {
            break;
        }

        if (FALSE ==
            CertGetNameStringW(capiCertificate, CERT_NAME_ATTR_TYPE,
                0, (LPSTR)szOID_COMMON_NAME, certname, MAX_SIZE_CERT_NAME))
        {
            CertFreeCertificateContext(capiCertificate);
            capiCertificate = NULL;
            break;
        }

        if ((0 == wcsncmp(certname, CertSearchString, MAX_SIZE_CERT_NAME)))
        {
            break;
        }
    }
    return capiCertificate;
}

static LPCWSTR HandleError(DWORD err, LPCWSTR custMsg)
{
    errmsg = new WCHAR[ERROR_BUFFER_SIZE];
    DWORD nSize = ERROR_BUFFER_SIZE;
    wcscpy_s(errmsg, ERROR_BUFFER_SIZE, custMsg);
    wcscat_s(errmsg, ERROR_BUFFER_SIZE, L"\n");
    wcscat_s(errmsg, ERROR_BUFFER_SIZE, L"GetLastError: ");
    _ultow_s(err, errmsg + wcslen(errmsg), nSize - wcslen(errmsg), 10);
    wcscat_s(errmsg, ERROR_BUFFER_SIZE, L"\n");
    FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        nullptr, err, 0, errmsg + wcslen(errmsg), nSize - wcslen(errmsg), nullptr);
    return errmsg;
}

void ClearSignMemory()
{
    if (msg != nullptr)
    {
        delete msg;
        msg = nullptr;
    }
}

void ClearErrorMemory()
{
    if (errmsg != nullptr)
    {
        delete errmsg;
        errmsg = nullptr;
    }
}
Жизнь не обязана доставлять удовольствие. Достаточно отсутствия страданий.
Re: Вызов криптоапи случайным образом дает ошибку
От: kov_serg Россия  
Дата: 14.04.21 14:44
Оценка: 6 (1) +1
Здравствуйте, s_aa, Вы писали:

_>Вот выделил пример, вызов CryptSignMessage выдает иногда (случайным образом) ошибку 0xC0000225.

_>Что подскажете. Если что я не настоящий плюсовик, нужда заставила немножко пописать на C++.

..
            CRYPT_SIGN_MESSAGE_PARA signPara;
            ZeroMemory(&signParam,sizeof(signPara));

            signPara.cbSize = sizeof(signPara);
..

Попробуйте очищать все поля в структуре. Возможно он так реагирует на мусор в rgpMsgCrl и rgUnauthAttr.
Re: Вызов криптоапи случайным образом дает ошибку
От: Pzz Россия https://github.com/alexpevzner
Дата: 14.04.21 16:26
Оценка:
Здравствуйте, s_aa, Вы писали:

_>Вот выделил пример, вызов CryptSignMessage выдает иногда (случайным образом) ошибку 0xC0000225.

_>Что подскажете. Если что я не настоящий плюсовик, нужда заставила немножко пописать на C++.

А тебе обязательно это делать именно с помощью CryptoAPI? Может, OpenSSL возьмешь, или еще какую опенсорсную библиотеку?

Проблем с CryptoAPI, собственно, две:
1) С ним хрен разберешься
2) Никогда нельзя быть уверенным в том, какие алгоритмы доступны на машине заказчика (оно еще и от региона/экспортных ограничений может зависеть). Причем если некий алгоритм был доступен сегодня, никто не обещал, что он по-прежнему будет доступен и завтра.
Re[2]: Вызов криптоапи случайным образом дает ошибку
От: s_aa Россия  
Дата: 14.04.21 18:44
Оценка:
Pzz>А тебе обязательно это делать именно с помощью CryptoAPI? Может, OpenSSL возьмешь, или еще какую опенсорсную библиотеку?

Я бы рад, но чтобы юридически значимые подписи делать нужно сертифицированное ПО,в данном случае криптопро.
Жизнь не обязана доставлять удовольствие. Достаточно отсутствия страданий.
Re[3]: Вызов криптоапи случайным образом дает ошибку
От: Pzz Россия https://github.com/alexpevzner
Дата: 14.04.21 18:46
Оценка:
Здравствуйте, s_aa, Вы писали:

Pzz>>А тебе обязательно это делать именно с помощью CryptoAPI? Может, OpenSSL возьмешь, или еще какую опенсорсную библиотеку?


_>Я бы рад, но чтобы юридически значимые подписи делать нужно сертифицированное ПО,в данном случае криптопро.


Ну, тады ой.
Re[2]: Вызов криптоапи случайным образом дает ошибку
От: VladFein США  
Дата: 14.04.21 18:55
Оценка: -1
Здравствуйте, kov_serg, Вы писали:

_>
_>..
_>            CRYPT_SIGN_MESSAGE_PARA signPara;
_>            ZeroMemory(&signParam,sizeof(signPara));

_>            signPara.cbSize = sizeof(signPara);
_>..
_>

_>Попробуйте очищать все поля в структуре. Возможно он так реагирует на мусор в rgpMsgCrl и rgUnauthAttr.

SecureZeroMemory ?
Re: Вызов криптоапи случайным образом дает ошибку
От: VVV Россия  
Дата: 19.04.21 02:58
Оценка:
Здравствуйте, s_aa, Вы писали:

_> hashAlg.pszObjId = (LPSTR)"1.2.643.7.1.1.2.2";

_> hashEncryptionAlg.pszObjId = (LPSTR)"1.2.643.7.1.1.1.1";

Не факт, что это "root cause", но, если приходится снимать константность с помощью (LPSTR), то, может завести массивчики на стеке с этими строками и их адреса передавать?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.