Список персональных сертификатов пользователя в хранилище сертификатов Windows можно посмотреть с помощью менеджера сертификатов (certmgr.msc).
А как получить этот список в своей программе?
Столкнувшись с этим вопросом, я сначала хотел взять какое-нибудь готовое решение из сторонних библиотек. Но потом среди исходных кодов Delphi нашел модуль System.Net.HttpClient.Win, который содержит импортированные из Crypt32.dll функции для работы с хранилищем сертификатов. Кроме того, в нем реализована обертка функции CryptUIDlgSelectCertificateFromStore из Cryptui.dll, которая отображает стандартное диалоговое окно для выбора персонального сертификата из их списка.
Но, что делать, если стандартный диалог для выбора сертификата не нравится, или нужен только список сертификатов без диалога? Авторы System.Net.HttpClient.Win почему-то скрыли все типы данных, константы и функции Crypt32.dll в секции implementation. Удивительный приступ жадности.
Копируем System.Net.HttpClient.Win.pas в каталог своей программы и делаем в нем небольшие исправления: переносим декларацию функции ShowSelectCertificateDialog (ее, как и другой неиспользуемый код, можно за ненадобностью удалить) и ключевое слово implementation в конец объявления типов (вставляем перед комментарием "Crypt API helper functions"), в конце модуля убираем секции initialization и finalization. Итак, модуль для работы с функциями Crypt32.dll готов, и мы можем писать свою функцию для получения списка персональных сертификатов:
Столкнувшись с этим вопросом, я сначала хотел взять какое-нибудь готовое решение из сторонних библиотек. Но потом среди исходных кодов Delphi нашел модуль System.Net.HttpClient.Win, который содержит импортированные из Crypt32.dll функции для работы с хранилищем сертификатов. Кроме того, в нем реализована обертка функции CryptUIDlgSelectCertificateFromStore из Cryptui.dll, которая отображает стандартное диалоговое окно для выбора персонального сертификата из их списка.
unit System.Net.HttpClient.Win; interface {$HPPEMIT NOUSINGNAMESPACE} {$HPPEMIT '#pragma comment(lib, "winhttp")'} {$HPPEMIT '#pragma comment(lib, "crypt32")'} function ShowSelectCertificateDialog(AParentWnd: UIntPtr; const ATitle, ADisplayString: string; var ACertificate): Boolean; implementationгде ACertificate – это параметр типа TCertificate для получения информации о выбранном сертификате.
// Record to store the information of a certificate TCertificate = record // Name of the certificate CertName: string; // Serial number of the certificate SerialNum: string; // Expiry date of the certificate Expiry: TDateTime; // Start date of the certificate Start: TDateTime; // Subject of the certificate Subject: string; // Issuer of the certificate Issuer: string; // ProtocolName of the certificate ProtocolName: string; // Algorithm Signature of the certificate AlgSignature: string; // Algorithm Encryption of the certificate AlgEncryption: string; // Encryption's KeySize of the certificate KeySize: Integer; // Returns True when all key fields are empty function IsEmpty: Boolean; end;Создадим небольшой тестовый проект. На его главную форму добавим TButton для вызова функции ShowSelectCertificateDialog и TMemo для вывода информации из ACertificate.
unit Unit1; interface uses Vcl.Forms, Vcl.Controls, Vcl.StdCtrls, System.Classes, System.SysUtils, System.DateUtils, System.Net.HttpClient.Win, System.Net.URLClient; type TForm1 = class(TForm) btnSelectCert: TButton; mCert: TMemo; procedure btnSelectCertClick(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.btnSelectCertClick(Sender: TObject); var cert: TCertificate; begin mCert.Clear; if ShowSelectCertificateDialog(Handle, 'Это ATitle', 'Это ADisplayString', cert) then begin mCert.Lines.Add('Name: ' + cert.CertName); mCert.Lines.Add('SerialNum: ' + cert.SerialNum); mCert.Lines.Add('Start: ' + DateTimeToStr(TTimeZone.Local.ToLocalTime(cert.Start))); mCert.Lines.Add('Expiry: ' + DateTimeToStr(TTimeZone.Local.ToLocalTime(cert.Expiry))); mCert.Lines.Add('Subject: ' + cert.Subject); mCert.Lines.Add('Issuer: ' + cert.Issuer); end; end; end.Запускаем полученную программу и нажимаем кнопку "Сертификат". В открывшемся диалоге мы видим первый сертификат из списка персональных сертификатов. Чтобы посмотреть перечень всех персональных сертификатов необходимо кликнуть на "More choices". Теперь выбираем нужный сертификат и нажимаем кнопку "OK". Как видите, все легко и просто. Диалоговое окно для выбора персонального сертификата можно получить вызовом всего лишь одной функции.
Но, что делать, если стандартный диалог для выбора сертификата не нравится, или нужен только список сертификатов без диалога? Авторы System.Net.HttpClient.Win почему-то скрыли все типы данных, константы и функции Crypt32.dll в секции implementation. Удивительный приступ жадности.
Копируем System.Net.HttpClient.Win.pas в каталог своей программы и делаем в нем небольшие исправления: переносим декларацию функции ShowSelectCertificateDialog (ее, как и другой неиспользуемый код, можно за ненадобностью удалить) и ключевое слово implementation в конец объявления типов (вставляем перед комментарием "Crypt API helper functions"), в конце модуля убираем секции initialization и finalization. Итак, модуль для работы с функциями Crypt32.dll готов, и мы можем писать свою функцию для получения списка персональных сертификатов:
program CertList; {$APPTYPE CONSOLE} uses System.SysUtils, System.DateUtils, System.Net.URLClient, System.Net.HttpClient.Win; procedure GetCertificates; var hStore: HCERTSTORE; pCert : PCCERT_CONTEXT; CertInfo: TCertificate; begin // открываем хранилище сертификатов пользователя hStore := CertOpenSystemStore(0, 'MY'); if hStore = nil then WriteLn('Can''t open the store') else try // находим в хранилище первый сертификат pCert := CertEnumCertificatesInStore(hStore, nil); while pCert <> nil do begin // копируем информацию из структуры Win32 API в запись типа TCertificate CryptCertToTCertificate(pCert, CertInfo); if not CertInfo.IsEmpty then begin WriteLn('***'); WriteLn('Name: ' + CertInfo.CertName); WriteLn('SerialNum: ' + CertInfo.SerialNum); WriteLn('Start: ' + DateTimeToStr(TTimeZone.Local.ToLocalTime(CertInfo.Start))); WriteLn('Expiry: ' + DateTimeToStr(TTimeZone.Local.ToLocalTime(CertInfo.Expiry))); WriteLn('Subject: ' + CertInfo.Subject); WriteLn('Issuer: ' + CertInfo.Issuer); end; // находим в хранилище следующий сертификат pCert := CertEnumCertificatesInStore(hStore, pCert); end; finally // закрываем хранилище сертификатов CertCloseStore(hStore, CERT_CLOSE_STORE_FORCE_FLAG); end; end; begin ReportMemoryLeaksOnShutdown := True; try GetCertificates; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end.Запускаем программу и наслаждаемся списком сертификатов и базовой информацией о них:
Комментариев нет:
Отправить комментарий