std::string generate_root_cert()

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;
}