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

Здравствуйте, пытаюсь через библиотеку CryptoAPI используя C++, реализовать функционал подпись данных, используя USB токен. В документации MSDN написано что подпись можно реализовать через функцию CryptSignHash где 1 аргументом, нужно получить HCRYPTHASH, который в свою очередь получается через функцию CryptCreateHash, в аргументах которого нужно передать HCRYPTKEY, который как я понимаю должен содержать в себе публичный ключ сертификата, но я не могу понять, как получить публичный ключ сертификата, могу получить сам сертификат (он установлен в пользовательские сертификаты), но что с ним делать дальше не понимаю... может кто то напишет пример как вынуть этот публичный ключ для сертификата... буду очень благодарен...
  • Вопрос задан
  • 568 просмотров
Пригласить эксперта
Ответы на вопрос 3
udjin123
@udjin123
PHP, Golang, React
В КриптоПро SDK есть хорошие примеры работы с подписями на C++. Мне когда то очень помогли, когда в Golang делал работу с ЭЦП. Пришлось вставки на Си делать. Посоветую скачать и изучить.

Вот пример от туда - открывается хранилище, получается сертификат, далее получает ссылку на закрытый ключ ну и создание и подписание хеша

#pragma warning(disable : 4996)

#include <iterator>
#include <vector>
#include <iostream>
#include <wchar.h>

#ifdef _WIN32
#include <tchar.h>
#else
#include <cstdio>
#include "reader/tchar.h"
#endif

#include "cades.h"

/*
Пример создания усовершенствованной подписи CADES_X_LONG_TYPE_1 с помощью
упрощённых функций КриптоПро ЭЦП SDK по хэш-значению. Пример подписывает
произвольные данные, которые формирует самостоятельно. Результат будет сохранен
в файл sign.dat. Для подписи необходимо чтобы в хранилище сертификатов
присутствовал сертификат с закрытым ключом и ссылкой на работающую OCSP службу
*/

using namespace std;

#include "../samples_util.h"

