From Windows Certificate Store to TMS Cryptography Pack TX509Certificate component.

TX509Certificate is a component of TMS Cryptography Pack to use X509 certificates. With it, we can sign certificate signing request (CSR) or use it in XAdES, CAdES or PAdES to sign or verify documents.

We can generate self-signed certificates, import them from PEM file, from PEM string, from PFX file.

In this post, I will explain how to import certificate from Windows Certificate Store.

To do that, we have to use the crypt32 DLL and CertOpenSystemStore and CertFindCertificateInStore.

CertOpenSystemStore

function CertOpenSystemStore(hProv: HCRYPTPROV; szSubsystemProtocol: LPCTSTR)
 : hCertStore;

This function is used to open the certificate store. The microsoft documentation is here.

The first parameter hProv is not used and should be set to NULL. The second parameter is a string that names a system store. Most common options are:

CACertification authority certificates.
MYA certificate store that holds certificates with associated private keys.
ROOTRoot certificates.
SPCSoftware Publisher Certificate.

You can use CertEnumSystemStore function to list the names of existing system stores.

The function returns an handle to the store.

CertFindCertificateInStore

function CertFindCertificateInStore(hCertStore: hCertStore;
 dwCertEncodingType, dwFindFlags, dwFindType: DWORD; pvFindPara: Pointer;
 pPrevCertContext: PCCERT_CONTEXT): PCCERT_CONTEXT;

The function finds a certificate in a given store. The microsoft documentation is here.

The first parameter is the handle of the store, returned by CertOpenSystemStore. The second parameter is the encoding type of the cert. The options are:

We will use only X509_ASN_ENCODING because it is the certificate encoding type.

The third parameter is used with some dwFindType values to modify the search criteria. For most dwFindType values, dwFindFlags is not used and should be set to zero.

The fourth parameter specifies the type of search being made. For the complete list of options, you can read the documentation.
We will use in this example the CERT_FIND_SUBJECT_NAME option, to search from subject name of the certificate.

The fifth parameter contains the content of the search. In your example, it will contain the subject name of the certificate we want.

The sixth parameter is a pointer to the last CERT_CONTEXT structure returned by this function. This parameter must be NULL on the first call of the function. To find successive certificates meeting the search criteria, set pPrevCertContext to the pointer returned by the previous call to the function.

If the function succeeds, the function returns a pointer to a read-only CERT_CONTEXT structure.

A CERT_CONTEXT structure is:

_CERT_CONTEXT = record
    dwCertEncodingType: DWORD;
    pbCertEncoded: LPBYTE;
    cbCertEncoded: DWORD;
    pCertInfo: PCERT_INFO;
    hCertStore: hCertStore;
  end;

dwCertEncodingType is the encoding type of the certificate, pbCertEncoded is the content of the certificate, cbCertEncoded is the length of the certificate, pCertInfo is the certificate information and hCertStore is the store.

Code

The code to import a TX509Certificate from the Windows Certificate Store is the following:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  CryptBase, X509Obj, MiscObj;

const
  X509_ASN_ENCODING = $00000001;
  CERT_FIND_SUBJECT_STR = 8 shl 16 or 7;  // values taken from System.Net.HttpClient.Win

type
  HCRYPTPROV = ULONG_PTR;

  _CERT_CONTEXT = record
    dwCertEncodingType: DWORD;
    pbCertEncoded: LPBYTE;
    cbCertEncoded: DWORD;
    pCertInfo: PCERT_INFO;
    hCertStore: hCertStore;
  end;

  PCERT_CONTEXT = ^_CERT_CONTEXT;
  PCCERT_CONTECT = PCERT_CONTEXT;

  function ExtractCertWindowsStore(subjectName: string): TX509Certificate;

  function CertOpenSystemStore(hProv: HCRYPTPROV; szSubsystemProtocol: LPCTSTR)
    : hCertStore; stdcall; external 'crypt32.dll' name 'CertOpenSystemStoreW';

  function CertFindCertificateInStore(hCertStore: hCertStore;
    dwCertEncodingType, dwFindFlags, dwFindType: DWORD; pvFindPara: Pointer;
    pPrevCertContext: PCCERT_CONTEXT): PCCERT_CONTEXT; stdcall;
    external 'crypt32.dll' name 'CertFindCertificateInStore';

implementation

{$R *.dfm}

function ExtractCertWindowsStore(subjectName: string): TX509Certificate;
var
  Store: hCertStore;
  Cert: PCCERT_CONTEXT;
  s: string;
  i: integer;
  Conv: TConvert;
  X509Cert: TX509Certificate;
begin
  Cert := nil;
  Store := CertOpenSystemStore(0, PChar('MY'));
  if (Store <> nil) then
    Cert := CertFindCertificateInStore(Store, X509_ASN_ENCODING, 0,
      CERT_FIND_SUBJECT_STR, PChar(subjectName), nil)
  else
     raise Exception.Create('Unable to open certificate store');

  if (Cert <> nil) then
  begin
    s := '';
    for i := 0 to Cert.cbCertEncoded - 1 do
      s := s + IntToHex(integer(Cert.pbCertEncoded[i]), 2);

    Conv := TConvert.Create(hexa);
    try
      s := Conv.HexaToBase64(s);
    finally
      Conv.Free;
    end;
    X509Cert := TX509Certificate.Create;
    X509Cert.CrtStr := s;
    X509Cert.Decode;
    Result := X509Cert;
  end
  else
    raise Exception.Create('Certificate ' + subjectName + ' not found');
end;

end.

Laisser un commentaire