HNR
HNR

Reputation: 91

CryptAcquireCertificatePrivateKey() fails with error CRYPT_E_NO_KEY_PROPERTY

This is the setup

AD CS (windows server 2019 Hyper-V VM)

client(windows 10 Hyper-V VM)

when I try to import private key with following code, CertFindCertificateInStore() returns error CRYPT_E_NO_KEY_PROPERTY.

 HANDLE hFile = CreateFileA(pfxPath.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
 if (hFile == INVALID_HANDLE_VALUE) {
     HandleError("Unable to open PFX file");
 }

 DWORD fileSize = GetFileSize(hFile, nullptr);
 std::vector<BYTE> fileData(fileSize);
 DWORD bytesRead;
 if (!ReadFile(hFile, fileData.data(), fileSize, &bytesRead, nullptr) || bytesRead != fileSize) {
     HandleError("Unable to read PFX file");
 }

 CloseHandle(hFile);

 CRYPT_DATA_BLOB pfxBlob = { static_cast<DWORD>(fileData.size()), fileData.data() };
 if (!PFXIsPFXBlob(&pfxBlob)) {
     HandleError("Invalid PFX file");
 }

 hStore = PFXImportCertStore(&pfxBlob, password.c_str(), CRYPT_EXPORTABLE);

 if (!hStore) {
     HandleError("Failed to import PFX certificate store");
 }

 certContext = CertFindCertificateInStore(hStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, nullptr);
 if (!certContext) {
     HandleError("Failed to find certificate in PFX store");
 }

 
 if (!CryptAcquireCertificatePrivateKey(certContext, CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, nullptr, &hCryptProvOrNCryptKey, &dwKeySpec, &fCallerFreeProvOrNCryptKey)) {
     auto dw = GetLastError();
     HandleError("Failed to acquire private key");
 }

with self signed certificate, able to successfully import private key and export it as well.

I have tried passing 0 instead of CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, but still same error.

Thanks for the help.

Upvotes: 0

Views: 339

Answers (1)

RbMm
RbMm

Reputation: 33774

CryptAcquireCertificatePrivateKey returns error CRYPT_E_NO_KEY_PROPERTY.

CryptAcquireCertificatePrivateKey look for CERT_KEY_PROV_INFO_PROP_ID and if it not exist, return such error.

the PFXImportCertStore set on cert CERT_KEY_PROV_INFO_PROP_ID in case PKCS12_NO_PERSIST_KEY not used or CERT_KEY_CONTEXT_PROP_ID if we use this flag. but this is not on all certificates in pfx, but only on cert which public key, correspond to private key from pfx. but you :

while exporting selected these options "include all certificates in the certificate path if possible"

so several certificates was in pfx, and only one containing CERT_KEY_PROV_INFO_PROP_ID or CERT_KEY_CONTEXT_PROP_ID property.

so instead CertFindCertificateInStore we need CertEnumCertificatesInStore and check every certificate in store, until found one "good".

also in most case better use PKCS12_NO_PERSIST_KEY, otherwise private key will be stored in new key container with autogenerated random name.

code can be look like:

CRYPT_DATA_BLOB PFX;
if (NOERROR ReadFromFile(pfxPath, &PFX.pbData, &PFX.cbData))
{
    if (HCERTSTORE hStore = PFXImportCertStore(&PFX, password, 
        CRYPT_EXPORTABLE|PKCS12_ALWAYS_CNG_KSP|PKCS12_NO_PERSIST_KEY))
    {
        PCCERT_CONTEXT pCertContext = 0;
        while(pCertContext = CertEnumCertificatesInStore(hStore, pCertContext))
        {
            CERT_KEY_CONTEXT ckc;
            ULONG cb = sizeof(ckc);
            if (CertGetCertificateContextProperty(pCertContext, CERT_KEY_CONTEXT_PROP_ID, &ckc, &cb))
            {
                // use ckc.hNCryptKey
                // not need call NCryptFreeObject(ckc.hNCryptKey);
                CertFreeCertificateContext(pCertContext);
                break;
            }
        }
        CertCloseStore(hStore, 0);
    }
    LocalFree(PFX.pbData);
}

Upvotes: 1

Related Questions