2
votes

I am reposting my question from the MSDN forums here.

This problem 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.

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

Edit

I would like to emphasize that I need a non interactive procedure to copy the machine certificate from LocalComputer\My to MSMQ\My.

1
You are sure the file is properly read into memory? I would step in a debugger and verify that everything up til the call to PFXImportCertStore works.Andreas Magnusson

1 Answers

1
votes

Instead of using a program I would try to use the certificate store mmc plugin:

  • Start mmc.exe, this will give you the MMC in authoring mode.
  • Click File -> Add/remove Snap-in
  • Find and double-click "Certificates". This will pop up a dialog where you can select which certificate storage you want to administer.
  • I think "Service Account" is the appropriate selection for you here
  • Click next
  • Select local if you are on il-mark-lap already, otherwise select "Another computer". AFAIK, you will have to be logged in with a domain administrator account for this to work though.
  • Click next
  • Select your service
  • click finish.

At this point you should already be able to access the certificate storage for your service. If you want to do this more often then I suggest you do this as well:

  • Right-click "Certificates" in the tree and select "New Window from Here"
  • Switch back to the console root window and close it
  • Save your work of art to a .msc file

Edit

Before you save, select "Certificates" from the tree, and then View -> Options. Here you can select to see the "Physical Certificate stores" if you like.