Problem signing a hash using the Wincrypt lib

William Coelho 0 Reputation points
2024-06-13T18:30:13.1666667+00:00

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:

  1. What could be causing the problem reported above?
  2. Is there any more efficient way to sign a hash with the user’s private key installed on Windows?
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,764 questions
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.