in azure-protected-vm-secrets/Windows/WincryptX509.cpp [24:157]
std::string generate_root_cert() {
std::string encoded_cert;
HCRYPTPROV hProv;
BYTE bEncoded[TEMPBUF_SIZE];
DWORD dwEncoded = TEMPBUF_SIZE;
PCCERT_CONTEXT pc = NULL;
DWORD dwError;
if (!CertStrToName(X509_ASN_ENCODING,
SELFSIGNEDCERTNAME,
CERT_X500_NAME_STR,
NULL,
bEncoded,
&dwEncoded,
NULL)) {
throw WinCryptError("CertStrToName failed.", GetLastError());
}
WCHAR* pszKeyContainerName = (WCHAR *)KEY_CONTAINER_NAME;
if (!CryptAcquireContext(&hProv, pszKeyContainerName, MS_STRONG_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
{
dwError = GetLastError();
if (dwError == NTE_EXISTS) {
if (!CryptAcquireContext(&hProv, pszKeyContainerName, MS_STRONG_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
{
throw WinCryptError("CryptAcquireContext failed.", GetLastError());
}
}
else {
throw WinCryptError("CryptAcquireContext failed.", GetLastError());
}
}
HCRYPTKEY hKey = NULL;
DWORD keyLength = 0x08000000; // upper 16 bits is 2048 which is our requested key length
if (!CryptGenKey(hProv, AT_SIGNATURE, keyLength | CRYPT_EXPORTABLE, &hKey))
{
throw WinCryptError("CryptGenKey failed.", GetLastError());
}
CRYPT_KEY_PROV_INFO kpi;
ZeroMemory(&kpi, sizeof(kpi));
kpi.pwszContainerName = pszKeyContainerName;
kpi.pwszProvName = (LPWSTR)MS_STRONG_PROV;
kpi.dwProvType = PROV_RSA_FULL;
kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID | CRYPT_MACHINE_KEYSET;
kpi.dwKeySpec = AT_SIGNATURE;
SYSTEMTIME et;
GetSystemTime(&et);
// We need to add 5 years to the current date to come up with the expiration time
// This is the way MSDN recommends
FILETIME ft;
if (!SystemTimeToFileTime(&et, &ft))
{
throw WinCryptError("SystemTimeToFileTime failed.", GetLastError());
}
ULARGE_INTEGER bigInt;
ZeroMemory(&bigInt, sizeof(bigInt));
bigInt.HighPart = ft.dwHighDateTime;
bigInt.LowPart = ft.dwLowDateTime;
// 5 years * 365 days * 24 hours * 60 minutes * 60 seconds * 1,000,000,000 nanoseconds / 100 (100-nanoseconds)
bigInt.QuadPart += 1576800000000000; // 5 years in 100-nanosecond intervals
ft.dwHighDateTime = bigInt.HighPart;
ft.dwLowDateTime = bigInt.LowPart;
if (!FileTimeToSystemTime(&ft, &et))
{
throw WinCryptError("FileTimeToSystemTime failed.", GetLastError());
}
LPSTR lpEKU[1] = { 0 };
lpEKU[0] = const_cast<LPSTR> (szOID_PKIX_KP_CODE_SIGNING);
CERT_ENHKEY_USAGE certEKU;
certEKU.cUsageIdentifier = 1;
certEKU.rgpszUsageIdentifier = &lpEKU[0];
DWORD cbEncodedEKU = 0;
PBYTE pbEncodedEKU = NULL;
CERT_NAME_BLOB sib;
sib.cbData = dwEncoded;
sib.pbData = bEncoded;
// encode the key usage definition for use in the cert extension.
if (!CryptEncodeObjectEx(X509_ASN_ENCODING,
szOID_ENHANCED_KEY_USAGE,
&certEKU,
CRYPT_ENCODE_ALLOC_FLAG,
NULL,
&pbEncodedEKU,
&cbEncodedEKU))
{
throw WinCryptError("CryptEncodeObjectEx failed.", GetLastError());
}
// place the encoded key usage in the cert extension
CERT_EXTENSIONS certExts;
CERT_EXTENSION certExt[1];
certExt[0].pszObjId = (LPSTR)szOID_ENHANCED_KEY_USAGE;
certExt[0].fCritical = FALSE;
certExt[0].Value.cbData = cbEncodedEKU;
certExt[0].Value.pbData = pbEncodedEKU;
certExts.cExtension = 1;
certExts.rgExtension = certExt;
CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm;
ZeroMemory(&SignatureAlgorithm, sizeof(SignatureAlgorithm));
SignatureAlgorithm.pszObjId = (LPSTR)szOID_RSA_SHA256RSA;
// create the self signed certificate
pc = CertCreateSelfSignCertificate(hProv, &sib, 0, &kpi, &SignatureAlgorithm, NULL, &et, &certExts);
BYTE* pbEncodedCert = NULL;
DWORD cbEncodedCert = 0;
CertSerializeCertificateStoreElement(pc, 0, NULL, &cbEncodedCert);
std::vector<unsigned char> cert = std::vector<unsigned char>(pc->pbCertEncoded, pc->pbCertEncoded + pc->cbCertEncoded);
encoded_cert = encoders::base64_encode(cert);
if (hKey != NULL)
CryptDestroyKey(hKey);
if (pbEncodedEKU != NULL) {
LocalFree(pbEncodedEKU);
}
CertFreeCertificateContext(pc);
CryptReleaseContext(hProv, 0);
return encoded_cert;
}