Answer the question
In order to leave comments, you need to log in
How to extract the public key from the certificate for further signing and data encryption through the CryptoAPI library?
Hello, I am trying through the CryptoAPI library using C ++ to implement the data signing functionality using a USB token. The MSDN documentation says that the signature can be implemented through the CryptSignHash function, where 1 argument, you need to get HCRYPTHASH, which in turn is obtained through the CryptCreateHash function, in the arguments of which you need to pass HCRYPTKEY, which, as I understand it, should contain the public key of the certificate, but I don’t I can understand how to get the public key of the certificate, I can get the certificate itself (it is installed in user certificates), but I don’t understand what to do next with it ... can someone write an example of how to extract this public key for a certificate ... I will be very grateful ...
Answer the question
In order to leave comments, you need to log in
The CryptoPro SDK has good examples of working with C++ signatures. They helped me a lot when I did work with EDS in Golang. I had to do inserts in C. I advise you to download and study.
Here is an example from there - the repository is opened, a certificate is obtained, then it receives a link to the private key, and the creation and signing of the hash
#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;
}
For signing, you need a private key. The public key can be used to verify that the signature is correct.
In theory, a token is not just a flash drive. It should be impossible to extract the secret key from it. Therefore, you need to dig towards PKCS7 and CryptSignMessage functions
CRYPT_SIGN_MESSAGE_PARA
CERT_CONTEXT
CertEnumCertificatesInStore
CertOpenStore
and so
on .
PS I will not give a code example.
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question