locked
PFXImportCertStore API fails to import a PFX failed exported from the certificate store RRS feed

  • Question

  • Dear ladies and sirs.

    I have an acute problem. It has to do with importing a certificate into the personal certificate store associated with a windows service.

    The name of my machine is il-mark-lap (the machine is pingable by this name).

    The process:

    1. There is a self signed authority certificate, let us call it NCCA . Its private key lives on another machine, let us refer it by dev-profiler .

    dev-profiler> makecert -n "CN=NCCA" -sr localmachine -ss root -a sha1 -cy authority -r -sv NCCA.pvk NCCA.cer

    2. The il-mark-lap machine certificate is created on dev-profiler and imported to the LocalComputer\My certificate store on il-mark-lap . Note, that the authority certificate (NCCA ) has to be moved to LocalComputer\Root certificate store, but since I do not know how to move, I use the export-delete-import sequence.

    dev-profiler> makecert -n "CN=il-mark-lap" -sr CurrentUser -ss My -cy end -pe -sky exchange -a sha1 -is Root -ir LocalMachine -in NCCA
    dev-profiler> certutil -user -exportpfx -p 123 il-mark-lap il-mark-lap.pfx
    dev-profiler> certutil -user -delstore My il-mark-lap

    il-mark-lap> cscript CStore.vbs import -l LM -s My -e il-mark-lap.pfx 123
    il-mark-lap> cscript CStore.vbs export -l LM -s My -subject NCCA NCCA.cer
    il-mark-lap> cscript CStore.vbs delete -noprompt -l LM -subject NCCA My
    il-mark-lap> cscript CStore.vbs import -l LM -s Root NCCA.cer

    3. The il-mark-lap machine certificate is copied from LocalComputer\My certificate store into MSMQ\My certificate store (Message Queuing service Personal certificate store). Again, I do not know how to copy, so I use the export-import sequence.

    il-mark-lap> cscript CStore.vbs export -l LM -s My -subject il-mark-lap tmp.pfx
    il-mark-lap> ImportPfxIntoSrvCertStore MSMQ tmp.pfx 123

    Where ImportPfxIntoSrvCertStore is my program written in C++ to import the given PFX into the Personal certificate store of the given service, MSMQ in my case.

    Omitting all the error handling, the relevant  C++ code is this:

    CSafeHandle pfxFileHandle(::CreateFile(wszPfxFilePath, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0));
    CSafeHandle pfxFileMapping(::CreateFileMapping(pfxFileHandle, 0, PAGE_READONLY, 0, 0, 0));
    CSafeMapViewOfFile pfxFileBuffer(::MapViewOfFile(pfxFileMapping, FILE_MAP_READ, 0, 0, 0));
    
    CRYPT_DATA_BLOB blob;
    blob.cbData = ::GetFileSize(pfxFileHandle, 0);
    blob.pbData = LPBYTE(LPVOID(pfxFileBuffer));
    
    CSafeCertStoreHandle pfxStore(::PFXImportCertStore(&blob, wszPassword, CRYPT_MACHINE_KEYSET | CRYPT_EXPORTABLE));
    CSafeCertStoreHandle serviceStore(::CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_SERVICES, wszCertificateStoreName));
     
    PCCERT_CONTEXT pctx = NULL;
    while
     (NULL != (pctx = ::CertEnumCertificatesInStore(pfxStore, pctx)))
    {
     ::CertAddCertificateContextToStore(serviceStore, pctx, CERT_STORE_ADD_REPLACE_EXISTING, NULL);
    }
    

    Ignore the CSafeXXXHandle and CSafeMapViewOfFile classes, these are simple handle/buffer holders, releasing the respective handle/buffer in the destructor ("resource acquisition is initialization" design pattern).

    Anyway, the PFXImportCertStore API fails with the message An error occurred during encode or decode operation . If I call the PFXIsPFXBlob API, it returns FALSE.

    Here is the Locals debugger view at the start of the code:

    +        wszPfxFilePath    0x00774e0c "tmp.pfx"    const wchar_t *
    +        wszCertificateStoreName    0x002cf7f4 "MSMQ\My"    const wchar_t *
    +        wszPassword    0x00774e1c "123"    const wchar_t *

    So, all the parameters seem to be correct.

    I have no idea what is wrong. The PFX file being imported is absolutely correct, because it is imported flawlessly using the MMC console. Please, help!!!

    Thanks.

    P.S.

    I have based my code on the example found in this article - http://www.codeguru.com/Cpp/I-N/internet/security/article.php/c6211

     

    • Moved by Jesse Jiang Thursday, January 20, 2011 5:54 AM (From:Visual C++ General)
    Tuesday, January 18, 2011 7:45 PM

Answers

  • Well, that doesn't look like a valid PFX file to me, and it's not a Certificate file either.  It ought to begin with bytes similar to

        30 8x

    where x may be 2 or possibly more, depending on how much other data there is, so something has gone wrong.  (In fact, these two bytes should appear at the front of any valid encoding of a SEQUENCE used to represent PKI data.)

    It doesn't look like a base-64 encoding of the value either.  It may be something recognised by mmc, but I don't know what that is.

    Incidentally, there are a number of programs named 'certutil.exe' around; are you using the correct one?

    Finally, did you try the memory-based certificate store I previously suggested?

    * Note for DER-gurus: I know this is simplifying the length field.


    Answering policy: see profile.
    • Marked as answer by Markell Tuesday, February 8, 2011 8:17 AM
    Thursday, February 3, 2011 11:49 AM

All replies

  • The error message suggests that something is wrong with the decrypting of the private key. 

    The thing that springs to my mind is the possibility that the password used by certutil to export the pfx was treated as consisting of narrow characters ("123") where your import program is using a wide character password (L"123").

    Otherwise, check that the length and content of the blob is exactly that of the PFX file, and make sure that you are opening the PFX file in BINARY mode (not TEXT).


    Answering policy: see profile.
    Tuesday, January 18, 2011 10:07 PM
  • Hi.

    I do not think password is the issue. The problem is that PFXIsPFXBlob API return FALSE and it does not accept any password. Besides, the signature of the PFXImportCertStore method is:

    HCERTSTORE WINAPI PFXImportCertStore(
    __in  CRYPT_DATA_BLOB *pPFX,
    __in  LPCWSTR szPassword,
    __in  DWORD dwFlags
    );

    leaving not that many options for the size of the characters using by szPassword.

    The size of the blob is exactly the size of the PFX file on disk. BTW, I am never actually reading the file, so Binary/Text is a non issue here.

    Thanks.

    Wednesday, January 19, 2011 9:30 PM
  • I've not used memory mapped files for this purpose, so I cannot comment further in that direction.

    Side-stepping that issue, you may find PFXExportCertStoreEx Function useful;  with this, you don't need to involve a file at all.  It supports the usual Call-For-Buffer-Size-Allocate-Memory-Call-For-Data pattern for the blob, which can then be passed back to PFXImportCertStore().


    Answering policy: see profile.
    Wednesday, January 19, 2011 11:02 PM
  •  

    Hi Markell,

     

    I think your issue should be raised on Security for Applications in Microsoft Windows I believe they will know more information of this issue then us, and I will move this one to that forum.

     

    Thanks for your understanding,

    Jesse

     


    Jesse Jiang [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Thursday, January 20, 2011 5:54 AM
  • Hi David.

    I am trying to take your suggestion, but there is a problem. It seems to me that PFXExportCertStoreEx exports the whole certificate store rather then a specific certificate. Can you direct me how to export a specific certificate?

    Thanks.

    Sunday, January 23, 2011 12:43 PM
  • One way to achieve that may be to use a temporary memory-based store using CertOpenStore(sz_CERT_STORE_PROV_MEMORY, ...) and then adding a link to the original certificate using CertAddCertificateLinkToStore Function

    This memory store can then be exported using PFXExportCertStoreEx as before.  Don't forget to tidy up afterwards.


    Answering policy: see profile.
    Monday, January 24, 2011 11:51 AM
  • A couple of things I noticed in this code and some questions:

    - Can you display the first 20 bytes or so of the blob.pbData? We can look at the raw data and see if it appears to be a pfx from the first few bytes...  Pkcs12 [pfx] is defined at the topmost layer as:

    //
    //PFX ::= SEQUENCE {
    //     version  INTEGER {v3(3)}(v3,...),
    //     authSafe ContentInfo,
    //     macData     MacData OPTIONAL
    //}

    This is what the PFXIsPFXBlob api would be checking.

    - I'm curous what account the code is running under? It seems like you are trying to create keys in machine context [CRYPT_MACHINE_KEYSET] but the cert is in the MSMQ\My store. I'm not familiar with MSMQ. Will it have access to machine keys? And the account that is running this code, will it be able to create machine keys in a way that MSMQ will be able to access them? I'm also not familiar with acl's on the MSMQ\My store. I assume it won't be a problem to add certs to this service account from the current account context?

     

    Friday, January 28, 2011 4:51 AM
  • Hi Markell,

    is this problem still current for you?


    Answering policy: see profile.
    Wednesday, February 2, 2011 1:09 PM
  • Hi.

    Yes indeed,

    Below is the memory referenced by blob.pbData:

    0x00030000  00 00 00 00 43 45 52 54 18 00 00 00 01 00 00 00  ....CERT........
    0x00030010  10 00 00 00 5f 78 15 97 8a 7a 3d 10 34 19 21 fa  ...._x.—.z=.4.!ת

    I notice the CERT string, so my guess this is indeed the pfx file. But, to be sure I dumped the pfx file using the od command (from unix):

    c:\>od -t x1 tmp.pfx
    0000000 00 00 00 00 43 45 52 54 18 00 00 00 01 00 00 00
    0000020 10 00 00 00 5f 78 15 97 8a 7a 3d 10 34 19 21 fa

    ......

    (only the first two lines are given) Both outputs are identical.

    The same tmp.pfx file is imported fine using the mmc GUI, but according to PFXIsPFXBlob it is not a valid PFX blob.

    The code is run from Visual Studio, which as started by a local admin with elevated privileges.

    However, I am not sure it is at all relevant for the PFXIsPFXBlob API. I have no problem to share the complete source code as well as all the cer, pvk and pfx files if  needed.

    Thanks.

     

    Thursday, February 3, 2011 8:59 AM
  • Well, that doesn't look like a valid PFX file to me, and it's not a Certificate file either.  It ought to begin with bytes similar to

        30 8x

    where x may be 2 or possibly more, depending on how much other data there is, so something has gone wrong.  (In fact, these two bytes should appear at the front of any valid encoding of a SEQUENCE used to represent PKI data.)

    It doesn't look like a base-64 encoding of the value either.  It may be something recognised by mmc, but I don't know what that is.

    Incidentally, there are a number of programs named 'certutil.exe' around; are you using the correct one?

    Finally, did you try the memory-based certificate store I previously suggested?

    * Note for DER-gurus: I know this is simplifying the length field.


    Answering policy: see profile.
    • Marked as answer by Markell Tuesday, February 8, 2011 8:17 AM
    Thursday, February 3, 2011 11:49 AM
  • I have to agree with David that the supplied memory dump does not appear to be a pkcs12 pfx or a netscape format pfx [both of which start with a sequence which means 0x30 should be the first byte.

    The mmc is doing  a CryptQueryObject to figure out what type of file you are actually importing. Most likely, this is not a pfx, but is actually a ".cer" file masquarading with a ".pfx" extension. When you import this file using the mmc, does it ever ask you for a password? Probably not, this is "proof" that the file could not be a pfx.

    What you probably want to do is a CertCreateCertificateContext with the blob of data. This will create a PCCERT_CONTEXT which you can then use to add to your serviceStore.

    Andrew

    • Proposed as answer by Andrew Bernat Sunday, February 6, 2011 8:33 PM
    Sunday, February 6, 2011 4:22 AM
  • Hi David and abernat. Thanks for the replies.

    I am using CStore.vbs script coming with CAPICOM to export the certificate. I understand now that it exports the .cer file rather than .pfx. Thanks folks, I will check your advise.

    Sunday, February 6, 2011 3:44 PM