Jump to content
Carlos Feitoza Filho

How to get a certificate store from a smart card (USB Token) using CNG?

Recommended Posts

I'm using Crypto API's CryptAcquireContext function 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.

 

Definition of additional functions used on my code:

 

procedure AppendBytes(const AOrigin: PByte; AOriginSize: DWORD; var ADestination: PByte; ADestinationSize: DWORD);
var
  Destination: PByte;
  DstPtrNew: PByte;
  DstPtrOld: PByte;
  OrgPtr: PByte;
  i: DWORD;
begin
  GetMem(Destination,ADestinationSize + AOriginSize);

  DstPtrNew := Destination;

  DstPtrOld := ADestination;

  for i := 1 to ADestinationSize do
  begin
    DstPtrNew^ := DstPtrOld^;
    Inc(DstPtrNew);
    Inc(DstPtrOld);
  end;

  FreeMem(ADestination);

  OrgPtr := AOrigin;

  for i := 1 to AOriginSize do
  begin
    DstPtrNew^ := OrgPtr^;
    Inc(DstPtrNew);
    Inc(OrgPtr);
  end;

  ADestination := Destination;
end;

function StringLengthInBytes(AUnicodeString: UnicodeString): DWORD;
begin
  Result := Length(AUnicodeString) * 2;
end;

procedure UnicodeStringToBinary(const AUnicodeString: UnicodeString; out ABinary: PByte; out ABinarySize: DWORD);
var
  OrdinaryString: PByte absolute AUnicodeString;
begin
  ABinarySize := StringLengthInBytes(AUnicodeString);
  ABinary := nil;
  AppendBytes(OrdinaryString,ABinarySize,ABinary,0);
end;

Some observations:

  1. I'm not checking the returns here (Status variable) to simplify this code sample
     
  2. My NCryptOpenStorageProvider signature is more close of the Windows API version, so, its first argument is a pointer to NCRYPT_PROV_HANDLE. 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
     
  3. I've found one code in C++ here: github.com/Microsoft/TSS.MSR/blob/master/PCPTool.v11/exe/… (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

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×