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:
CA | Certification authority certificates. |
MY | A certificate store that holds certificates with associated private keys. |
ROOT | Root certificates. |
SPC | Software 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:
- X509_ASN_ENCODING
- PKCS_7_ASN_ENCODING
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.