4

I'm using Crypto API's CryptAcquireContext function (https://learn.microsoft.com/en-us/windows/desktop/api/Wincrypt/nf-wincrypt-cryptacquirecontexta) to get access to my Certificate Store contained on my USB Token, and this is working like a charm!

However, the CryptAcquireContext function is deprecated and the Crypto API documentation recommends the use of CNG to achieve the same results. All my problem now is how to use CNG to get a certificate context from my USB Token, and to achieve this I'm using the following code:

var
  Provider: NCRYPT_PROV_HANDLE;
  Reader: PByte;
  ReaderSize: DWORD;
  MemorySize: DWORD;
begin
  // Get a handle to the smartcard reader specific provider
  Status := NCryptOpenStorageProvider(@Provider
                                     ,'SafeSign Standard RSA and AES Cryptographic Service Provider'
                                     ,0); // returns ERROR_SUCCESS
  // Convert the name of the reader to a PByte
  UnicodeStringToBinary('Giesecke & Devrient GmbH StarSign CUT 0',Reader,ReaderSize);

  // Inform the name of the reader to the CNG
  Status := NCryptSetProperty(Provider
                             ,NCRYPT_READER_PROPERTY
                             ,Reader
                             ,ReaderSize
                             ,0); // returns ERROR_SUCCESS

  MemorySize := SizeOf(HCERTSTORE);

  // Try to get the size needed to a variable of type HCERTSTORE.
  // This is the first step before get the certificate store
  Status := NCryptGetProperty(Provider
                             ,NCRYPT_USER_CERTSTORE_PROPERTY
                             ,nil
                             ,0
                             ,@MemorySize
                             ,0); //Returns 0x80090029 (NTE_NOT_SUPPORTED)
end;

As you can see the NCryptGetProperty function fails with error code 0x80090029 which means NTE_NOT_SUPPORTED. What I'm doing wrong? I've found an example (C++) doing the same as me, so, I guess everything is OK with my implementation, but...

My goal is to list all certificates on my smart card (actually an USB Token). I can do this using Crypto API, but the CryptAcquireContext function is deprecated, so, I need to use another one. Using CAPI I get the Certificate Store and I can list It using the default certificate dialog, so, I need, using CNG, get the Certificate Store to do the same thing, but the way I'm doing now seems wrong.

Well, some observations:

  1. I'm not checking the returns here (Status variable) to simplify this code sample
  2. The UnicodeStringToBinary function is strictly correct. The returned buffer (PByte) has twice the size of the original string and the bytes are all "nn 00 nn 00 nn 00", so, the Reader variable contains an Unicode String, just like the NCRYPT_READER_PROPERTY property requires. I can post the code on request.
  3. My NCryptOpenStorageProvider signature is more close of the Windows API version, so, its first argument is a pointer to NCRYPT_PROV_HANDLE
Jason Aller
  • 3,541
  • 28
  • 38
  • 38
  • Some observations: You send pointer when geting handle of the provider `NCryptOpenStorageProvider(@Provider,...)` - Delphi "headers" are usually written in a way that when API takes pointer, var parameter is used, ie user mustn't send aadress of variable. Also, are you sure `NCryptGetProperty(Provider, NCRYPT_USER_CERTSTORE_PROPERTY,...)` is actually supported? The only time I have used CNG API, once I had correct provider I iterated over keys (NCryptEnumKeys) and called `NCryptGetProperty(hKey, NCRYPT_CERTIFICATE_PROPERTY,...)` until right one was found. – ain Jun 28 '18 at 09:23
  • Hi @ain, allow me explain some things about your observations. Yes, I'm using pointers because I changed the header to match the Windows API. The reason for this is purely didatic. It is more easy to convert C/C++ code this way. Actually, my header defines the `NCryptOpenStorageProvider` function as `function NCryptOpenStorageProvider(phProvider: PNCRYPT_PROV_HANDLE; pszProviderName: LPCWSTR; dwFlags: DWORD): SECURITY_STATUS; stdcall;` where `PNCRYPT_PROV_HANDLE = ^NCRYPT_PROV_HANDLE` – Carlos B. Feitoza Filho Jun 28 '18 at 17:34
  • @ain, about your second observation, the answer is "i do not know". Unfortunately this is my very first time with CNG. I was able to use it to generate hashes successfully, but now I'm trying to convert my old Crypto API code to be CNG "compilant" (as CryptAcquireContext function is deprecated, I need to get rid of it). My Intention is to list all certificates from my SmartCard store. This is being done perfectly with Crypto API, but its being dificult with CNG. Do you have some tips? – Carlos B. Feitoza Filho Jun 28 '18 at 17:40
  • I updated the question with more informations – Carlos B. Feitoza Filho Jun 28 '18 at 17:46
  • Do you have functioning C++ code? – David Heffernan Jul 30 '18 at 10:23
  • Hello @DavidHeffernan, and sorry for the late answer. I'v found one code in C++ here: https://github.com/Microsoft/TSS.MSR/blob/master/PCPTool.v11/exe/SDKSample.cpp (on line 7186). I cannot test it (I do not have enough C++ knowledge) to reproduce in C++ but as I can see, I'm on the right way however. I know that the context shown on this code is different form mine, but analysing only my goal (get a certificate store from smart card), I guess I'm doing roughly right – Carlos B. Feitoza Filho Aug 01 '18 at 20:48
  • 1
    Hi. Have you managed to solve your issue? – Bozzy Apr 23 '19 at 07:34
  • @Bozzy unfortunatelly no :( Do you have any clues to solve? Some place to read about? I'm stuck :( – Carlos B. Feitoza Filho Apr 24 '19 at 00:08
  • 1
    I don't know if this can solve your problem or at least help you, but I've got some interesting results. Take a look at this gist: (https://gist.github.com/fperana/826fd2f749b129b5fcfc7ded6405b47b) – Bozzy Apr 24 '19 at 11:02
  • Hi @Bozzy, I'm out of time lately, but I will test it soon, indeed! Thank you! – Carlos B. Feitoza Filho May 01 '19 at 15:03
  • @Bozzy i tried the example but it does not work :( I only want to get a certificate store from an USB Token using CNG, but I do not know why this is being that hard. Using Crypto API is so simple as 1,2,3. As I can achieve my goal using Crypto API I guess there are no problem with my token – Carlos B. Feitoza Filho May 02 '19 at 21:45

1 Answers1

0

I'm not working with delphi... but the reader name must be null-terminated.

I got this working in .NET with Microsoft Smart Card Key Storage Provider and the call:

NCrypt.NCryptSetProperty(
        provider,
        NCrypt.KeyStoragePropertyIdentifiers.NCRYPT_READER_PROPERTY,
        Encoding.Unicode.GetBytes(smartCardReader + '\0')
        );
Dharman
  • 30,962
  • 25
  • 85
  • 135
Daniel Fisher lennybacon
  • 3,865
  • 1
  • 30
  • 38
  • Hi Daniel and thanks for your answer. The UnicodeStringToBinary function merely converts the informed string into a PByte (a pointer to a Byte) without add any terminator. This *really* can be the problem here. I'm currently busy with other work and won't be able to test your suggestion, even so I'll now put in my source code a reminder to test this with a link to this page and if it works out I will come back to accept your answer. Thank you very much again for the help. – Carlos B. Feitoza Filho Oct 18 '21 at 17:47