Problem signing a hash using the Wincrypt lib
Hello devs. I need to read the user certificates installed in Windows and use them to make a signature in a hash of a file previously calculated in sha256 with the private key of the chosen certificate.
To do this, I developed the following code inspired by some repositories and also official Microsoft documentation:
This method is in QT c++, it receives a PCCERT_CONTEXT of the certificate chosen to sign and a base64 hash already calculated previously.
QString SOCertificateDao::signHash(PCCERT_CONTEXT pCertContext, const QString base64Hash) {
HCRYPTPROV hProv = NULL;
DWORD dwKeySpec = 0;
BOOL fCallerFreeProvOrNCryptKey = FALSE;
HCRYPTKEY hKey = NULL;
QString signature;
if (!CryptAcquireCertificatePrivateKey(pCertContext, 0, NULL, &hProv, &dwKeySpec, &fCallerFreeProvOrNCryptKey)) {
qDebug() << "erro ao adquirir a chave privada do certificado. erro:" << GetLastError();
return signature;
}
if (!CryptGetUserKey(hProv, dwKeySpec, &hKey)) {
qDebug() << "erro ao obter a chave do usuário. erro:" << GetLastError();
if (fCallerFreeProvOrNCryptKey) {
CryptReleaseContext(hProv, 0);
}
return signature;
}
// Criar um objeto hash vazio
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)){
qDebug() << "erro ao recuperar o provedor:" << GetLastError();
}
HCRYPTHASH hHash = NULL;
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) {
qDebug() << "erro ao criar o objeto hash. erro:" << GetLastError();
CryptDestroyKey(hKey);
if (fCallerFreeProvOrNCryptKey) {
CryptReleaseContext(hProv, 0);
}
return signature;
}
QByteArray hash = QByteArray::fromBase64(base64Hash.toUtf8());
const BYTE* byteArrayPointer = reinterpret_cast<const BYTE*>(hash.data());
if (!CryptSetHashParam(hHash, HP_HASHVAL, reinterpret_cast<const BYTE*>(byteArrayPointer), 0)) { // transformar hash no formato que ele espera
qDebug() << "erro ao definir o valor do hash. erro:" << GetLastError();
CryptDestroyHash(hHash);
CryptDestroyKey(hKey);
if (fCallerFreeProvOrNCryptKey) {
CryptReleaseContext(hProv, 0);
}
return signature;
}
// Assinar o hash
DWORD dwSigLen = 0;
if (!CryptSignHash(hHash, dwKeySpec, NULL, CRYPT_NOHASHOID, NULL, &dwSigLen)) {
qDebug() << "erro ao determinar o tamanho da assinatura - erro:" << GetLastError();
CryptDestroyHash(hHash);
CryptDestroyKey(hKey);
if (fCallerFreeProvOrNCryptKey) {
CryptReleaseContext(hProv, 0);
}
return signature;
}
QByteArray signatureArray(dwSigLen, 0);
if (!CryptSignHash(hHash, dwKeySpec, NULL, CRYPT_NOHASHOID, reinterpret_cast<BYTE *>(signatureArray.data()), &dwSigLen)) {
qDebug() << "erro ao assinar o hash - erro:" << GetLastError();
CryptDestroyHash(hHash);
CryptDestroyKey(hKey);
if (fCallerFreeProvOrNCryptKey) {
CryptReleaseContext(hProv, 0);
}
return signature;
}
signature = QString::fromLatin1(signatureArray.toBase64());
// Limpeza
CryptDestroyHash(hHash);
CryptDestroyKey(hKey);
if (fCallerFreeProvOrNCryptKey) {
CryptReleaseContext(hProv, 0);
}
return signature;
}
The problem I’m facing is that the first call to the CryptSignHash method returns the error code: 2148073494 (set of keys does not exist).
I would like to clarify a few points:
- What could be causing the problem reported above?
- Is there any more efficient way to sign a hash with the user’s private key installed on Windows?