std::vector BcryptECDiffieHellman::ExportPkcs8PrivateKey()

in azure-protected-vm-secrets/Windows/BcryptECDiffieHellman.cpp [321:562]


std::vector<unsigned char> BcryptECDiffieHellman::ExportPkcs8PrivateKey() const
{
	std::vector<unsigned char> result;
#ifndef PLATFORM_UNIX
	DWORD privateKeyBlobLen = 0;
	NTSTATUS status;
    CRYPT_ECC_PRIVATE_KEY_INFO eccPrivateInfo = { 0 };
    CRYPT_PRIVATE_KEY_INFO* privateKeyInfo = nullptr;
    DWORD privateKeyInfoLen = 0;
    BYTE* pbCurveParameters = nullptr;
    DWORD cbCurveParameters = 0;
    BCRYPT_ECCKEY_BLOB* pekb = nullptr;
    // Get the size of the private key blob
	status = BCryptExportKey(this->hEccKeyHandle, NULL, BCRYPT_ECCPRIVATE_BLOB, NULL, 0, &privateKeyBlobLen, 0);
    if (status != STATUS_SUCCESS) {
		// LibraryError, Bcrypt subclass, keyError
		throw BcryptError(status, "BCryptExportKey - Get Size - failed.\n",
            ErrorCode::LibraryError_Bcrypt_keyError);
	}
    unsigned char* privateKeyBlob = (unsigned char *)malloc(privateKeyBlobLen);
    // Export the private key blob
	status = BCryptExportKey(this->hEccKeyHandle, NULL, BCRYPT_ECCPRIVATE_BLOB, privateKeyBlob, privateKeyBlobLen, &privateKeyBlobLen, 0);
    if (status != STATUS_SUCCESS) {
		// LibraryError, Bcrypt subclass, keyError
		throw BcryptError(status, "BCryptExportKey - Export Key - failed.\n",
            ErrorCode::LibraryError_Bcrypt_keyError);
	}
    pekb = (BCRYPT_ECCKEY_BLOB*)privateKeyBlob;
    // Get the size of the curve parameters
    status = BCryptGetProperty(
        this->hEccKeyHandle,
        BCRYPT_ECC_PARAMETERS,
        NULL,                       // pbOutput
        0,                          // cbOutput
        &cbCurveParameters,
        0                           // dwFlags
    );
    if (status != STATUS_SUCCESS) {
		// LibraryError, Bcrypt subclass, propertyError
        throw BcryptError(status, "BCryptGetProperty Size failed.\n",
            ErrorCode::LibraryError_Bcrypt_propertyError);
    }
    // Get the curve parameters
    pbCurveParameters = (BYTE*)malloc(cbCurveParameters);
    status = BCryptGetProperty(
		this->hEccKeyHandle,
		BCRYPT_ECC_PARAMETERS,
		pbCurveParameters,
		cbCurveParameters,
		&cbCurveParameters,
		0);
    if (status != STATUS_SUCCESS) {
		// LibraryError, Bcrypt subclass, propertyError
        throw BcryptError(status, "BCryptGetProperty Value failed.\n",
            ErrorCode::LibraryError_Bcrypt_propertyError);
    }

    CRYPT_DATA_BLOB CNGECCBlob = { 0 };
	CNGECCBlob.cbData = cbCurveParameters;
    CNGECCBlob.pbData = pbCurveParameters;
    ULONG cbCurveInfo = 0;
    // Get the size of the curve info
    if (!CryptEncodeObject(
        X509_ASN_ENCODING,
        X509_ECC_PARAMETERS,
        &CNGECCBlob,
        NULL,
        &cbCurveInfo))
    {
        // ParsingError, ASN subclass, x509PrivKeyError
        throw WinCryptError("CryptEncodeObject 1 failed.", GetLastError(),
            ErrorCode::ParsingError_Asn1_x509PrivKeyError);
	}

    eccPrivateInfo.dwVersion = CRYPT_ECC_PRIVATE_KEY_INFO_v1;
    eccPrivateInfo.szCurveOid = NULL;
    eccPrivateInfo.PrivateKey.pbData = (BYTE*)(pekb + 1) + EC_PUBLIC_NUM_COMPONENTS * pekb->cbKey;
    eccPrivateInfo.PrivateKey.cbData = pekb->cbKey;
    ULONG cbPublicKey;
    BYTE* pbPublicKey;

    pbPublicKey = (BYTE*)&pekb[1];
    cbPublicKey = pekb->cbKey;

    eccPrivateInfo.PublicKey.cbData = 1 + 2 * cbPublicKey;
    eccPrivateInfo.PublicKey.cUnusedBits = 0;
	eccPrivateInfo.PublicKey.pbData = (BYTE*)malloc(eccPrivateInfo.PublicKey.cbData);
    ZeroMemory(eccPrivateInfo.PublicKey.pbData, eccPrivateInfo.PublicKey.cbData);
    eccPrivateInfo.PublicKey.pbData[0] = EC_UNCOMPRESSED_BLOB;
    CopyMemory(
        eccPrivateInfo.PublicKey.pbData + 1,
        pbPublicKey,
        cbPublicKey);
    CopyMemory(
        eccPrivateInfo.PublicKey.pbData + 1 + cbPublicKey,
        pbPublicKey + pekb->cbKey,
        cbPublicKey);
    CRYPT_BIT_BLOB CryptBitBlob = { 0 };
    CryptBitBlob.cbData = 1;
    CryptBitBlob.pbData = (BYTE*)malloc(1);
    CryptBitBlob.pbData[0] = CERT_KEY_AGREEMENT_KEY_USAGE;
    CryptBitBlob.cUnusedBits = 0;

    ULONG cbKeyUsage = 0;
    ULONG cbEncodedKey = 0;
    // Get the size of the key
    if (!CryptEncodeObjectEx(
        X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
        X509_ECC_PRIVATE_KEY,
        (BYTE*)(&eccPrivateInfo),
        0,
        NULL,
        NULL, &cbEncodedKey))
    {
        // ParsingError, ASN subclass, x509PrivKeyError
        throw WinCryptError("CryptEncodeObjectEx 1 failed.", GetLastError(),
            ErrorCode::ParsingError_Asn1_x509PrivKeyError);
    }

    ULONG cbPriKeyInfo = cbPriKeyInfo = sizeof(*privateKeyInfo) + sizeof(szOID_ECC_PUBLIC_KEY);
    if (cbKeyUsage > 0)
    {
        cbPriKeyInfo +=
            sizeof(CRYPT_ATTRIBUTES) +
            sizeof(CRYPT_ATTRIBUTE) +
            sizeof(CRYPT_ATTR_BLOB) +
            sizeof(szOID_KEY_USAGE);
    }

    cbPriKeyInfo += cbCurveInfo;
    cbPriKeyInfo += cbKeyUsage;
    cbPriKeyInfo += cbEncodedKey;

    BYTE* pb = NULL;
    privateKeyInfo = (PCRYPT_PRIVATE_KEY_INFO)malloc(cbPriKeyInfo);
    if (privateKeyInfo == NULL) {
        // GenericError, Memory subclass, mallocError
        throw std::exception("ECDH Malloc Fail");
    }
    ZeroMemory(privateKeyInfo, cbPriKeyInfo);

    if (cbKeyUsage > 0)
    {
        CRYPT_ATTRIBUTES* pAttrList = (CRYPT_ATTRIBUTES*)(privateKeyInfo + 1);
        CRYPT_ATTRIBUTE* pAttr = (CRYPT_ATTRIBUTE*)(pAttrList + 1);
        CRYPT_ATTR_BLOB* pAttrBlob = (CRYPT_ATTR_BLOB*)(pAttr + 1);

        privateKeyInfo->pAttributes = pAttrList;

        pAttrList->cAttr = 1;
        pAttrList->rgAttr = pAttr;

        pAttr->pszObjId = (PSTR)(pAttrBlob + 1);
        pAttr->cValue = 1;
        pAttr->rgValue = pAttrBlob;

        pAttrBlob->cbData = cbKeyUsage;
        pAttrBlob->pbData =
            (BYTE*)(pAttr->pszObjId + sizeof(szOID_KEY_USAGE));

        CopyMemory(
            pAttr->pszObjId,
            szOID_KEY_USAGE,
            sizeof(szOID_KEY_USAGE));

        if (!CryptEncodeObject(
            X509_ASN_ENCODING,
            X509_BITS,
            &CryptBitBlob,
            pAttrBlob->pbData,
            &pAttrBlob->cbData))
        {
            // ParsingError, ASN subclass, x509PrivKeyError
            throw WinCryptError("CryptEncodeObjectEx 2 failed.", GetLastError(),
                ErrorCode::ParsingError_Asn1_x509PrivKeyError);
        }

        pb = pAttrBlob->pbData + pAttrBlob->cbData;
    }
    else
    {
        pb = (BYTE*)(privateKeyInfo + 1);
    }

    privateKeyInfo->Algorithm.Parameters.cbData = cbCurveInfo;
    privateKeyInfo->Algorithm.Parameters.pbData = pb + cbEncodedKey;
    privateKeyInfo->Algorithm.pszObjId =
        (PSTR)(pb + cbEncodedKey + cbCurveInfo);

    privateKeyInfo->PrivateKey.cbData = cbEncodedKey;
    privateKeyInfo->PrivateKey.pbData = pb;
    // Encode the key
    if (!CryptEncodeObjectEx(
        X509_ASN_ENCODING,
        X509_ECC_PRIVATE_KEY,
        &eccPrivateInfo,
        0,
        NULL,
        privateKeyInfo->PrivateKey.pbData,
        &privateKeyInfo->PrivateKey.cbData))
    {
        // ParsingError, ASN subclass, x509PrivKeyError
        throw WinCryptError("CryptEncodeObjectEx 3 failed.", GetLastError(),
            ErrorCode::ParsingError_Asn1_x509PrivKeyError);
    }
    // Encode the curve parameters
    if (!CryptEncodeObject(
        X509_ASN_ENCODING,
        X509_ECC_PARAMETERS,
        &CNGECCBlob,
        privateKeyInfo->Algorithm.Parameters.pbData,
        &privateKeyInfo->Algorithm.Parameters.cbData))
    {
        // ParsingError, ASN subclass, x509PrivKeyError    
        throw WinCryptError("CryptEncodeObjectEx 4 failed.", GetLastError(),
            ErrorCode::ParsingError_Asn1_x509PrivKeyError);
    }

    CopyMemory(
        privateKeyInfo->Algorithm.pszObjId,
        szOID_ECC_PUBLIC_KEY,
        sizeof(szOID_ECC_PUBLIC_KEY));
    ULONG derPrivateBytesLen = 0;
    unsigned char *derPrivateBytes = nullptr;
    // Encode the private key
    if (!CryptEncodeObjectEx(
        X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
        PKCS_PRIVATE_KEY_INFO,
        (BYTE*)privateKeyInfo,
        CRYPT_ENCODE_ALLOC_FLAG,
        NULL,
        &derPrivateBytes, &derPrivateBytesLen))
    {
        // ParsingError, ASN subclass, x509PrivKeyError
        throw WinCryptError("CryptEncodeObjectEx 5 failed.", GetLastError(),
            ErrorCode::ParsingError_Asn1_x509PrivKeyError);
    }
    
	result = std::vector<unsigned char>(derPrivateBytes, derPrivateBytes + derPrivateBytesLen);
#endif // !PLATFORM_UNIX
    return result;
}