int main(int argc, char *argv[]) {
    // Открываем хранилище сертификатов пользователя
    HCERTSTORE hStoreHandle = CertOpenSystemStore(0, _TEXT("MY"));

    if (!hStoreHandle) {
        cout << "Store handle was not got" << endl;
        return -1;
    }

    wchar_t *wa = NULL;
    if (argc > 1) {
        size_t len = strlen(argv[1]) + 1;
        wa = new wchar_t[len];
        mbstowcs(wa, argv[1], len);
    }

    // Получаем сертификат для подписания
    PCCERT_CONTEXT context = GetRecipientCert(hStoreHandle, wa);
    if (wa) delete[] wa;

    // Если сертификат не найден, завершаем работу
    if (!context) {
        cout << "There is no certificate with a CERT_KEY_CONTEXT_PROP_ID "
             << endl << "property and an AT_KEYEXCHANGE private key available."
             << endl
             << "While the message could be sign, in this case, it could"
             << endl << "not be verify in this program." << endl
             << "For more information, read the documentation "
                "http://cpdn.cryptopro.ru/" << endl;
        return -1;
    }

    HCRYPTPROV hProv(0);

    DWORD dwProvType = PROV_GOST_2012_256;

    // Получаем ссылку на закрытый ключ сертификата и дестриптор
    // криптопровайдера
    if (!CryptAcquireContext(&hProv, 0, NULL, dwProvType,
                             CRYPT_VERIFYCONTEXT)) {
        CertFreeCertificateContext(context);
        cout << "CryptAcquireContext() failed" << endl;
        return -1;
    }

    // Задаем параметры
    CRYPT_SIGN_MESSAGE_PARA signPara = {sizeof(signPara)};
    signPara.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
    signPara.pSigningCert = context;
    signPara.HashAlgorithm.pszObjId = (LPSTR)szOID_CP_GOST_R3411_12_256;

    CADES_SIGN_PARA cadesSignPara = {sizeof(cadesSignPara)};
    cadesSignPara.dwCadesType = CADES_BES;

    CADES_SIGN_MESSAGE_PARA para = {sizeof(para)};
    para.pSignMessagePara = &signPara;
    para.pCadesSignPara = &cadesSignPara;

    // Формируем данные для подписания
    vector<unsigned char> data(10, 25);

    CERT_CHAIN_PARA		ChainPara = { sizeof(ChainPara) };
    PCCERT_CHAIN_CONTEXT	pChainContext = NULL;

    std::vector<PCCERT_CONTEXT> certs;

    if (CertGetCertificateChain(
        NULL,
        context,
        NULL,
        NULL,
        &ChainPara,
        0,
        NULL,
        &pChainContext)) {

        for (DWORD i = 0; i < pChainContext->rgpChain[0]->cElement - 1; ++i)
        {
            certs.push_back(pChainContext->rgpChain[0]->rgpElement[i]->pCertContext);
        }
    }
    // Добавляем в сообщение цепочку сертификатов без корневого
    if (certs.size() > 0)
    {
        signPara.cMsgCert = (DWORD)certs.size();
        signPara.rgpMsgCert = &certs[0];
    }

    // Получение хэша данных
    HCRYPTHASH hash(0);
    if (!CryptCreateHash(hProv, CALG_GR3411_2012_256, 0, 0, &hash)) {
        CryptReleaseContext(hProv, 0);
        CertFreeCertificateContext(context);
        cout << "CryptCreateHash() failed" << endl;
        return -1;
    }

    DWORD cbToBeSigned(0);
    DWORD cb = sizeof(cbToBeSigned);
    BYTE *pbToBeSigned;

    switch (CryptHashData(hash, &data[0], (DWORD) data.size(), 0)) {
        case TRUE:
            if (!CryptGetHashParam(hash, HP_HASHSIZE, (LPBYTE) &cbToBeSigned, &cb,
                                   0)) {
                CryptReleaseContext(hProv, 0);
                CertFreeCertificateContext(context);
                CryptDestroyHash(hash);
                cout << "CryptGetHashParam() failed" << endl;
                return -1;
            }

            pbToBeSigned = new BYTE[cbToBeSigned];

            if (!CryptGetHashParam(hash, HP_HASHVAL, pbToBeSigned, &cbToBeSigned,
                                   0)) {
                delete[] pbToBeSigned;
                CryptReleaseContext(hProv, 0);
                CertFreeCertificateContext(context);
                CryptDestroyHash(hash);
                cout << "CryptGetHashParam() failed" << endl;
                return -1;
            }
            break;
        default:
            CryptReleaseContext(hProv, 0);
            CertFreeCertificateContext(context);
            CryptDestroyHash(hash);
            cout << "CryptHashData() failed" << endl;
            return -1;
    }

    PCRYPT_DATA_BLOB pSignedMessage = 0;

    string contentType(szOID_RSA_data);

    // Создаем подписанное сообщение
    if (!CadesSignHash(&para, pbToBeSigned, cbToBeSigned, contentType.c_str(), &pSignedMessage)) {
        delete[] pbToBeSigned;
        CryptReleaseContext(hProv, 0);
        CertFreeCertificateContext(context);
        CryptDestroyHash(hash);
        cout << "CadesSignHash() failed" << endl;
        return -1;
    }

     delete[] pbToBeSigned;

    vector<unsigned char> message(pSignedMessage->cbData);
    copy(pSignedMessage->pbData,
         pSignedMessage->pbData + pSignedMessage->cbData, message.begin());

    // Сохраняем результат в файл sign.dat
    if (SaveVectorToFile<unsigned char>("sign.dat", message)) {
        CryptReleaseContext(hProv, 0);
        CertFreeCertificateContext(context);
        CryptDestroyHash(hash);
        cout << "CryptHashData() failed" << endl;
        cout << "Signature was not saved" << endl;
        return -1;
    }

    cout << "Signature was saved successfully" << endl;

    // Освобождаем структуру с закодированным подписанным сообщением
    if (!CadesFreeBlob(pSignedMessage)) {
        CryptReleaseContext(hProv, 0);
        CertFreeCertificateContext(context);
        CryptDestroyHash(hash);
        cout << "CadesFreeBlob() failed" << endl;
        return -1;
    }

    // Закрываем хранилище
    if (!CertCloseStore(hStoreHandle, 0)) {
        CryptReleaseContext(hProv, 0);
        CertFreeCertificateContext(context);
        CryptDestroyHash(hash);
        cout << "Certificate store handle was not closed." << endl;
        return -1;
    }

    // Освобождаем ресурсы
    CryptReleaseContext(hProv, 0);
    CertFreeCertificateContext(context);
    CryptDestroyHash(hash);

    return 0;
}
Ответ написан
wataru
@wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
Для подписи и нужен приватный ключ. Публичным ключем можно проверить, что подпись правильная.
Ответ написан
@none7
По идее токен это не просто флешка. Вытащить секретный ключ из него должно быть невозможно. Следовательно Вам копать в сторону PKCS7 и функций
CryptSignMessage
CRYPT_SIGN_MESSAGE_PARA
CERT_CONTEXT
CertEnumCertificatesInStore
CertOpenStore
и т.д.
P.S. примера кода не дам.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы