Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Delphi [игнор отключен] [закрыт для гостей] / WinApi получение отпечатка сертификата из хранилища / 5 сообщений из 5, страница 1 из 1
05.09.2019, 08:54
    #39857702
ora0ra
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
WinApi получение отпечатка сертификата из хранилища
Всем привет!

Встал вопрос! Как получить отпечаток (он же thumbprint) из хранилища сертификатов в винде? Набросал библиотеку для получения серийного номера из crypt32.dll, а куда дальше двигать не понятно. Подскажите как получить отпечаток?

Библиотека:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
unit CryptoWinUtils;

interface

uses
  Windows, classes, SysUtils;

type
  /////////////////////////////
  // WinApi структуры
  /////////////////////////////

  CRYPTOAPI_BLOB = packed record
    cbData: DWORD;
    pbData: PByte;
  end;

  CRYPT_INTEGER_BLOB = CRYPTOAPI_BLOB;
  CRYPT_OBJID_BLOB = CRYPTOAPI_BLOB;

  CERT_NAME_BLOB = CRYPTOAPI_BLOB;
  PCERT_NAME_BLOB = ^CERT_NAME_BLOB;

  CRYPT_ALGORITHM_IDENTIFIER = packed record
    pszObjId: LPSTR;
    Parameters: CRYPT_OBJID_BLOB;
  end;

  CRYPT_BIT_BLOB = packed record
    cbData: DWORD;
    pbData: PBYTE;
    cUnusedBits: DWORD;
  end;

  CERT_PUBLIC_KEY_INFO = packed record
    Algorithm: CRYPT_ALGORITHM_IDENTIFIER;
    PublicKey: CRYPT_BIT_BLOB;
  end;

  CERT_EXTENSION = packed record
    pszObjId: LPSTR;
    fCritical: BOOL;
    Value: CRYPT_OBJID_BLOB;
  end;
  PCERT_EXTENSION = ^CERT_EXTENSION;
  TARR_CERT_EXTENSION = PCERT_EXTENSION;

  CERT_INFO = packed record
    dwVersion: DWORD;
    SerialNumber: CRYPT_INTEGER_BLOB;
    SignatureAlgorithm: CRYPT_ALGORITHM_IDENTIFIER;
    Issuer: CERT_NAME_BLOB;
    NotBefore: FILETIME;
    NotAfter: FILETIME;
    Subject: CERT_NAME_BLOB;
    SubjectPublicKeyInfo: CERT_PUBLIC_KEY_INFO;
    IssuerUniqueId: CRYPT_BIT_BLOB;
    SubjectUniqueId: CRYPT_BIT_BLOB;
    cExtension: DWORD;
    rgExtension: TARR_CERT_EXTENSION;
  end;
  PCERT_INFO = ^CERT_INFO;

  HCERTSTORE = Pointer;
  HCRYPTPROV = ULONG;

  CERT_CONTEXT = packed record
    dwCertEncodingType: DWORD;
    pbCertEncoded: PBYTE;
    cbCertEncoded: DWORD;
    pCertInfo: PCERT_INFO;
    hCertStore: HCERTSTORE;
  end;
  PCERT_CONTEXT = ^CERT_CONTEXT;
  PCCERT_CONTEXT = ^CERT_CONTEXT;

  /////////////////////////////
  // Другие структуры
  /////////////////////////////

  TCertRow = record
    SerialNumber: string;
    // ...
  end;
  TCertRows = array of TCertRow;

const
  X509_ASN_ENCODING = $00000001;
  PKCS_7_ASN_ENCODING = $00010000;

  CERT_SIMPLE_NAME_STR = 1; // All object identifiers (OIDs) are discarded. CERT_RDN entries are separated by a comma followed by a space (, ). Multiple attributes in a CERT_RDN are separated by a plus sign enclosed within spaces ( + ), for example, Firm, Qwe Asd + Zxc.
  CERT_OID_NAME_STR = 2; // OIDs are included with an equal sign (=) separator from their attribute value. CERT_RDN entries are separated by a comma followed by a space (, ). Multiple attributes in a CERT_RDN are separated by a plus sign followed by a space (+ ).
  CERT_X500_NAME_STR = 3; // OIDs are converted to their X.500 key names; otherwise, they are the same as CERT_OID_NAME_STR. If an OID does not have a corresponding X.500 name, the OID is used with a prefix of OID.
  CERT_NAME_STR_SEMICOLON_FLAG = $40000000; // Replace the comma followed by a space (, ) separator with a semicolon followed by a space (; ) separator.
  CERT_NAME_STR_CRLF_FLAG = $08000000; // Replace the comma followed by a space (, ) separator with a backslash followed by the letter r followed by a backslash followed by the letter n (\r\n) separator.
  CERT_NAME_STR_NO_PLUS_FLAG = $20000000; // Replace the plus sign enclosed within spaces ( + ) separator with a single space separator.
  CERT_NAME_STR_NO_QUOTING_FLAG = $10000000; // Disable quoting.
  CERT_NAME_STR_REVERSE_FLAG = $02000000; // The order of the RDNs in the distinguished name string is reversed after decoding. This flag is not set by default.
  CERT_NAME_STR_DISABLE_IE4_UTF8_FLAG = $00010000; // By default, a CERT_RDN_T61_STRING X.500 key string is decoded as UTF8. If UTF8 decoding fails, the X.500 key is decoded as an 8 bit character. Use CERT_NAME_STR_DISABLE_IE4_UTF8_FLAG to skip the initial attempt to decode as UTF8.
  CERT_NAME_STR_ENABLE_PUNYCODE_FLAG = $00200000; // If the name pointed to by the pName parameter contains an email RDN, and the host name portion of the email address contains a Punycode encoded IA5String, the name is converted to the Unicode equivalent.

  CERT_TYPE_INFO_SERIAL_NUMBER = 1;

  /////////////////////////////
  // WinApi функции
  /////////////////////////////

function CertFreeCertificateContext(pCertContext:
  PCCERT_CONTEXT): BOOL; stdcall external 'crypt32.dll';

function CertCreateCertificateContext(dwCertEncodingType: DWORD;
  pbCertEncoded: PBYTE; cbCertEncoded: DWORD): PCCERT_CONTEXT; stdcall
external 'crypt32.dll';

function CertOpenSystemStoreA(hProv: HCRYPTPROV;
  szSubsystemProtocol: LPCSTR): HCERTSTORE; stdcall
external 'crypt32.dll';

function CertCloseStore(hCertStore: HCERTSTORE;
  dwFlags: DWORD = 0): BOOL; stdcall
external 'crypt32.dll';

function CertEnumCertificatesInStore(hCertStore: HCERTSTORE;
  pPrevCertContext: PCCERT_CONTEXT): PCCERT_CONTEXT; stdcall
external 'crypt32.dll';

function CertNameToStrA(
  dwCertEncodingType: DWORD;
  pName: PCERT_NAME_BLOB;
  dwStrType: DWORD;
  psz: LPSTR;
  csz: DWORD): DWORD; stdcall
external 'crypt32.dll';

function CertNameToStrW(
  dwCertEncodingType: DWORD;
  pName: PCERT_NAME_BLOB;
  dwStrType: DWORD;
  psz: LPWSTR;
  csz: DWORD): DWORD; stdcall
external 'crypt32.dll';

/////////////////////////////
// Другие функции
/////////////////////////////

// ByteArrayToStr
function ByteArrayToStr(pbData: PByte; cbData: DWORD): string;

// Получить серийный номер сертификата
function CertGetSerialNumber(aCertInfo: PCCERT_CONTEXT): string;

// Получить список сертификатов из хранилища
function CertGetList(const aCertStoreName: string): TCertRows;

// Поиск сертификата
function CertFind(const aCertRows: TCertRows; const aCertData: string; const aCertTypeInfo: Integer): Integer;

implementation

// ByteArrayToStr

function ByteArrayToStr(pbData: PByte; cbData: DWORD): string;
var
  I, J: Integer;
  S: string;
begin
  Result := '';
  if not Assigned(pbData) or (cbData <= 0) then
    Exit;
  for I := 0 to cbData - 1 do
  begin
    J := PByteArray(pbData)^[i];
    S := IntToHex(J, 2);
    if (I > 0) and (I and 1 = 0) then
      S := S + ' ';
    Result := S + Result;
  end;
end;

// Получить серийный номер сертификата

function CertGetSerialNumber(aCertInfo: PCCERT_CONTEXT): string;
begin
  Result := ByteArrayToStr(
    aCertInfo.pCertInfo.SerialNumber.pbData
    , aCertInfo.pCertInfo.SerialNumber.cbData);
end;

// Получить список сертификатов из хранилища

function CertGetList(const aCertStoreName: string): TCertRows;
var
  certStoreName: PChar;
  hwndStore: HCERTSTORE;
  cert: PCCERT_CONTEXT;
  cryptoProvider: HCRYPTPROV;
  id: Integer;
begin
  // Преобразовываю имя хранилища
  certStoreName := StrAlloc(length(aCertStoreName) + 1);
  StrPCopy(certStoreName, aCertStoreName);

  // Получаю доступ к хранилищу
  hwndStore := CertOpenSystemStoreA(cryptoProvider, certStoreName);

  // Фокус на первый сертификат
  cert := CertEnumCertificatesInStore(hwndStore, nil);

  // Инициализация переменных
  SetLength(Result, 0);

  // Перебор сертификатов
  while cert <> nil do
  begin
    id := Length(Result);
    SetLength(Result, id + 1);

    // Серийный номер
    Result[id].SerialNumber := CertGetSerialNumber(cert);

    // Следующий сертификат
    cert := CertEnumCertificatesInStore(hwndStore, cert);
  end;

  // Завершить работу с хранилищем
  CertCloseStore(hwndStore);
end;

// Поиск сертификата

function CertFind(const aCertRows: TCertRows; const aCertData: string; const aCertTypeInfo: Integer): Integer;
var
  i, len: Integer;
  certData, certRowData: string;
begin
  Result := -1;
  len := Length(aCertRows);

  // Форматирование поискового текста
  case aCertTypeInfo of
    // Серийный номер
    CERT_TYPE_INFO_SERIAL_NUMBER:
      certData := AnsiUpperCase(StringReplace(aCertData, ' ', '', [rfReplaceAll]));
  end;

  // Поиск текста
  for i := 0 to len - 1 do
  begin
    // Форматирование переменной поиска из массива
    case aCertTypeInfo of
      // Серийный номер
      CERT_TYPE_INFO_SERIAL_NUMBER:
        certRowData := AnsiUpperCase(StringReplace(aCertRows[i].SerialNumber, ' ', '', [rfReplaceAll]));
    else
      Break;
    end;

    // Поиск
    if certRowData = certData then
    begin
      Result := i;
      Break;
    end;
  end;
end;

end.



Поиск сертификата по серийному номеру:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
procedure TForm1.Button1Click(Sender: TObject);
var
  r: TCertRows;
  i: integer;
begin
  r := CertGetList('My');

  i := CertFind(r, '01e3fbf60031a9579540ca4145db0c17yu', CERT_TYPE_INFO_SERIAL_NUMBER);

  ShowMessage( IntToStr(i) );
end;



Модератор: Как мне оформить свое сообщение?
...
Рейтинг: 0 / 0
05.09.2019, 09:29
    #39857720
sql2012
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
WinApi получение отпечатка сертификата из хранилища
ora0ra,

CertGetCertificateContextProperty(Certificate, CERT_SHA1_HASH_PROP_ID, nil, pcbData)
...
Рейтинг: 0 / 0
05.09.2019, 09:32
    #39857724
sql2012
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
WinApi получение отпечатка сертификата из хранилища
выделяешь память размером pcbData и вызываешь повторно с указателем уже, вместо nil.

Или, зная что это отпечаток, его размерность и предварительно выделив память, можно одним вызовом.
...
Рейтинг: 0 / 0
05.09.2019, 09:34
    #39857727
sql2012
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
WinApi получение отпечатка сертификата из хранилища
ora0ra,

JwaWinCrypt.pas в помощь
...
Рейтинг: 0 / 0
11.09.2019, 11:49
    #39860214
ora0ra
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
WinApi получение отпечатка сертификата из хранилища
sql2012, да благодарю, нашёл.

Написал такой код, если кому надо:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
function CertGetCertificateContextProperty(
  pCertContext: PCCERT_CONTEXT;
  dwPropId: DWORD;
  pvData: Pointer;
  pcbData: PDWORD
  ): BOOL; stdcall external 'crypt32.dll';


// Получить отпечаток сертификата

function CertGetThumbPrint(aCertInfo: PCCERT_CONTEXT): string;
const
  DataLen = 20;
var
  Data: array[0..DataLen - 1] of Byte;
  DataLenTmp: PDWORD;
begin
  DataLenTmp^ := DataLen;

  if CertGetCertificateContextProperty(
    aCertInfo,
    CERT_SHA1_HASH_PROP_ID,
    @Data,
    DataLenTmp) then
  begin
    SetLength(Result, DataLen * 2);
    BinToHex(@Data, PChar(Result), DataLen);
  end
  else
    Result := '';
end;


Иногда может ничего не выводить, тогда нужно настроить DataLen (длина хэша).
...
Рейтинг: 0 / 0
Форумы / Delphi [игнор отключен] [закрыт для гостей] / WinApi получение отпечатка сертификата из хранилища / 5 сообщений из 5, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]