В КриптоПро 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(¶, 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;
}