I have a self-signed certificate in the LocalMachine\My certificate store. I also have a number of unique GUID session identifiers. I would like to create symmetric session keys derived from the private key of this certificate. This must be non-random, based only on the private key and the session GUID.
I'm attempting to use CNG for this, but running into errors with the CNG API. Is there any way to debug this, or a way to view error logs here?
Setup Code:
HCERTSTORE certStore = nullptr;
if (SUCCEEDED(hr))
{
certStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_LOCAL_MACHINE, L"My");
if (nullptr == certStore)
{
hr = HRESULT_FROM_WIN32(GetLastError());
LogError("CertOpenStore failed", "hr=0x%X", hr);
}
}
PCCERT_CONTEXT certCtx = nullptr;
if (SUCCEEDED(hr))
{
CRYPT_HASH_BLOB hashBlob{ DWORD(certHash.size()), certHash.data() };
certCtx = CertFindCertificateInStore(certStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SHA1_HASH, &hashBlob, nullptr);
if (nullptr == certCtx)
{
hr = HRESULT_FROM_WIN32(GetLastError());
LogError("CertFindCertificateInStore failed", "hr=0x%X", hr);
}
}
NCRYPT_KEY_HANDLE privKey = NULL;
BOOL callerFreePrivKey = FALSE;
if (SUCCEEDED(hr))
{
DWORD keySpec = 0;
BOOL success = CryptAcquireCertificatePrivateKey(certCtx, CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, nullptr, &privKey, &keySpec, &callerFreePrivKey);
if (success)
{
if ((CERT_NCRYPT_KEY_SPEC != keySpec) || (!callerFreePrivKey))
{
privKey = NULL; // Make sure not to free this with NCryptFreeObject
hr = E_UNEXPECTED;
}
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
LogError("CryptAcquireCertificatePrivateKey failed", "hr=0x%X", hr);
}
}
Attempt 1: Using NCryptKeyDerivation Fails with 0x80090029 : The requested operation is not supported.
BYTE derivedKey[16]{};
DWORD cbDerivedKey = 0;
if (SUCCEEDED(hr))
{
NCryptBuffer params[]
{
{
sizeof(BCRYPT_SHA256_ALGORITHM),
KDF_HASH_ALGORITHM,
BCRYPT_SHA256_ALGORITHM
},
{
sizeof(KDF_LABEL_SYSTEM_1),
KDF_LABEL,
KDF_LABEL_SYSTEM_1,
},
{
sizeof(sessionId),
KDF_CONTEXT,
&sessionId,
},
};
NCryptBufferDesc paramsDesc
{
NCRYPTBUFFER_VERSION,
_countof(params),
params
};
hr = NCryptKeyDerivation(privKey, ¶msDesc, derivedKey, sizeof(derivedKey), &cbDerivedKey, NCRYPT_SILENT_FLAG);
if (FAILED(hr))
{
LogError("NCryptKeyDerivation failed", "hr=0x%X", hr);
}
}
Attempt 2: I thought to encrypt some fixed data buffer, and use this as the secret for a new key. After which I could use that new key with NCryptKeyDerivation. However, NCryptEncrypt fails with 0x80090027 : The parameter is incorrect.
DWORD blockLenBytes = 0;
if (SUCCEEDED(hr))
{
DWORD cbResult = 0;
hr = NCryptGetProperty(privKey, NCRYPT_BLOCK_LENGTH_PROPERTY, reinterpret_cast<PBYTE>(&blockLenBytes), sizeof(blockLenBytes), &cbResult, NCRYPT_SILENT_FLAG);
if (FAILED(hr))
{
LogError("NCryptOpenStorageProvider failed", "hr=0x%X", hr);
}
else if (cbResult != sizeof(blockLenBytes))
{
hr = E_UNEXPECTED;
LogError("NCryptOpenStorageProvider failed", "hr=0x%X", hr);
}
}
//Use certificate to encrypt static data
//This encrypted result will be our secret for performing the key derivation
std::vector<BYTE> seed(blockLenBytes);
if (SUCCEEDED(hr))
{
for (size_t i = 0; i < seed.size(); ++i)
{
seed[i] = BYTE(0xFF);
}
DWORD seedSizeBytes = DWORD(seed.size() * sizeof(seed[0]));
DWORD cbResult = 0;
hr = NCryptEncrypt(privKey, seed.data(), seedSizeBytes, nullptr, seed.data(), seedSizeBytes, &cbResult, NCRYPT_NO_PADDING_FLAG | NCRYPT_SILENT_FLAG);
if (FAILED(hr))
{
LogError("NCryptEncrypt failed", "hr=0x%X", hr);
}
}
User contributions licensed under CC BY-SA 3.0