cryptographycryptoapicngwincrypt

PFXExportCertStoreEx API not exporting private key to PFX file


I am trying to create creating a pfx file using PFXExportCertStoreEx API for a self-signed certificate and its corresponding private key.

Self-signed Certificate exporting to pfx but private key not exporting to a pfx file. I set the export policy for the private key as below.

 export_policy = NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;

I tried different approaches as below, in both cases PFXExportCertStoreEx API failing with error code 0x80090016.

Approach 1: Calling the API CertSetCertificateContextProperty with CERT_KEY_PROV_INFO_PROP_ID as mentioed here

Approach 2: Calling the API CertSetCertificateContextProperty with CERT_NCRYPT_KEY_HANDLE_PROP_ID and keyHandle

I am using the below code to create a private key, self-signed certificate, and export to the pfx file.

#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>
#include <strsafe.h>
#include<ncrypt.h>
#pragma comment (lib, "crypt32")
#pragma comment(lib, "ncrypt.lib") 

int main()
{
    CreateCert();
    return 0;
}

int CreateCert(void)
{
    int result = 0;
    CERT_NAME_BLOB nameBlob = {0, NULL};
    CERT_EXTENSIONS certExtensions = { 0 };
    NCRYPT_PROV_HANDLE providerHandle = { 0 };
    NCRYPT_KEY_HANDLE keyHandle = { 0 };
    PCCERT_CONTEXT certContext = NULL;
    ZeroMemory(&certExtensions,sizeof(certExtensions));
    
    CRYPT_KEY_PROV_INFO keyProvInfo;
    
    ZeroMemory(&keyProvInfo,sizeof(keyProvInfo));

    keyProvInfo.pwszContainerName = L"Label1_Key";
    keyProvInfo.dwProvType = PROV_RSA_FULL;
    keyProvInfo.dwFlags = CRYPT_MACHINE_KEYSET;
    keyProvInfo.dwKeySpec = AT_SIGNATURE;

    if (NCryptOpenStorageProvider(&providerHandle,
                                  MS_KEY_STORAGE_PROVIDER,
                                  0) != 0)
    {
        printf("\nFailed NCryptOpenStorageProvider");
        goto fail;
    }

    if (NCryptCreatePersistedKey(providerHandle,
                                 &keyHandle,
                                 BCRYPT_RSA_ALGORITHM,
                                 L"Label1_Key",
                                 AT_SIGNATURE,
                                 NCRYPT_OVERWRITE_KEY_FLAG) != 0)
    {
        printf("\nFailed NCryptCreatePersistedKey");
        goto fail;
    }
    NTSTATUS status = -1;
    DWORD dwBits = 2048;
    status = NCryptSetProperty(
                    keyHandle,
                    NCRYPT_LENGTH_PROPERTY,
                    (PBYTE) &dwBits,
                    sizeof(dwBits),
                    NCRYPT_PERSIST_FLAG
                );
    if( status )
    {
        printf("\nFailed NCryptSetProperty to key size");
        goto fail;
    }
    DWORD export_policy = NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
    if (NCryptSetProperty(
                keyHandle,
                NCRYPT_EXPORT_POLICY_PROPERTY,
                (PBYTE)&export_policy,
                sizeof(export_policy),
                NCRYPT_PERSIST_FLAG))
    {
        printf("\nFailed NCryptSetProperty to export policy");
        goto fail;
    }
    
    if (NCryptFinalizeKey(keyHandle, 
                          NCRYPT_SILENT_FLAG) != 0)
    {
        printf("\nFailed NCryptFinalizeKey");
        goto fail;
    }

    if (!CertStrToNameW(X509_ASN_ENCODING, 
                L"CN=Label1", 
                0, 
                NULL, 
                nameBlob.pbData,
                &nameBlob.cbData, 
                NULL))
    {
        printf("\nFailed CertStrToNameW 1");
        goto fail;
    }

    nameBlob.pbData = malloc(nameBlob.cbData);
    if (!CertStrToNameW(X509_ASN_ENCODING, 
                        L"CN=Label1", 
                        0, 
                        NULL, 
                        nameBlob.pbData,
                        &nameBlob.cbData, NULL))
    {
        printf("\nFailed CertStrToNameW 2");
        goto fail;
    }
    SYSTEMTIME EndTime;
    SYSTEMTIME StartTime;
    GetSystemTime(&StartTime);
    GetSystemTime(&EndTime);

    EndTime.wYear += 10;
    
    certContext = CertCreateSelfSignCertificate(
                    keyHandle, 
                    &nameBlob, 
                    0,
                    &keyProvInfo,
                    NULL,
                    &StartTime, 
                    &EndTime, 
                    &certExtensions
                );
    if (!certContext)
    {
        printf("\nFailed CertCreateSelfSignCertificate");
        goto fail;
    }
    CRYPT_DATA_BLOB  Friendly_Name_Blob;
    ZeroMemory( &Friendly_Name_Blob, sizeof(Friendly_Name_Blob) );
    

    Friendly_Name_Blob.pbData = (BYTE *)L"Temp Name";
    Friendly_Name_Blob.cbData =  (wcslen((LPWSTR)Friendly_Name_Blob.pbData)+1) * sizeof(WCHAR);;
    if(CertSetCertificateContextProperty(
                certContext,  
                CERT_FRIENDLY_NAME_PROP_ID,      
                0,
                &Friendly_Name_Blob))
    {
        printf("A name has been set.\n");
    }
    else
    {
        printf("The display name was not set.\n");
    }
    
    CRYPT_KEY_PROV_INFO kpi;
    ZeroMemory(&kpi, sizeof(kpi) );
    kpi.pwszContainerName = L"Label1_Key";
    kpi.dwProvType = PROV_RSA_FULL;
    kpi.dwKeySpec = AT_KEYEXCHANGE;
    kpi.dwFlags = CRYPT_MACHINE_KEYSET;

    if(CertSetCertificateContextProperty(certContext,
                                      CERT_KEY_PROV_INFO_PROP_ID,
                                      0, 
                                      (const void *)&kpi))
    {
        printf("Key set.\n");
    }
    else
    {
        printf("Key set faile\n");
        goto fail;
    }
    
    // if(CertSetCertificateContextProperty(certContext,
                                      // CERT_NCRYPT_KEY_HANDLE_PROP_ID,
                                      // 0, 
                                      // (const void *)&keyHandle))
    // {
        // printf("Key set.\n");
    // }
    // else
    // {
        // printf("Key set faile\n");
        // goto fail;
    // }
    
    
    PCCERT_CONTEXT pCertContext = NULL;
    HCERTSTORE hMemStore = NULL;
    CRYPT_DATA_BLOB Blob;
    hMemStore = CertOpenStore(CERT_STORE_PROV_MEMORY,
                              0,
                              (HCRYPTPROV_LEGACY)NULL,
                              CERT_STORE_CREATE_NEW_FLAG,
                              NULL);
    if(!hMemStore) 
    {
        printf("Error creating memory certificate store: %d\n",GetLastError());
        goto fail;
    }
    
    if(!CertAddCertificateContextToStore(hMemStore,
                                        certContext,
                                        CERT_STORE_ADD_ALWAYS,
                                        NULL)) 
    {
        printf("Error adding certificate to memory certificate store: %d\n",GetLastError());
        goto fail;
    }

    ZeroMemory(&Blob,sizeof(CRYPT_DATA_BLOB));
    if (!PFXExportCertStoreEx(hMemStore,
                                &Blob,
                                L"mypassword",
                                NULL,
                                EXPORT_PRIVATE_KEYS | 
                                REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY | 
                                PKCS12_INCLUDE_EXTENDED_PROPERTIES |
                                REPORT_NO_PRIVATE_KEY)) 
    {
        printf("Error sizing blob: 0x%x \n", GetLastError());
        goto fail;
    }

    Blob.pbData = (PBYTE)HeapAlloc(GetProcessHeap(),0,Blob.cbData);
    if(!Blob.pbData)
    {
        printf("Error allocating data blob: %d\n", GetLastError());
        goto fail;
    }

    if(!PFXExportCertStoreEx(hMemStore,
                                &Blob,
                                L"mypassword",
                                NULL,
                                EXPORT_PRIVATE_KEYS |
                                REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY |
                                PKCS12_INCLUDE_EXTENDED_PROPERTIES |
                                REPORT_NO_PRIVATE_KEY))
    {
        printf("Error exporting certificates: %d\n",GetLastError());
        goto fail;
    }
    HANDLE hFile = NULL;
    DWORD dwBytesWritten = 0;
    hFile = CreateFile("Certificate.pfx",GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,0);
    if(hFile == INVALID_HANDLE_VALUE) {
        printf("Error creating output file: %d\n", GetLastError());
        goto fail;
    }

    if(!WriteFile(hFile,Blob.pbData,Blob.cbData,&dwBytesWritten,0)) {
        printf("Error writing to file: %d\n", GetLastError());
        goto fail;
    }

    if (dwBytesWritten != Blob.cbData) {
        printf("Number of bytes written does not match requested!\n");
        goto fail;
    }
    printf("\nCertificate Created Successfully");
fail:
    free(nameBlob.pbData);
    if (providerHandle)
        NCryptFreeObject(providerHandle);
    if (certContext)
        CertFreeCertificateContext(certContext);
    if(hMemStore) 
        CertCloseStore(hMemStore, 0);
    if(pCertContext) 
        CertFreeCertificateContext(pCertContext);
    if(hFile != INVALID_HANDLE_VALUE)
    {
        CloseHandle(hFile);
    }

    if(Blob.pbData) HeapFree(GetProcessHeap(),0,Blob.pbData);
    return result;

}

