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