Reputation: 91
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
Reputation: 33774
CryptAcquireCertificatePrivateKey
returns errorCRYPT_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