PFXExportCertStoreEx API 未将私钥导出到 PFX 文件

PFXExportCertStoreEx API not exporting private key to PFX file

我正在尝试使用 PFXExportCertStoreEx API 创建一个 pfx 文件以获得自签名证书及其相应的私钥。

导出到 pfx 的自签名证书,但未导出到 pfx 文件的私钥。我将私钥的导出策略设置如下。

 export_policy = NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;

我尝试了如下不同的方法,在这两种情况下 PFXExportCertStoreEx API 失败,错误代码为 0x80090016。

方法一: 使用 CERT_KEY_PROV_INFO_PROP_ID 调用 API CertSetCertificateContextProperty,如前所述 here

方法二: 使用 CERT_NCRYPT_KEY_HANDLE_PROP_ID 和 keyHandle

调用 API CertSetCertificateContextProperty

我正在使用以下代码创建私钥、自签名证书并导出到 pfx 文件。

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

}

请告诉我如何将私钥连同自签名证书一起导出到 pfx 文件。

提前致谢。

将上面的代码更改为如下所示,然后私钥和证书成功导出到 pfx 文件。

在调用 NCryptOpenStorageProvider 之前。

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

调用 CertCreateSelfSignCertificate 后添加以下代码

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

查看下面的完整代码。

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