Let me know how can I export the private key to the pfx file along with a self-signed certificate.

Thanks in advance.


Solution

  • Changed the above code to as below then private key and certificate are exporting to a pfx file successfully.

    befre calling NCryptOpenStorageProvider.

     memset(&keyProvInfo, 0, sizeof(keyProvInfo));
     keyProvInfo.pwszContainerName = L"Label_key1";;
     keyProvInfo.pwszProvName = MS_KEY_STORAGE_PROVIDER;
     keyProvInfo.dwProvType = 0;
     keyProvInfo.dwKeySpec = 0;
    

    After calling CertCreateSelfSignCertificate add below code

    if(CertSetCertificateContextProperty(certContext,
       CERT_NCRYPT_KEY_HANDLE_PROP_ID,
       0, 
       (const void *)&keyHandle))
     {
     printf("Key set.\n");
     }
     else
     {
     printf("Key set faile\n");
     goto fail;
     }
    

    See the complete code below.

    #include <stdio.h>
    #include <windows.h>
    #include <wincrypt.h>
    #include <strsafe.h>
    #include <ncrypt.h>
    #pragma comment(lib, "crypt32")
    #pragma comment(lib, "ncrypt.lib")
    
    int main()
    {
        CreateCert();
        return 0;
    }
    
    int CreateCert(void)
    {
        int result = 0;
        CERT_NAME_BLOB nameBlob = {0, NULL};
        CERT_EXTENSIONS certExtensions = {0};
        NCRYPT_PROV_HANDLE providerHandle = {0};
        NCRYPT_KEY_HANDLE keyHandle = {0};
        PCCERT_CONTEXT certContext = NULL;
        ZeroMemory(&certExtensions, sizeof(certExtensions));
    
        CRYPT_KEY_PROV_INFO keyProvInfo;
    
        memset(&keyProvInfo, 0, sizeof(keyProvInfo));
        keyProvInfo.pwszContainerName = L"Label_key1";
        
        keyProvInfo.pwszProvName = MS_KEY_STORAGE_PROVIDER;
        keyProvInfo.dwProvType = 0;
        keyProvInfo.dwKeySpec = 0;
    
        if (NCryptOpenStorageProvider(&providerHandle,
                                      MS_KEY_STORAGE_PROVIDER,
                                      0) != 0)
        {
            printf("\nFailed NCryptOpenStorageProvider");
            goto fail;
        }
    
        if (NCryptCreatePersistedKey(providerHandle,
                                     &keyHandle,
                                     BCRYPT_RSA_ALGORITHM,
                                     keyProvInfo.pwszContainerName,
                                     AT_SIGNATURE,
                                     NCRYPT_OVERWRITE_KEY_FLAG) != 0)
        {
            printf("\nFailed NCryptCreatePersistedKey");
            goto fail;
        }
        NTSTATUS status = -1;
        DWORD dwBits = 2048;
        status = NCryptSetProperty(
            keyHandle,
            NCRYPT_LENGTH_PROPERTY,
            (PBYTE)&dwBits,
            sizeof(dwBits),
            NCRYPT_PERSIST_FLAG);
        if (status)
        {
            printf("\nFailed NCryptSetProperty to key size");
            goto fail;
        }
        DWORD export_policy = NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
        if (NCryptSetProperty(
                keyHandle,
                NCRYPT_EXPORT_POLICY_PROPERTY,
                (PBYTE)&export_policy,
                sizeof(export_policy),
                NCRYPT_PERSIST_FLAG))
        {
            printf("\nFailed NCryptSetProperty to export policy");
            goto fail;
        }
    
        if (NCryptFinalizeKey(keyHandle,
                              NCRYPT_SILENT_FLAG) != 0)
        {
            printf("\nFailed NCryptFinalizeKey");
            goto fail;
        }
    
        if (!CertStrToNameW(X509_ASN_ENCODING,
                            L"CN=Label1",
                            0,
                            NULL,
                            nameBlob.pbData,
                            &nameBlob.cbData,
                            NULL))
        {
            printf("\nFailed CertStrToNameW 1");
            goto fail;
        }
    
        nameBlob.pbData = malloc(nameBlob.cbData);
        if (!CertStrToNameW(X509_ASN_ENCODING,
                            L"CN=Label1",
                            0,
                            NULL,
                            nameBlob.pbData,
                            &nameBlob.cbData, NULL))
        {
            printf("\nFailed CertStrToNameW 2");
            goto fail;
        }
        SYSTEMTIME EndTime;
        SYSTEMTIME StartTime;
        GetSystemTime(&StartTime);
        GetSystemTime(&EndTime);
    
        EndTime.wYear += 10;
    
        certContext = CertCreateSelfSignCertificate(
            keyHandle,
            &nameBlob,
            0,
            &keyProvInfo,
            NULL,
            &StartTime,
            &EndTime,
            &certExtensions);
        if (!certContext)
        {
            printf("\nFailed CertCreateSelfSignCertificate");
            goto fail;
        }
    
        if (CertSetCertificateContextProperty(certContext,
                                              CERT_NCRYPT_KEY_HANDLE_PROP_ID,
                                              0,
                                              (const void *)&keyHandle))
        {
            printf("Key set.\n");
        }
        else
        {
            printf("Key set faile\n");
            goto fail;
        }
    
        CRYPT_DATA_BLOB Friendly_Name_Blob;
        ZeroMemory(&Friendly_Name_Blob, sizeof(Friendly_Name_Blob));
    
        Friendly_Name_Blob.pbData = (BYTE *)L"Temp Name";
        Friendly_Name_Blob.cbData = (wcslen((LPWSTR)Friendly_Name_Blob.pbData) + 1) * sizeof(WCHAR);
        ;
        if (CertSetCertificateContextProperty(
                certContext,
                CERT_FRIENDLY_NAME_PROP_ID,
                0,
                &Friendly_Name_Blob))
        {
            printf("A name has been set.\n");
        }
        else
        {
            printf("The display name was not set.\n");
        }
    
        PCCERT_CONTEXT pCertContext = NULL;
        HCERTSTORE hMemStore = NULL;
        CRYPT_DATA_BLOB Blob;
        hMemStore = CertOpenStore(CERT_STORE_PROV_MEMORY,
                                  0,
                                  (HCRYPTPROV_LEGACY)NULL,
                                  CERT_STORE_CREATE_NEW_FLAG,
                                  NULL);
        if (!hMemStore)
        {
            printf("Error creating memory certificate store: %d\n", GetLastError());
            goto fail;
        }
    
        if (!CertAddCertificateContextToStore(hMemStore,
                                              certContext,
                                              CERT_STORE_ADD_ALWAYS,
                                              NULL))
        {
            printf("Error adding certificate to memory certificate store: %d\n", GetLastError());
            goto fail;
        }
    
        ZeroMemory(&Blob, sizeof(CRYPT_DATA_BLOB));
        if (!PFXExportCertStoreEx(hMemStore,
                                  &Blob,
                                  L"mypassword",
                                  NULL,
                                  EXPORT_PRIVATE_KEYS |
                                      REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY |
                                      PKCS12_INCLUDE_EXTENDED_PROPERTIES |
                                      REPORT_NO_PRIVATE_KEY))
        {
            printf("Error sizing blob: 0x%x \n", GetLastError());
            goto fail;
        }
    
        Blob.pbData = (PBYTE)HeapAlloc(GetProcessHeap(), 0, Blob.cbData);
        if (!Blob.pbData)
        {
            printf("Error allocating data blob: %d\n", GetLastError());
            goto fail;
        }
    
        if (!PFXExportCertStoreEx(hMemStore,
                                  &Blob,
                                  L"mypassword",
                                  NULL,
                                  EXPORT_PRIVATE_KEYS |
                                      REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY |
                                      PKCS12_INCLUDE_EXTENDED_PROPERTIES |
                                      REPORT_NO_PRIVATE_KEY))
        {
            printf("Error exporting certificates: %d\n", GetLastError());
            goto fail;
        }
        HANDLE hFile = NULL;
        DWORD dwBytesWritten = 0;
        hFile = CreateFile("Certificate.pfx", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
        if (hFile == INVALID_HANDLE_VALUE)
        {
            printf("Error creating output file: %d\n", GetLastError());
            goto fail;
        }
    
        if (!WriteFile(hFile, Blob.pbData, Blob.cbData, &dwBytesWritten, 0))
        {
            printf("Error writing to file: %d\n", GetLastError());
            goto fail;
        }
    
        if (dwBytesWritten != Blob.cbData)
        {
            printf("Number of bytes written does not match requested!\n");
            goto fail;
        }
        printf("\nCertificate Created Successfully");
    fail:
        free(nameBlob.pbData);
        if (providerHandle)
            NCryptFreeObject(providerHandle);
        if (certContext)
            CertFreeCertificateContext(certContext);
        if (hMemStore)
            CertCloseStore(hMemStore, 0);
        if (pCertContext)
            CertFreeCertificateContext(pCertContext);
        if (hFile != INVALID_HANDLE_VALUE)
        {
            CloseHandle(hFile);
        }
    
        if (Blob.pbData)
            HeapFree(GetProcessHeap(), 0, Blob.pbData);
        return result;
    }